From 735e7c9769e2aae46b5d73021dd024a51691ecc7 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Mon, 11 May 2026 16:00:41 -0500 Subject: [PATCH] Remove special tibblify version checks Also apply air, and update workflows --- .Rbuildignore | 2 + .github/workflows/copilot-setup-steps.yml | 1 + .github/workflows/format-suggest.yaml | 46 +++++ .github/workflows/pkgdown-cleanup.yaml | 33 ++++ .github/workflows/pkgdown.yaml | 4 +- .github/workflows/qcthat.yaml | 157 ++++++++++++++++++ .vscode/extensions.json | 5 + .vscode/settings.json | 10 ++ DESCRIPTION | 2 +- R/components-schema.R | 22 ++- R/components-security_scheme-api_key.R | 16 +- ...ty_scheme-oauth2-authorization_code_flow.R | 22 ++- ...nts-security_scheme-oauth2-implicit_flow.R | 20 ++- ...onents-security_scheme-oauth2-token_flow.R | 20 ++- R/components-security_scheme-oauth2.R | 12 +- R/components-security_scheme.R | 33 ++-- R/components-security_schemes.R | 10 +- R/components.R | 8 +- R/info-license.R | 10 +- R/info-origin.R | 33 ++-- R/info.R | 20 ++- R/paths.R | 65 +++----- R/servers-server_variables.R | 10 +- R/servers-string_replacements.R | 33 ++-- R/servers.R | 18 +- R/swagger_to_openapi.R | 32 ++-- R/validate_in.R | 13 +- R/zzz.R | 3 +- air.toml | 0 tests/testthat/test-as.R | 4 +- .../test-components-security_scheme-oauth2.R | 6 +- .../test-components-security_scheme_details.R | 3 +- .../test-servers-string_replacements.R | 3 +- tests/testthat/test-servers.R | 6 +- 34 files changed, 491 insertions(+), 191 deletions(-) create mode 100644 .github/workflows/format-suggest.yaml create mode 100644 .github/workflows/pkgdown-cleanup.yaml create mode 100644 .github/workflows/qcthat.yaml create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 air.toml diff --git a/.Rbuildignore b/.Rbuildignore index ab61171..867260e 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -14,3 +14,5 @@ ^\.positai$ ^\.claude$ ^AGENTS\.md$ +^[.]?air[.]toml$ +^\.vscode$ diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 611b08f..e1907e1 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -45,6 +45,7 @@ jobs: any::testthat any::usethis any::withr + Gilead-BioStats/qcthat local::. - name: Install air diff --git a/.github/workflows/format-suggest.yaml b/.github/workflows/format-suggest.yaml new file mode 100644 index 0000000..af50210 --- /dev/null +++ b/.github/workflows/format-suggest.yaml @@ -0,0 +1,46 @@ +# Workflow derived from https://github.com/posit-dev/setup-air/tree/main/examples + +on: + # Using `pull_request_target` over `pull_request` for elevated `GITHUB_TOKEN` + # privileges, otherwise we can't set `pull-requests: write` when the pull + # request comes from a fork, which is our main use case (external contributors). + # + # `pull_request_target` runs in the context of the target branch (`main`, usually), + # rather than in the context of the pull request like `pull_request` does. Due + # to this, we must explicitly checkout `ref: ${{ github.event.pull_request.head.sha }}`. + # This is typically frowned upon by GitHub, as it exposes you to potentially running + # untrusted code in a context where you have elevated privileges, but they explicitly + # call out the use case of reformatting and committing back / commenting on the PR + # as a situation that should be safe (because we aren't actually running the untrusted + # code, we are just treating it as passive data). + # https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + pull_request_target: + +name: format-suggest.yaml + +jobs: + format-suggest: + name: format-suggest + runs-on: ubuntu-latest + + permissions: + # Required to push suggestion comments to the PR + pull-requests: write + + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Install + uses: posit-dev/setup-air@v1 + + - name: Format + run: air format . + + - name: Suggest + uses: reviewdog/action-suggester@v1 + with: + level: error + fail_level: error + tool_name: air diff --git a/.github/workflows/pkgdown-cleanup.yaml b/.github/workflows/pkgdown-cleanup.yaml new file mode 100644 index 0000000..d8ea7c2 --- /dev/null +++ b/.github/workflows/pkgdown-cleanup.yaml @@ -0,0 +1,33 @@ +# This workflow removes the pkgdown preview directory when a PR is closed. +name: Clean up pkgdown preview + +on: + pull_request: + types: [closed] + +permissions: + contents: write + +jobs: + clean-pr-preview: + runs-on: ubuntu-latest + steps: + - name: Checkout gh-pages branch + uses: actions/checkout@v6 + with: + repository: ${{ github.repository }} + token: ${{ secrets.GITHUB_TOKEN }} + ref: gh-pages + + - name: Remove PR preview directory + run: | + pr_dir="pr/${{ github.event.number }}" + if [ -d "$pr_dir" ]; then + git config --local user.name "$GITHUB_ACTOR" + git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git rm -rf "$pr_dir" + git commit -m "Remove preview for PR #${{ github.event.number }}" + git push + else + echo "Directory $pr_dir does not exist, skipping cleanup." + fi diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 2e4ee7c..7f4f411 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -32,7 +32,7 @@ jobs: use-container: "true" token: ${{ secrets.GITHUB_TOKEN }} needs: website - extra-packages: any::pkgdown gilead-biostats/qcthat local::. + extra-packages: any::pkgdown gilead-biostats/qcthat local::. any::glue - name: Build site run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) @@ -54,7 +54,7 @@ jobs: strOwner <- tolower(qcthat::GetGHOwner()) strRepo <- qcthat::GetGHRepo() strURL <- glue::glue( - "https://{strOwner}.github.io/{strRepo}/pr/{intPRNumber}/dev" + "https://{strOwner}.github.io/{strRepo}/pr/{intPRNumber}" ) print(paste("🌐 URL:", strURL)) qcthat::CommentIssue( diff --git a/.github/workflows/qcthat.yaml b/.github/workflows/qcthat.yaml new file mode 100644 index 0000000..2adec84 --- /dev/null +++ b/.github/workflows/qcthat.yaml @@ -0,0 +1,157 @@ +# Workflow derived from +# https://github.com/Gilead-BioStats/qcthat/tree/v1.1.1/inst/workflows/qcthat.yaml. +on: + pull_request: + types: [opened, edited, reopened, synchronize, milestoned] + release: + types: [released] + issues: + types: [closed] + workflow_dispatch: + inputs: + pr: + description: PR number to which reports should be added (leave blank for none). + required: false + milestone: + description: Milestone name to use for the milestone report (leave blank for none). + required: false + tag: + description: Release tag to which the report should be attached (leave blank for none). + required: false + issueNumber: + description: The closed issue number to process to update user acceptance testing information. + required: false + +name: qcthat Quality Control + +permissions: + # read: Required for generating reports and updating UAT status. + # write: Required for initiating the UAT process. + issues: write + # read: Required for updating UAT status. + # write: Required for adding reports to pull requests. + pull-requests: write + # write: Required for attaching reports to releases. + contents: write + # write: Required for updating UAT status. + actions: write + +# Configuration variables for controlling workflow behavior +env: + qcthat_UAT: true + qcthat_PR_REPORT: true + qcthat_COMPLETED_REPORT: true + qcthat_MILESTONE_REPORT: true + qcthat_RELEASE_REPORT: true + qcthat_FAIL_FOR_TEST_FAILURES: true + +jobs: + qcthat: + runs-on: ubuntu-latest + container: + image: ghcr.io/api2r/pkgskills-ci:release + if: >- + (github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'qcthat-uat')) || + github.event_name != 'issues' + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v6 + + - uses: api2r/actions/install@v1 + with: + use-container: "true" + token: ${{ secrets.GITHUB_TOKEN }} + extra-packages: Gilead-BioStats/qcthat@main local::. + + - name: Manage User Acceptance Testing + if: >- + env.qcthat_UAT == 'true' && ( + (github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'qcthat-uat')) || + (github.event_name == 'workflow_dispatch' && inputs.issueNumber != '') + ) + run: | + Rscript -e "qcthat::TriggerUAT()" + + - name: Generate Issue-Test Matrix + if: >- + (env.qcthat_PR_REPORT == 'true' || env.qcthat_RELEASE_REPORT == 'true' || env.qcthat_FAIL_FOR_TEST_FAILURES == 'true') && ( + github.event_name == 'pull_request' || + github.event_name == 'release' || + (github.event_name == 'workflow_dispatch' && inputs.issueNumber == '') + ) + run: | + # Generate the full matrix for the package + IssueTestMatrix <- qcthat::QCPackage() + print(IssueTestMatrix) + + # Save the matrix and UAT data for subsequent steps + saveRDS(IssueTestMatrix, "ITM.rds") + qcthat::SaveUATIssues() + shell: Rscript {0} + + - name: Update PR Reports + if: >- + env.qcthat_PR_REPORT == 'true' && ( + github.event_name == 'pull_request' || + (github.event_name == 'workflow_dispatch' && inputs.pr != '') + ) + run: | + issueTestMatrix <- readRDS("ITM.rds") + qcthat::LoadUATIssues() + qcthat::CommentAllReports( + dfITM = issueTestMatrix, + lglPR = as.logical("${{ env.qcthat_PR_REPORT }}"), + lglMilestone = as.logical("${{ env.qcthat_MILESTONE_REPORT }}"), + lglCompleted = as.logical("${{ env.qcthat_COMPLETED_REPORT }}"), + lglUAT = as.logical("${{ env.qcthat_UAT }}") + ) + shell: Rscript {0} + + - name: Update Release Reports + if: >- + env.qcthat_RELEASE_REPORT == 'true' && ( + github.event_name == 'release' || inputs.tag != '' + ) + run: | + issueTestMatrix <- readRDS("ITM.rds") + qcthat::LoadUATIssues() + qcthat::AttachReleaseReports( + dfITM = issueTestMatrix, + lglCompleted = as.logical("${{ env.qcthat_COMPLETED_REPORT }}"), + lglMilestone = as.logical("${{ env.qcthat_MILESTONE_REPORT }}") + ) + shell: Rscript {0} + + - name: Flag failure for PR + if: >- + env.qcthat_FAIL_FOR_TEST_FAILURES == 'true' && ( + github.event_name == 'pull_request' || + (github.event_name == 'workflow_dispatch' && inputs.pr != '') + ) + run: | + issueTestMatrix <- readRDS("ITM.rds") + dfPR <- qcthat::QCPR(dfITM = issueTestMatrix) + if (any(dfPR$Disposition == "fail", na.rm = TRUE)) { + cli::cli_abort( + "One or more tests failed or were skipped for PR-associated issues." + ) + } + shell: Rscript {0} + + - name: Flag failure for completed + if: >- + env.qcthat_FAIL_FOR_TEST_FAILURES == 'true' && ( + github.event_name == 'pull_request' || + github.event_name == 'release' || + (github.event_name == 'workflow_dispatch' && inputs.issueNumber == '') + ) + run: | + issueTestMatrix <- readRDS("ITM.rds") + dfCompleted = qcthat::QCCompletedIssues(dfITM = issueTestMatrix) + if (any(dfCompleted$Disposition == "fail", na.rm = TRUE)) { + cli::cli_abort( + "One or more tests failed or were skipped for completed issues." + ) + } + shell: Rscript {0} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..344f76e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "Posit.air-vscode" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a9f69fe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "[r]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "Posit.air-vscode" + }, + "[quarto]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "quarto.quarto" + } +} diff --git a/DESCRIPTION b/DESCRIPTION index a41da3c..365d7b2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -26,7 +26,7 @@ Imports: snakecase, stbl (>= 0.3.0), tibble, - tibblify (>= 0.3.2), + tibblify (>= 0.4.0), xml2, yaml Suggests: diff --git a/R/components-schema.R b/R/components-schema.R index d434ce8..b849bff 100644 --- a/R/components-schema.R +++ b/R/components-schema.R @@ -41,14 +41,20 @@ class_schema <- S7::new_class( description = character_scalar_property("description"), format = character_scalar_property("format") ), - constructor = function(type = c( - "string", "number", "integer", - "boolean", "array", "object" - ), - ..., - nullable = FALSE, - description = character(), - format = character()) { + constructor = function( + type = c( + "string", + "number", + "integer", + "boolean", + "array", + "object" + ), + ..., + nullable = FALSE, + description = character(), + format = character() + ) { check_dots_empty() if (missing(type)) { type <- character() diff --git a/R/components-security_scheme-api_key.R b/R/components-security_scheme-api_key.R index ede68b7..d29a045 100644 --- a/R/components-security_scheme-api_key.R +++ b/R/components-security_scheme-api_key.R @@ -30,8 +30,10 @@ class_api_key_security_scheme <- S7::new_class( parameter_name = character_scalar_property("parameter_name"), location = character_scalar_property("location") ), - constructor = function(parameter_name = character(), - location = c("query", "header", "cookie")) { + constructor = function( + parameter_name = character(), + location = c("query", "header", "cookie") + ) { if (length(parameter_name)) { location <- rlang::arg_match(location) } else { @@ -75,10 +77,12 @@ S7::method(length, class_api_key_security_scheme) <- function(x) { #' @export #' @family components_security_schemes #' @family components -as_api_key_security_scheme <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +as_api_key_security_scheme <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { as_api_object( x, class_api_key_security_scheme, diff --git a/R/components-security_scheme-oauth2-authorization_code_flow.R b/R/components-security_scheme-oauth2-authorization_code_flow.R index da7ae18..97a98d0 100644 --- a/R/components-security_scheme-oauth2-authorization_code_flow.R +++ b/R/components-security_scheme-oauth2-authorization_code_flow.R @@ -36,11 +36,13 @@ class_oauth2_authorization_code_flow <- S7::new_class( authorization_url = character_scalar_property("authorization_url"), token_url = character_scalar_property("token_url") ), - constructor = function(authorization_url = character(), - token_url = character(), - ..., - refresh_url = character(), - scopes = class_scopes()) { + constructor = function( + authorization_url = character(), + token_url = character(), + ..., + refresh_url = character(), + scopes = class_scopes() + ) { check_dots_empty() S7::new_object( S7::S7_object(), @@ -84,10 +86,12 @@ S7::method(length, class_oauth2_authorization_code_flow) <- function(x) { #' @export #' @family components_security_schemes #' @family components -as_oauth2_authorization_code_flow <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +as_oauth2_authorization_code_flow <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { as_api_object( x, class_oauth2_authorization_code_flow, diff --git a/R/components-security_scheme-oauth2-implicit_flow.R b/R/components-security_scheme-oauth2-implicit_flow.R index 4c4a821..c0c00a7 100644 --- a/R/components-security_scheme-oauth2-implicit_flow.R +++ b/R/components-security_scheme-oauth2-implicit_flow.R @@ -30,10 +30,12 @@ class_oauth2_implicit_flow <- S7::new_class( properties = list( authorization_url = character_scalar_property("authorization_url") ), - constructor = function(authorization_url = character(), - ..., - refresh_url = character(), - scopes = class_scopes()) { + constructor = function( + authorization_url = character(), + ..., + refresh_url = character(), + scopes = class_scopes() + ) { check_dots_empty() S7::new_object( S7::S7_object(), @@ -75,9 +77,11 @@ S7::method(length, class_oauth2_implicit_flow) <- function(x) { #' @export #' @family components_security_schemes #' @family components -as_oauth2_implicit_flow <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +as_oauth2_implicit_flow <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { as_api_object(x, class_oauth2_implicit_flow, ..., arg = arg, call = call) } diff --git a/R/components-security_scheme-oauth2-token_flow.R b/R/components-security_scheme-oauth2-token_flow.R index 68c366e..88175ca 100644 --- a/R/components-security_scheme-oauth2-token_flow.R +++ b/R/components-security_scheme-oauth2-token_flow.R @@ -35,10 +35,12 @@ class_oauth2_token_flow <- S7::new_class( properties = list( token_url = class_character ), - constructor = function(token_url = character(), - ..., - refresh_url = character(), - scopes = class_scopes()) { + constructor = function( + token_url = character(), + ..., + refresh_url = character(), + scopes = class_scopes() + ) { check_dots_empty() S7::new_object( S7::S7_object(), @@ -78,9 +80,11 @@ S7::method(length, class_oauth2_token_flow) <- function(x) { #' @export #' @family components_security_schemes #' @family components -as_oauth2_token_flow <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +as_oauth2_token_flow <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { as_api_object(x, class_oauth2_token_flow, ..., arg = arg, call = call) } diff --git a/R/components-security_scheme-oauth2.R b/R/components-security_scheme-oauth2.R index a162357..f578734 100644 --- a/R/components-security_scheme-oauth2.R +++ b/R/components-security_scheme-oauth2.R @@ -37,11 +37,13 @@ class_oauth2_security_scheme <- S7::new_class( client_credentials_flow = class_oauth2_token_flow, authorization_code_flow = class_oauth2_authorization_code_flow ), - constructor = function(..., - implicit_flow = class_oauth2_implicit_flow(), - password_flow = class_oauth2_token_flow(), - client_credentials_flow = class_oauth2_token_flow(), - authorization_code_flow = class_oauth2_authorization_code_flow()) { + constructor = function( + ..., + implicit_flow = class_oauth2_implicit_flow(), + password_flow = class_oauth2_token_flow(), + client_credentials_flow = class_oauth2_token_flow(), + authorization_code_flow = class_oauth2_authorization_code_flow() + ) { check_dots_empty() S7::new_object( S7::S7_object(), diff --git a/R/components-security_scheme.R b/R/components-security_scheme.R index c607de6..857edc4 100644 --- a/R/components-security_scheme.R +++ b/R/components-security_scheme.R @@ -75,33 +75,40 @@ S7::method(as_security_scheme, abstract_security_scheme) <- function(x, ...) { x } -S7::method(as_security_scheme, class_list) <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +S7::method(as_security_scheme, class_list) <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { if (!length(x) || !any(lengths(x))) { return(NULL) } type <- snakecase::to_snake_case(x$type) x$type <- NULL - switch(type, + switch( + type, api_key = as_api_key_security_scheme(x, ..., arg = arg, call = call), oauth_2 = as_oauth2_security_scheme(x, ..., arg = arg, call = call), oauth2 = as_oauth2_security_scheme(x, ..., arg = arg, call = call) ) } -S7::method(as_security_scheme, NULL) <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +S7::method(as_security_scheme, NULL) <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { NULL } -S7::method(as_security_scheme, class_any) <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +S7::method(as_security_scheme, class_any) <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { msg <- "Can't coerce {.arg {arg}} {.cls {class(x)}} to {.cls rapid::security_scheme}." if (missing(x)) { msg <- c( diff --git a/R/components-security_schemes.R b/R/components-security_schemes.R index 0f3f0f0..2a3d082 100644 --- a/R/components-security_schemes.R +++ b/R/components-security_schemes.R @@ -58,10 +58,12 @@ class_security_schemes <- S7::new_class( details = class_security_scheme_details, description = class_character ), - constructor = function(name = character(), - details = class_security_scheme_details(), - ..., - description = character()) { + constructor = function( + name = character(), + details = class_security_scheme_details(), + ..., + description = character() + ) { check_dots_empty() S7::new_object( S7::S7_object(), diff --git a/R/components.R b/R/components.R index 3ff7687..0e48cd4 100644 --- a/R/components.R +++ b/R/components.R @@ -35,8 +35,7 @@ class_components <- S7::new_class( properties = list( security_schemes = class_security_schemes ), - constructor = function(..., - security_schemes = class_security_schemes()) { + constructor = function(..., security_schemes = class_security_schemes()) { check_dots_empty() S7::new_object( S7::S7_object(), @@ -112,9 +111,6 @@ S7::method(length, class_components) <- function(x) { #' ) #' ) #' )) -as_components <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +as_components <- function(x, ..., arg = caller_arg(x), call = caller_env()) { as_api_object(x, class_components, ..., arg = arg, call = call) } diff --git a/R/info-license.R b/R/info-license.R index 8ae6adb..66d31e2 100644 --- a/R/info-license.R +++ b/R/info-license.R @@ -38,10 +38,12 @@ class_license <- S7::new_class( identifier = character_scalar_property("identifier"), url = character_scalar_property("url") ), - constructor = function(name = character(), - ..., - identifier = character(), - url = character()) { + constructor = function( + name = character(), + ..., + identifier = character(), + url = character() + ) { check_dots_empty() S7::new_object( S7::S7_object(), diff --git a/R/info-origin.R b/R/info-origin.R index 8bd3e4c..a56a9a6 100644 --- a/R/info-origin.R +++ b/R/info-origin.R @@ -36,10 +36,12 @@ class_origin <- S7::new_class( format = character_scalar_property("format"), version = character_scalar_property("version") ), - constructor = function(url = character(), - ..., - format = character(), - version = character()) { + constructor = function( + url = character(), + ..., + format = character(), + version = character() + ) { check_dots_empty() if (is.list(url) && length(url) == 1) { url <- unname(unlist(url)) @@ -88,17 +90,20 @@ S7::method(length, class_origin) <- function(x) { #' ) #' ) #' ) -as_origin <- S7::new_generic("as_origin", "x", function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { - S7::S7_dispatch() -}) +as_origin <- S7::new_generic( + "as_origin", + "x", + function(x, ..., arg = caller_arg(x), call = caller_env()) { + S7::S7_dispatch() + } +) -S7::method(as_origin, class_any) <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +S7::method(as_origin, class_any) <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { as_api_object(x, class_origin, ..., arg = arg, call = call) } diff --git a/R/info.R b/R/info.R index 6399bce..a4fd1aa 100644 --- a/R/info.R +++ b/R/info.R @@ -63,15 +63,17 @@ class_info <- S7::new_class( terms_of_service = character_scalar_property("terms_of_service"), origin = class_origin ), - constructor = function(title = character(), - version = character(), - ..., - contact = class_contact(), - description = character(), - license = class_license(), - summary = character(), - terms_of_service = character(), - origin = class_origin()) { + constructor = function( + title = character(), + version = character(), + ..., + contact = class_contact(), + description = character(), + license = class_license(), + summary = character(), + terms_of_service = character(), + origin = class_origin() + ) { check_dots_empty() S7::new_object( S7::S7_object(), diff --git a/R/paths.R b/R/paths.R index d9e9cc5..df89102 100644 --- a/R/paths.R +++ b/R/paths.R @@ -54,39 +54,47 @@ class_paths <- S7::new_class( #' as_paths(mtcars) as_paths <- S7::new_generic("as_paths", "x") -S7::method(as_paths, class_data.frame) <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +S7::method(as_paths, class_data.frame) <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { class_paths(x) } -S7::method(as_paths, class_any) <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +S7::method(as_paths, class_any) <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { as_api_object(x, class_paths, ..., arg = arg, call = call) } .parse_paths <- S7::new_generic(".parse_paths", "paths") -S7::method(.parse_paths, class_data.frame | class_paths) <- function(paths, - ...) { +S7::method(.parse_paths, class_data.frame | class_paths) <- function( + paths, + ... +) { paths } -S7::method(.parse_paths, class_list) <- function(paths, - openapi, - x, - call = caller_env()) { +S7::method(.parse_paths, class_list) <- function( + paths, + openapi, + x, + call = caller_env() +) { if (!is.null(openapi) && openapi >= "3") { return(.parse_openapi_spec(x, call = call)) } return(tibble::tibble()) } +# nocov start -.parse_openapi_spec <- function(x, call = caller_env()) { # nocov start - .check_tibblify_version(call = call) +.parse_openapi_spec <- function(x, call = caller_env()) { rlang::try_fetch( { tibblify::parse_openapi_spec(x) @@ -101,31 +109,6 @@ S7::method(.parse_paths, class_list) <- function(paths, ) } -.check_tibblify_version <- function(call = caller_env()) { - expected_body <- c( - "{", - "openapi_spec <- read_spec(file)", - "version <- openapi_spec$openapi", - "if (is_null(version) || version < \"3\") {\n cli_abort(\"OpenAPI versions before 3 are not supported.\")\n}", - "if (is_installed(\"memoise\")) {\n memoise::forget(parse_schema_memoised)\n}", - "out <- purrr::map(openapi_spec$paths, ~{\n parse_path_item_object(path_item_object = .x, openapi_spec = openapi_spec)\n})", - "fast_tibble(list(endpoint = names2(out), operations = unname(out)))" - ) - actual_body <- as.character(body(tibblify::parse_openapi_spec)) - - if (!identical(actual_body, expected_body)) { - cli::cli_abort( - c( - "Incorrect tibblify version.", - i = "This package requires an in-progress version of the package tibblify.", - i = "To parse this spec, first {.run pak::pak('mgirlich/tibblify#193')}." - ), - class = "rapid_error_bad_tibblify", - call = call - ) - } -} - # nocov end S7::method(.parse_paths, class_any) <- function(paths, ...) { diff --git a/R/servers-server_variables.R b/R/servers-server_variables.R index 97d6a12..8f624a9 100644 --- a/R/servers-server_variables.R +++ b/R/servers-server_variables.R @@ -88,9 +88,11 @@ S7::method(as_server_variables, class_list) <- function(x) { ) } -S7::method(as_server_variables, class_any) <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +S7::method(as_server_variables, class_any) <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { as_api_object(x, class_server_variables, ..., arg = arg, call = call) } diff --git a/R/servers-string_replacements.R b/R/servers-string_replacements.R index 7bc5790..986f178 100644 --- a/R/servers-string_replacements.R +++ b/R/servers-string_replacements.R @@ -43,11 +43,13 @@ class_string_replacements <- S7::new_class( enum = enum_property("enum"), description = class_character ), - constructor = function(name = character(), - default = character(), - ..., - enum = list(), - description = character()) { + constructor = function( + name = character(), + default = character(), + ..., + enum = list(), + description = character() + ) { check_dots_empty() S7::new_object( S7::S7_object(), @@ -63,11 +65,12 @@ class_string_replacements <- S7::new_class( "name", required = "default", optional = c("enum", "description") - ) %|0|% validate_in_enums( - self, - value_name = "default", - enum_name = "enum" - ) + ) %|0|% + validate_in_enums( + self, + value_name = "default", + enum_name = "enum" + ) } ) @@ -119,9 +122,11 @@ S7::method(as_string_replacements, class_list) <- function(x) { ) } -S7::method(as_string_replacements, class_any) <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +S7::method(as_string_replacements, class_any) <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { as_api_object(x, class_string_replacements, ..., arg = arg, call = call) } diff --git a/R/servers.R b/R/servers.R index ff918a7..363fabd 100644 --- a/R/servers.R +++ b/R/servers.R @@ -58,9 +58,11 @@ class_servers <- S7::new_class( description = class_character, variables = class_server_variables ), - constructor = function(url = character(), - description = character(), - variables = class_server_variables()) { + constructor = function( + url = character(), + description = character(), + variables = class_server_variables() + ) { S7::new_object( S7::S7_object(), url = url %||% character(), @@ -140,9 +142,11 @@ S7::method(as_servers, class_list) <- function(x, ..., call = caller_env()) { ) } -S7::method(as_servers, class_any) <- function(x, - ..., - arg = caller_arg(x), - call = caller_env()) { +S7::method(as_servers, class_any) <- function( + x, + ..., + arg = caller_arg(x), + call = caller_env() +) { as_api_object(x, class_servers, ..., arg = arg, call = call) } diff --git a/R/swagger_to_openapi.R b/R/swagger_to_openapi.R index 938503e..1068f23 100644 --- a/R/swagger_to_openapi.R +++ b/R/swagger_to_openapi.R @@ -4,7 +4,7 @@ swagger_to_openapi <- function(x) { # If it's already openapi 3+, return it. if ( length(x$openapi) && - numeric_version(as.character(x$openapi)) >= "3" + numeric_version(as.character(x$openapi)) >= "3" ) { return(x) } @@ -35,28 +35,30 @@ swagger_to_openapi <- function(x) { .is_swagger_spec <- S7::new_generic(".is_swagger_spec", "x") -S7::method(.is_swagger_spec, class_any) <- function(x, - ..., - min_version = character(), - arg = caller_arg(x), - call = caller_env()) { +S7::method(.is_swagger_spec, class_any) <- function( + x, + ..., + min_version = character(), + arg = caller_arg(x), + call = caller_env() +) { return(FALSE) } -S7::method(.is_swagger_spec, class_list) <- function(x, - min_version = character(), - arg = caller_arg(x), - call = caller_env()) { +S7::method(.is_swagger_spec, class_list) <- function( + x, + min_version = character(), + arg = caller_arg(x), + call = caller_env() +) { version <- x$openapi %||% x$swagger %||% x$swaggerVersion # TODO: Split this up. Don't be fancy. This will error if numeric_version # fails; instead we should return FALSE if we can't parse the version. return( length(version) == 1 && - ( - !length(min_version) || - numeric_version(as.character(version)) >= - numeric_version(as.character(min_version)) - ) + (!length(min_version) || + numeric_version(as.character(version)) >= + numeric_version(as.character(min_version))) ) } diff --git a/R/validate_in.R b/R/validate_in.R index d05f855..cd3efae 100644 --- a/R/validate_in.R +++ b/R/validate_in.R @@ -10,7 +10,8 @@ validate_in_enums <- function(obj, value_name, enum_name) { .check_all_in_enums <- function(values, enums) { unlist(purrr::map2( - values, enums, + values, + enums, .check_one_in_enum )) } @@ -30,10 +31,12 @@ validate_in_enums <- function(obj, value_name, enum_name) { ) } -.msg_some_not_in_fixed <- function(value_name, - enums, - missing_msgs, - enum_name = "the designated values") { +.msg_some_not_in_fixed <- function( + value_name, + enums, + missing_msgs, + enum_name = "the designated values" +) { enum_name <- cli::format_inline(enum_name) c( cli::format_inline("{.arg {value_name}} must be one of {enum_name}."), diff --git a/R/zzz.R b/R/zzz.R index 5a43c00..0fe131e 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -2,6 +2,7 @@ #' @rawNamespace if (getRversion() < "4.3.0") importFrom("S7", "@") NULL -.onLoad <- function(...) { # nocov start +.onLoad <- function(...) { + # nocov start S7::methods_register() } # nocov end diff --git a/air.toml b/air.toml new file mode 100644 index 0000000..e69de29 diff --git a/tests/testthat/test-as.R b/tests/testthat/test-as.R index 64eb274..282a456 100644 --- a/tests/testthat/test-as.R +++ b/tests/testthat/test-as.R @@ -22,7 +22,9 @@ test_that("as_api_object() warns about unexpected fields", { ) ) expect_warning( - {as_api_object(list(start = 1, end = 2, a = 1), simple_class)}, + { + as_api_object(list(start = 1, end = 2, a = 1), simple_class) + }, class = "rapid_warning_extra_names" ) }) diff --git a/tests/testthat/test-components-security_scheme-oauth2.R b/tests/testthat/test-components-security_scheme-oauth2.R index 3bc3d51..737a17c 100644 --- a/tests/testthat/test-components-security_scheme-oauth2.R +++ b/tests/testthat/test-components-security_scheme-oauth2.R @@ -51,8 +51,10 @@ test_that("class_oauth2_security_scheme() works with valid objects", { expect_identical( S7::prop_names(test_result), c( - "implicit_flow", "password_flow", - "client_credentials_flow", "authorization_code_flow" + "implicit_flow", + "password_flow", + "client_credentials_flow", + "authorization_code_flow" ) ) }) diff --git a/tests/testthat/test-components-security_scheme_details.R b/tests/testthat/test-components-security_scheme_details.R index 68051d4..619d014 100644 --- a/tests/testthat/test-components-security_scheme_details.R +++ b/tests/testthat/test-components-security_scheme_details.R @@ -40,7 +40,8 @@ test_that("class_security_scheme_details() accepts lists of security_schemes", { ) expect_snapshot( class_security_scheme_details(list( - class_api_key_security_scheme(), class_oauth2_security_scheme() + class_api_key_security_scheme(), + class_oauth2_security_scheme() )) ) }) diff --git a/tests/testthat/test-servers-string_replacements.R b/tests/testthat/test-servers-string_replacements.R index 7029934..33477f3 100644 --- a/tests/testthat/test-servers-string_replacements.R +++ b/tests/testthat/test-servers-string_replacements.R @@ -73,7 +73,8 @@ test_that("class_string_replacements() works for a full object", { default = c("demo", "8443", "v2"), description = c( "The active user's folder.", - NA, NA + NA, + NA ), enum = list( NULL, diff --git a/tests/testthat/test-servers.R b/tests/testthat/test-servers.R index ea4dcca..e9ee2c7 100644 --- a/tests/testthat/test-servers.R +++ b/tests/testthat/test-servers.R @@ -47,7 +47,8 @@ test_that("length() of a servers reports the overall length", { default = c("demo", "8443", "v2"), description = c( "The active user's folder.", - NA, NA + NA, + NA ), enum = list( NULL, @@ -123,7 +124,8 @@ test_that("as_servers() returns expected objects", { name = c("username", "port", "basePath"), description = c( "this value is assigned by the service provider", - NA, NA + NA, + NA ), default = c("demo", "8443", "v2"), enum = list(NULL, c("8443", "443"), NULL)