Skip to content

Commit

Permalink
Use split version of nectar::call_api (#53)
Browse files Browse the repository at this point in the history
Also standardize some filenames, etc.
  • Loading branch information
jonthegeek committed Apr 26, 2024
1 parent eb4f4de commit f4db307
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 62 deletions.
1 change: 0 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ importFrom(glue,glue_collapse)
importFrom(httptest2,use_httptest2)
importFrom(lubridate,now)
importFrom(lubridate,parse_date_time)
importFrom(nectar,call_api)
importFrom(nectar,stabilize_string)
importFrom(purrr,discard)
importFrom(purrr,imap)
Expand Down
1 change: 0 additions & 1 deletion R/beekeeper-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#' @importFrom httptest2 use_httptest2
#' @importFrom lubridate now
#' @importFrom lubridate parse_date_time
#' @importFrom nectar call_api
#' @importFrom nectar stabilize_string
#' @importFrom purrr discard
#' @importFrom purrr imap
Expand Down
2 changes: 1 addition & 1 deletion R/generate_pkg-security.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
security_data <- as_bk_data(security_schemes)
if (length(security_data)) {
security_data$security_file_path <- .bk_use_template(
template = "020-security.R",
template = "020-auth.R",
data = c(security_data, api_abbr = api_abbr)
)
security_data$security_signature <- .generate_security_signature(
Expand Down
2 changes: 1 addition & 1 deletion R/generate_pkg_main.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#'
#' @param config_file The path to a beekeeper yaml file.
#' @param pkg_agent A string to identify this package, for use in the
#' `user_agent` argument of [nectar::call_api()].
#' `user_agent` argument of [nectar::req_setup()].
#'
#' @return A character vector of paths to files that were added or updated,
#' invisibly.
Expand Down
20 changes: 13 additions & 7 deletions inst/templates/010-call.R
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Set up the basic call once at package build.
{{api_abbr}}_req_base <- nectar::req_setup(
"{{base_url}}",
user_agent = "{{pkg_agent}}"
)

#' Call the {{api_title}} API
#'
#' Generate a request to an {{api_title}} endpoint.
#'
#' @inheritParams nectar::call_api{{#security_arg_helps}}
#' @inheritParams nectar::req_modify{{#security_arg_helps}}
#' @param {{name}} {{{description}}}{{/security_arg_helps}}
#'
#' @return The response from the endpoint.
Expand All @@ -11,14 +17,14 @@
query = NULL,
body = NULL,
method = NULL{{#has_security}},{{{security_signature}}}{{/has_security}}) {
nectar::call_api(
base_url = "{{base_url}}",
req <- nectar::req_modify(
{{api_abbr}}_req_base,
path = path,
query = query,
body = body,
method = method,
user_agent = "{{pkg_agent}}"{{#has_security}},
security_fn = {{api_abbr}}_security,
security_args = list({{security_arg_list}}){{/has_security}}
method = method
)
{{#has_security}}req <- .{{api_abbr}}_req_auth(req, {{security_arg_list}}){{/has_security}}
resp <- nectar::req_perform_opinionated(req)
nectar::resp_parse(resp, response_parser = .{{api_abbr}}_response_parser)
}
8 changes: 4 additions & 4 deletions inst/templates/020-security.R → inst/templates/020-auth.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
# pass the same parameter in a header, possibly with a different name. Consult
# the text description of authentication in your API documentation.

{{api_abbr}}_security <- function(req, {{security_arg_nulls}}) {
.{{api_abbr}}_req_auth <- function(req, {{security_arg_nulls}}) {
{{#security_schemes}}
if (!is.null({{arg_name}})) {
req <- {{api_abbr}}_security_{{name}}(req, {{arg_name}})
req <- .{{api_abbr}}_req_auth_{{name}}(req, {{arg_name}})
}
{{/security_schemes}}
return(req)
Expand All @@ -19,8 +19,8 @@
{{#description}}
# {{description}}
{{/description}}
{{api_abbr}}_security_{{name}} <- function(req, {{#api_key}}{{arg_name}}) {
nectar::security_api_key(
.{{api_abbr}}_req_auth_{{name}} <- function(req, {{#api_key}}{{arg_name}}) {
nectar::req_auth_api_key(
req,
location = "{{location}}",
parameter_name = "{{parameter_name}}",
Expand Down
2 changes: 1 addition & 1 deletion man/generate_pkg.Rd

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

18 changes: 13 additions & 5 deletions tests/testthat/_fixtures/guru-010-call.R
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
# Set up the basic call once at package build.
guru_req_base <- nectar::req_setup(
"https://api.apis.guru/v2",
user_agent = "TESTPKG (https://example.com)"
)

#' Call the APIs.guru API
#'
#' Generate a request to an APIs.guru endpoint.
#'
#' @inheritParams nectar::call_api
#' @inheritParams nectar::req_modify
#'
#' @return The response from the endpoint.
#' @export
guru_call_api <- function(path,
query = NULL,
body = NULL,
method = NULL) {
nectar::call_api(
base_url = "https://api.apis.guru/v2",
req <- nectar::req_modify(
guru_req_base,
path = path,
query = query,
body = body,
method = method,
user_agent = "TESTPKG (https://example.com)"
method = method
)

resp <- nectar::req_perform_opinionated(req)
nectar::resp_parse(resp, response_parser = .guru_response_parser)
}
20 changes: 13 additions & 7 deletions tests/testthat/_fixtures/trello-010-call.R
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Set up the basic call once at package build.
trello_req_base <- nectar::req_setup(
"https://trello.com/1",
user_agent = "TESTPKG (https://example.com)"
)

#' Call the Trello API
#'
#' Generate a request to an Trello endpoint.
#'
#' @inheritParams nectar::call_api
#' @inheritParams nectar::req_modify
#' @param key An API key provided by the API provider. This key is not clearly documented in the API description. Check the API documentation for details.
#' @param token An API key provided by the API provider. This key is not clearly documented in the API description. Check the API documentation for details.
#'
Expand All @@ -13,14 +19,14 @@ trello_call_api <- function(path,
body = NULL,
method = NULL, key = Sys.getenv("TRELLO_KEY"),
token = Sys.getenv("TRELLO_TOKEN")) {
nectar::call_api(
base_url = "https://trello.com/1",
req <- nectar::req_modify(
trello_req_base,
path = path,
query = query,
body = body,
method = method,
user_agent = "TESTPKG (https://example.com)",
security_fn = trello_security,
security_args = list(key = key, token = token)
method = method
)
req <- .trello_req_auth(req, key = key, token = token)
resp <- nectar::req_perform_opinionated(req)
nectar::resp_parse(resp, response_parser = .trello_response_parser)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
# pass the same parameter in a header, possibly with a different name. Consult
# the text description of authentication in your API documentation.

trello_security <- function(req, key = NULL, token = NULL) {
.trello_req_auth <- function(req, key = NULL, token = NULL) {
if (!is.null(key)) {
req <- trello_security_api_key(req, key)
req <- .trello_req_auth_api_key(req, key)
}
if (!is.null(token)) {
req <- trello_security_api_token(req, token)
req <- .trello_req_auth_api_token(req, token)
}
return(req)
}

# An API key provided by the API provider. This key is not clearly documented in the API description. Check the API documentation for details.
trello_security_api_key <- function(req, key) {
nectar::security_api_key(
.trello_req_auth_api_key <- function(req, key) {
nectar::req_auth_api_key(
req,
location = "query",
parameter_name = "key",
Expand All @@ -27,8 +27,8 @@ trello_security_api_key <- function(req, key) {
}

# An API key provided by the API provider. This key is not clearly documented in the API description. Check the API documentation for details.
trello_security_api_token <- function(req, token) {
nectar::security_api_key(
.trello_req_auth_api_token <- function(req, token) {
nectar::req_auth_api_key(
req,
location = "query",
parameter_name = "token",
Expand Down
2 changes: 1 addition & 1 deletion tests/testthat/_snaps/generate_pkg-paths.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
scrub_path(changed_files)
Output
[1] "/R/010-call.R" "/tests/testthat/test-010-call.R"
[3] "/R/020-security.R" "/R/paths-audit.R"
[3] "/R/020-auth.R" "/R/paths-audit.R"
[5] "/tests/testthat/test-paths-audit.R" "/R/paths-legal.R"
[7] "/tests/testthat/test-paths-legal.R" "/R/paths-debts.R"
[9] "/tests/testthat/test-paths-debts.R" "/tests/testthat/setup.R"
Expand Down
4 changes: 2 additions & 2 deletions tests/testthat/test-generate_pkg-security.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ test_that("generate_pkg() generates security functions", {
)
config <- readLines(test_path("_fixtures", "trello_beekeeper.yml"))
trello_rapid <- readRDS(test_path("_fixtures", "trello_rapid.rds"))
security_expected <- readLines(test_path("_fixtures", "trello-020-security.R"))
security_expected <- readLines(test_path("_fixtures", "trello-020-auth.R"))

create_local_package()
writeLines(config, "_beekeeper.yml")
saveRDS(trello_rapid, "trello_rapid.rds")

generate_pkg(pkg_agent = "TESTPKG (https://example.com)")

security_result <- scrub_testpkg(readLines("R/020-security.R"))
security_result <- scrub_testpkg(readLines("R/020-auth.R"))
expect_identical(security_result, security_expected)
})
58 changes: 34 additions & 24 deletions vignettes/beekeeper.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ url("https://api.apis.guru/v2/openapi.yaml") |>
api_abbr = "guru"
)
# Or for the FEC
# Or for the FEC API
url("https://api.apis.guru/v2/specs/fec.gov/1.0/openapi.yaml") |>
use_beekeeper(
api_abbr = "fec"
Expand All @@ -68,15 +68,21 @@ generate_pkg()

### 010-call.R

The main file generated for the package is `R/010-call.R`.
The first file generated for the package is `R/010-call.R`.
This file defines a function that can be used to call the API.

```{r fec_call_api}
# Set up the basic call once at package build.
fec_req_base <- nectar::req_setup(
"https://api.open.fec.gov/v1",
user_agent = "fecapi (https://github.com/jonthegeek/fecapi)"
)
#' Call the OpenFEC API
#'
#' Generate a request to an OpenFEC endpoint.
#'
#' @inheritParams nectar::call_api
#' @inheritParams nectar::req_modify
#' @param api_key An API key provided by the API provider. This key is not
#' clearly documented in the API description. Check the API documentation for
#' details.
Expand All @@ -88,28 +94,29 @@ fec_call_api <- function(path,
body = NULL,
method = NULL,
api_key = Sys.getenv("FEC_API_KEY")) {
nectar::call_api(
base_url = "https://api.open.fec.gov/v1",
req <- nectar::req_modify(
fec_req_base,
path = path,
query = query,
body = body,
method = method,
user_agent = "fecapi (https://github.com/jonthegeek/fecapi)",
security_fn = fec_security,
security_args = list(api_key = api_key)
method = method
)
req <- .fec_req_auth(req, api_key = api_key)
resp <- nectar::req_perform_opinionated(req)
nectar::resp_parse(resp, response_parser = .fec_response_parser)
}
```

Notice that the function includes API-key security arguments when appropriate!
Notice that the function includes API-key authentication arguments when appropriate!

### 020-security.R
### 020-auth.R

Security for the API is defined in `R/020-security.R`.
Security for the API is defined in `R/020-auth.R`.
The initial version of this file works, but you may want to edit the automatic output.
In the case of the OpenFEC API, the description specifies three security schemes that overlap with one another: two that set an `api_key` in the query string, and one that sets an `X-Api-Key` field in the header.
In the case of the OpenFEC API, the description specifies three security schemes that overlap with one another: one that sets an `X-Api-Key` field in the header, and two that set an `api_key` in the query string.
The names of the generated functions are based on the names of the security schemes in the OpenAPI document.

```{r fec_security}
```{r fec_auth}
# These functions were generated by the {beekeeper} package, based on
# components@security_schemes from the source API description. You may want to
# delete unused options. In addition, APIs often have additional security
Expand All @@ -118,17 +125,19 @@ In the case of the OpenFEC API, the description specifies three security schemes
# pass the same parameter in a header, possibly with a different name. Consult
# the text description of authentication in your API documentation.
fec_security <- function(req, api_key) {
req <- fec_security_api_key_header_auth(req, api_key)
req <- fec_security_api_key_query_auth(req, api_key)
req <- fec_security_api_key(req, api_key)
.fec_req_auth <- function(req, api_key = NULL) {
if (!is.null(api_key)) {
req <- .fec_req_auth_api_key_header_auth(req, api_key)
req <- .fec_req_auth_api_key_query_auth(req, api_key)
req <- .fec_req_auth_api_key(req, api_key)
}
return(req)
}
# An API key provided by the API provider. This key is not clearly documented in
# the API description. Check the API documentation for details.
fec_security_api_key_header_auth <- function(req, api_key) {
nectar::security_api_key(
.fec_req_auth_api_key_header_auth <- function(req, api_key) {
nectar::req_auth_api_key(
req,
location = "header",
parameter_name = "X-Api-Key",
Expand All @@ -138,8 +147,8 @@ fec_security_api_key_header_auth <- function(req, api_key) {
# An API key provided by the API provider. This key is not clearly documented in
# the API description. Check the API documentation for details.
fec_security_api_key_query_auth <- function(req, api_key) {
nectar::security_api_key(
.fec_req_auth_api_key_query_auth <- function(req, api_key) {
nectar::req_auth_api_key(
req,
location = "query",
parameter_name = "api_key",
Expand All @@ -149,8 +158,8 @@ fec_security_api_key_query_auth <- function(req, api_key) {
# An API key provided by the API provider. This key is not clearly documented in
# the API description. Check the API documentation for details.
fec_security_api_key <- function(req, api_key) {
nectar::security_api_key(
.fec_req_auth_api_key <- function(req, api_key) {
nectar::req_auth_api_key(
req,
location = "query",
parameter_name = "api_key",
Expand All @@ -160,6 +169,7 @@ fec_security_api_key <- function(req, api_key) {
```

For the real package, I deleted the two `query` functions, since the `header` function is sufficient and slightly more secure.
I also renamed the `header` function from `.fec_req_auth_api_key_header_auth` to `.fec_req_auth_api_key_header` to remove the redundant `_auth`.

### test files

Expand Down

0 comments on commit f4db307

Please sign in to comment.