From 574ec13febe3a9a715d6200118ab5f95f646e507 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Sat, 6 Feb 2021 16:00:22 -0800 Subject: [PATCH 01/20] Un-deprecate run(swaggerCallback=), add run(quiet=) param To better support headless running scenarios --- R/plumber.R | 21 ++++++++++++++------- R/ui.R | 10 +++++++--- man/Plumber.Rd | 9 +++++++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/R/plumber.R b/R/plumber.R index 99bd63de3..3552bde78 100644 --- a/R/plumber.R +++ b/R/plumber.R @@ -145,14 +145,18 @@ Plumber <- R6Class( #' This value does not need to be explicitly assigned. To explicitly set it, see [options_plumber()]. #' @param debug Deprecated. See `$setDebug()` #' @param swagger Deprecated. See `$setDocs(docs)` or `$setApiSpec()` - #' @param swaggerCallback Deprecated. See `$setDocsCallback()` + #' @param swaggerCallback An optional single-argument function that is + #' called back with the URL to an OpenAPI user interface when one becomes + #' ready. If missing, defaults to `$setDocsCallback()`. + #' @param quiet If `TRUE`, don't print routine startup messages. #' @importFrom lifecycle deprecated run = function( host = '127.0.0.1', port = getOption('plumber.port', NULL), swagger = deprecated(), debug = deprecated(), - swaggerCallback = deprecated() + swaggerCallback, + quiet = FALSE ) { if (isTRUE(private$disable_run)) { @@ -184,14 +188,16 @@ Plumber <- R6Class( } } } - if (lifecycle::is_present(swaggerCallback)) { - lifecycle::deprecate_warn("1.0.0", "run(swaggerCallback = )", "setDocsCallback(callback = )") - self$setDocsCallback(swaggerCallback) + + if (missing(swaggerCallback)) { + swaggerCallback <- private$docs_callback } port <- findPort(port) - message("Running plumber API at ", urlHost(host = host, port = port, changeHostLocation = FALSE)) + if (!isTRUE(quiet)) { + message("Running plumber API at ", urlHost(host = host, port = port, changeHostLocation = FALSE)) + } # Set and restore the wd to make it appear that the proc is running local to the file's definition. if (!is.null(private$filename)) { @@ -205,7 +211,8 @@ Plumber <- R6Class( host = host, port = port, docs_info = private$docs_info, - callback = private$docs_callback + callback = swaggerCallback, + quiet = quiet ) on.exit(unmount_docs(self, private$docs_info), add = TRUE) } diff --git a/R/ui.R b/R/ui.R index ffe5f1e32..b4c779d6b 100644 --- a/R/ui.R +++ b/R/ui.R @@ -2,7 +2,7 @@ # Mount OpenAPI and Docs #' @noRd -mount_docs <- function(pr, host, port, docs_info, callback) { +mount_docs <- function(pr, host, port, docs_info, callback, quiet = FALSE) { # return early if not enabled if (!isTRUE(docs_info$enabled)) { @@ -26,14 +26,18 @@ mount_docs <- function(pr, host, port, docs_info, callback) { # Mount Docs if (length(registered_docs()) == 0) { - message("No visual documentation options registered. See help(register_docs).") + if (!isTRUE(quiet)) { + message("No visual documentation options registered. See help(register_docs).") + } return() } if (is_docs_available(docs_info$docs)) { docs_mount <- .globals$docs[[docs_info$docs]]$mount docs_url <- do.call(docs_mount, c(list(pr, api_url), docs_info$args)) - message("Running ", docs_info$docs, " Docs at ", docs_url, sep = "") + if (!isTRUE(quiet)) { + message("Running ", docs_info$docs, " Docs at ", docs_url, sep = "") + } } else { return() } diff --git a/man/Plumber.Rd b/man/Plumber.Rd index 11736bc4e..0dab3f8a2 100644 --- a/man/Plumber.Rd +++ b/man/Plumber.Rd @@ -220,7 +220,8 @@ See also: \code{\link[=pr_run]{pr_run()}} port = getOption("plumber.port", NULL), swagger = deprecated(), debug = deprecated(), - swaggerCallback = deprecated() + swaggerCallback, + quiet = FALSE )}\if{html}{\out{}} } @@ -241,7 +242,11 @@ This value does not need to be explicitly assigned. To explicitly set it, see \c \item{\code{debug}}{Deprecated. See \verb{$setDebug()}} -\item{\code{swaggerCallback}}{Deprecated. See \verb{$setDocsCallback()}} +\item{\code{swaggerCallback}}{An optional single-argument function that is +called back with the URL to an OpenAPI user interface when one becomes +ready. If missing, defaults to \verb{$setDocsCallback()}.} + +\item{\code{quiet}}{If \code{TRUE}, don't print routine startup messages.} } \if{html}{\out{}} } From b642e2e6b3cfdc738c6752332bb9a4d0a29325fd Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Sat, 6 Feb 2021 16:03:36 -0800 Subject: [PATCH 02/20] Add pr_run(quiet=), update news --- NEWS.md | 2 ++ R/pr.R | 7 +++++-- man/pr_run.Rd | 9 ++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 27135fdf8..4be0ff10f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -23,6 +23,8 @@ plumber 1.0.0.9999 Development version * OpenAPI Specification can be set using a file path. (@meztez #696) +* Added `quiet = TRUE` to `Plumber$run()` to suppress routine startup messages. (@jcheng5 #765) + ### Bug fixes * Fixed bug where `httpuv` would return a status of `500` with body `An exception occurred` if no headers were set on the response object. (#745) diff --git a/R/pr.R b/R/pr.R index ec2c4219e..826ec61a8 100644 --- a/R/pr.R +++ b/R/pr.R @@ -475,6 +475,7 @@ pr_filter <- function(pr, #' @param port A number or integer that indicates the server port that should #' be listened on. Note that on most Unix-like systems including Linux and #' Mac OS X, port numbers smaller than 1025 require root privileges. +#' @param quiet If `TRUE`, don't print routine startup messages. #' #' @examples #' \dontrun{ @@ -488,11 +489,13 @@ pr_filter <- function(pr, #' @export pr_run <- function(pr, host = '127.0.0.1', - port = getOption('plumber.port', NULL) + port = getOption('plumber.port', NULL), + quiet = FALSE ) { validate_pr(pr) pr$run(host = host, - port = port) + port = port, + quiet = quiet) } diff --git a/man/pr_run.Rd b/man/pr_run.Rd index fdd279eec..46a0d6606 100644 --- a/man/pr_run.Rd +++ b/man/pr_run.Rd @@ -4,7 +4,12 @@ \alias{pr_run} \title{Start a server using \code{plumber} object} \usage{ -pr_run(pr, host = "127.0.0.1", port = getOption("plumber.port", NULL)) +pr_run( + pr, + host = "127.0.0.1", + port = getOption("plumber.port", NULL), + quiet = FALSE +) } \arguments{ \item{pr}{A Plumber API. Note: The supplied Plumber API object will also be updated in place as well as returned by the function.} @@ -16,6 +21,8 @@ all IPv4 addresses and "::/0" represents all IPv6 addresses.} \item{port}{A number or integer that indicates the server port that should be listened on. Note that on most Unix-like systems including Linux and Mac OS X, port numbers smaller than 1025 require root privileges.} + +\item{quiet}{If \code{TRUE}, don't print routine startup messages.} } \description{ \code{port} does not need to be explicitly assigned. From b79563322b578d96a923a54fda2e1d25d77b6886 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Sat, 6 Feb 2021 16:34:25 -0800 Subject: [PATCH 03/20] Add unit test --- tests/testthat/test-plumber.R | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/testthat/test-plumber.R b/tests/testthat/test-plumber.R index b141a531f..4883afadb 100644 --- a/tests/testthat/test-plumber.R +++ b/tests/testthat/test-plumber.R @@ -602,3 +602,14 @@ test_that("handle method rejects forbidden arguments", { expect_error(pr$handle("GET", "nested/path/here", function(){}, expr = function(){}), "can not be supplied to") }) + +test_that("quiet=TRUE suppresses startup messages", { + later::later(httpuv::interrupt) # Causes pr_run() to immediately exit + expect_message(pr() %>% pr_run()) + + later::later(httpuv::interrupt) + expect_message(pr() %>% pr_run(quiet = TRUE), NA) + + later::later(httpuv::interrupt) + expect_message(pr()$run(quiet = TRUE), NA) +}) From 2aa507fd89f301d7b07071a032bbe6f08a27fb8b Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Sat, 6 Feb 2021 16:38:08 -0800 Subject: [PATCH 04/20] Add pr_run(swaggerCallback=) --- R/pr.R | 5 +++++ man/pr_run.Rd | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/R/pr.R b/R/pr.R index 826ec61a8..e6c81ae02 100644 --- a/R/pr.R +++ b/R/pr.R @@ -475,6 +475,9 @@ pr_filter <- function(pr, #' @param port A number or integer that indicates the server port that should #' be listened on. Note that on most Unix-like systems including Linux and #' Mac OS X, port numbers smaller than 1025 require root privileges. +#' @param swaggerCallback An optional single-argument function that is called +#' back with the URL to an OpenAPI user interface when one becomes ready. If +#' missing, defaults to `$setDocsCallback()`. #' @param quiet If `TRUE`, don't print routine startup messages. #' #' @examples @@ -490,11 +493,13 @@ pr_filter <- function(pr, pr_run <- function(pr, host = '127.0.0.1', port = getOption('plumber.port', NULL), + swaggerCallback, quiet = FALSE ) { validate_pr(pr) pr$run(host = host, port = port, + swaggerCallback = swaggerCallback, quiet = quiet) } diff --git a/man/pr_run.Rd b/man/pr_run.Rd index 46a0d6606..a9a104df2 100644 --- a/man/pr_run.Rd +++ b/man/pr_run.Rd @@ -8,6 +8,7 @@ pr_run( pr, host = "127.0.0.1", port = getOption("plumber.port", NULL), + swaggerCallback, quiet = FALSE ) } @@ -22,6 +23,10 @@ all IPv4 addresses and "::/0" represents all IPv6 addresses.} be listened on. Note that on most Unix-like systems including Linux and Mac OS X, port numbers smaller than 1025 require root privileges.} +\item{swaggerCallback}{An optional single-argument function that is called +back with the URL to an OpenAPI user interface when one becomes ready. If +missing, defaults to \verb{$setDocsCallback()}.} + \item{quiet}{If \code{TRUE}, don't print routine startup messages.} } \description{ From 0f9de8af26238c8d946b12584ee2f1a9a76ce4c3 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 26 Feb 2021 13:38:47 -0500 Subject: [PATCH 05/20] Use rlang to leverage `rlang::missing_arg()` and friends --- DESCRIPTION | 3 ++- NAMESPACE | 1 + R/plumber.R | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 963846e3b..7547f6559 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -32,7 +32,8 @@ Imports: magrittr, mime, lifecycle (>= 0.2.0), - ellipsis (>= 0.3.0) + ellipsis (>= 0.3.0), + rlang LazyData: TRUE ByteCompile: TRUE Suggests: diff --git a/NAMESPACE b/NAMESPACE index fac11426b..16ba38749 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -104,6 +104,7 @@ importFrom(jsonlite,parse_json) importFrom(jsonlite,toJSON) importFrom(lifecycle,deprecated) importFrom(magrittr,"%>%") +importFrom(rlang,missing_arg) importFrom(stats,runif) importFrom(stats,setNames) importFrom(utils,file.edit) diff --git a/R/plumber.R b/R/plumber.R index e49051794..2baf776e1 100644 --- a/R/plumber.R +++ b/R/plumber.R @@ -150,6 +150,7 @@ Plumber <- R6Class( #' ready. If missing, defaults to `$setDocsCallback()`. #' @param quiet If `TRUE`, don't print routine startup messages. #' @importFrom lifecycle deprecated + #' @importFrom rlang missing_arg run = function( host = '127.0.0.1', port = getOption('plumber.port', NULL), From 6a2af47fb1f427090ca45124198dc99672ef524f Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 26 Feb 2021 13:42:46 -0500 Subject: [PATCH 06/20] Do not permanently alter router given `$run()` parameters `debug`, `swaggerCallback`, and `docs` Add `...` and check that it is empty --- R/plumber.R | 126 +++++++++++++++++++++++++++++++------------------ R/pr.R | 20 ++++++-- man/Plumber.Rd | 20 ++++++-- man/pr_run.Rd | 20 ++++++-- 4 files changed, 129 insertions(+), 57 deletions(-) diff --git a/R/plumber.R b/R/plumber.R index 2baf776e1..0743015e2 100644 --- a/R/plumber.R +++ b/R/plumber.R @@ -143,20 +143,29 @@ Plumber <- R6Class( #' Mac OS X, port numbers smaller than 1025 require root privileges. #' #' This value does not need to be explicitly assigned. To explicitly set it, see [options_plumber()]. - #' @param debug Deprecated. See `$setDebug()` - #' @param swagger Deprecated. See `$setDocs(docs)` or `$setApiSpec()` + #' @param debug If `TRUE`, it will provide more insight into your API errors. Using this value will only last for the duration of the run. See `$setDebug()` + #' @param swagger Deprecated. Please use `docs` instead. See `$setDocs(docs)` or `$setApiSpec()` for more customization. #' @param swaggerCallback An optional single-argument function that is #' called back with the URL to an OpenAPI user interface when one becomes - #' ready. If missing, defaults to `$setDocsCallback()`. + #' ready. If missing, defaults to information previously set with `$setDocsCallback()`. + #' This value will only be used while running the router. + #' @param docs Visual documentation value to use while running the API. + #' This value will only be used while running the router. + #' If missing, defaults to information previously set with `setDocs()`. + #' For more customization, see `$setDocs()` or [pr_set_docs()] for examples. #' @param quiet If `TRUE`, don't print routine startup messages. + #' @param ... Should be empty. #' @importFrom lifecycle deprecated #' @importFrom rlang missing_arg run = function( host = '127.0.0.1', port = getOption('plumber.port', NULL), swagger = deprecated(), - debug = deprecated(), - swaggerCallback, + debug = missing_arg(), + swaggerCallback = missing_arg(), + ..., + # any new args should go below `...` + docs = missing_arg(), quiet = FALSE ) { @@ -164,37 +173,57 @@ Plumber <- R6Class( stop("Plumber router `$run()` method should not be called while `plumb()`ing a file") } + ellipsis::check_dots_empty() + # Legacy support for RStudio pro products. # Checks must be kept for >= 2 yrs after plumber v1.0.0 release date - if (lifecycle::is_present(debug)) { - lifecycle::deprecate_warn("1.0.0", "run(debug = )", "setDebug(debug = )") - self$setDebug(debug) - } if (lifecycle::is_present(swagger)) { - if (is.function(swagger)) { - # between v0.4.6 and v1.0.0 - lifecycle::deprecate_warn("1.0.0", "run(swagger = )", "setApiSpec(api = )") - self$setApiSpec(swagger) - # spec is now enabled by default. Do not alter + if (!rlang::is_missing(docs)) { + lifecycle::deprecate_warn("1.0.0", "Plumber$run(swagger = )", "Plumber$run(docs = )", details = "`docs` will take preference (ignoring `swagger`)") + # (`docs` is resolved after `swagger` checks) } else { - if (isTRUE(private$docs_info$has_not_been_set)) { - # <= v0.4.6 - lifecycle::deprecate_warn("1.0.0", "run(swagger = )", "setDocs(docs = )") - self$setDocs(swagger) + if (is.function(swagger)) { + # between v0.4.6 and v1.0.0 + lifecycle::deprecate_warn("1.0.0", "Plumber$run(swagger = )", "Plumber$setApiSpec(api = )") + # set the new api function and force turn on the docs + old_api_spec_handler <- private$api_spec_handler + self$setApiSpec(swagger) + on.exit({ + private$api_spec_handler <- old_api_spec_handler + }, add = TRUE) + docs <- TRUE } else { - # $setDocs() has been called (other than during initialization). - # Believe that it is the correct behavior - # Warn about updating the run method - lifecycle::deprecate_warn("1.0.0", "run(swagger = )", details = "The Plumber docs have already been set. Ignoring `swagger` parameter.") + if (isTRUE(private$docs_info$has_not_been_set)) { + # <= v0.4.6 + lifecycle::deprecate_warn("1.0.0", "Plumber$run(swagger = )", "Plumber$run(docs = )") + docs <- swagger + } else { + # $setDocs() has been called (other than during initialization). + # `docs` is not provided + # Believe that prior `$setDocs()` behavior is the correct behavior + # Warn about updating the run method + lifecycle::deprecate_warn("1.0.0", "Plumber$run(swagger = )", "Plumber$run(docs = )", details = "The Plumber docs have already been set. Ignoring `swagger` parameter.") + } } } } - if (missing(swaggerCallback)) { - swaggerCallback <- private$docs_callback - } - + swaggerCallback <- rlang::maybe_missing(swaggerCallback, private$docs_callback) port <- findPort(port) + if (!rlang::is_missing(debug)) { + prev_debug <- self$getDebug() + self$setDebug(debug) + on.exit({ + self$setDebug(prev_debug) + }, add = TRUE) + } + docs_info <- + if (!rlang::is_missing(docs)) { + # Manually provided. Need to upgrade the parameter + upgrade_docs_parameter(docs) + } else { + private$docs_info + } if (!isTRUE(quiet)) { message("Running plumber API at ", urlHost(host = host, port = port, changeHostLocation = FALSE)) @@ -206,16 +235,16 @@ Plumber <- R6Class( on.exit({setwd(old_wd)}, add = TRUE) } - if (isTRUE(private$docs_info$enabled)) { + if (isTRUE(docs_info$enabled)) { mount_docs( pr = self, host = host, port = port, - docs_info = private$docs_info, + docs_info = docs_info, callback = swaggerCallback, quiet = quiet ) - on.exit(unmount_docs(self, private$docs_info), add = TRUE) + on.exit(unmount_docs(self, docs_info), add = TRUE) } on.exit(private$runHooks("exit"), add = TRUE) @@ -887,23 +916,7 @@ Plumber <- R6Class( docs = getOption("plumber.docs", TRUE), ... ) { - stopifnot(length(docs) == 1) - stopifnot(is.logical(docs) || is.character(docs)) - if (isTRUE(docs)) { - docs <- "swagger" - } - if (is.character(docs) && is_docs_available(docs)) { - enabled <- TRUE - } else { - enabled <- FALSE - docs <- "__not_enabled__" - } - private$docs_info <- list( - enabled = enabled, - docs = docs, - args = list(...), - has_not_been_set = FALSE - ) + private$docs_info <- upgrade_docs_parameter(docs, ...) }, #' @description Set a callback to notify where the API's visual documentation is located. #' @@ -1279,6 +1292,27 @@ Plumber <- R6Class( +upgrade_docs_parameter <- function(docs, ...) { + stopifnot(length(docs) == 1) + stopifnot(is.logical(docs) || is.character(docs)) + if (isTRUE(docs)) { + docs <- "swagger" + } + if (is.character(docs) && is_docs_available(docs)) { + enabled <- TRUE + } else { + enabled <- FALSE + docs <- "__not_enabled__" + } + + list( + enabled = enabled, + docs = docs, + args = list(...), + has_not_been_set = FALSE + ) +} + diff --git a/R/pr.R b/R/pr.R index e6c81ae02..9088f6f0b 100644 --- a/R/pr.R +++ b/R/pr.R @@ -475,9 +475,15 @@ pr_filter <- function(pr, #' @param port A number or integer that indicates the server port that should #' be listened on. Note that on most Unix-like systems including Linux and #' Mac OS X, port numbers smaller than 1025 require root privileges. +#' @param ... Should be empty. +#' @param docs Visual documentation value to use while running the API. +#' This value will only be used while running the router. +#' If missing, defaults to information previously set with [pr_set_docs()]. +#' For more customization, see [pr_set_docs()] for examples. #' @param swaggerCallback An optional single-argument function that is called #' back with the URL to an OpenAPI user interface when one becomes ready. If -#' missing, defaults to `$setDocsCallback()`. +#' missing, defaults to information set with [pr_set_docs_callback()]. +#' This value will only be used while running the router. #' @param quiet If `TRUE`, don't print routine startup messages. #' #' @examples @@ -486,19 +492,27 @@ pr_filter <- function(pr, #' pr_run() #' #' pr() %>% -#' pr_run(port = 5762) +#' pr_run( +#' port = 5762, +#' docs = FALSE, +#' quiet = TRUE +#' ) #' } #' #' @export pr_run <- function(pr, host = '127.0.0.1', port = getOption('plumber.port', NULL), - swaggerCallback, + ..., + docs = missing_arg(), + swaggerCallback = missing_arg(), quiet = FALSE ) { validate_pr(pr) + ellipsis::check_dots_empty() pr$run(host = host, port = port, + docs = docs, swaggerCallback = swaggerCallback, quiet = quiet) } diff --git a/man/Plumber.Rd b/man/Plumber.Rd index 0dab3f8a2..bda264463 100644 --- a/man/Plumber.Rd +++ b/man/Plumber.Rd @@ -219,8 +219,10 @@ See also: \code{\link[=pr_run]{pr_run()}} host = "127.0.0.1", port = getOption("plumber.port", NULL), swagger = deprecated(), - debug = deprecated(), - swaggerCallback, + debug = missing_arg(), + swaggerCallback = missing_arg(), + ..., + docs = missing_arg(), quiet = FALSE )}\if{html}{\out{}} } @@ -238,13 +240,21 @@ Mac OS X, port numbers smaller than 1025 require root privileges. This value does not need to be explicitly assigned. To explicitly set it, see \code{\link[=options_plumber]{options_plumber()}}.} -\item{\code{swagger}}{Deprecated. See \verb{$setDocs(docs)} or \verb{$setApiSpec()}} +\item{\code{swagger}}{Deprecated. Please use \code{docs} instead. See \verb{$setDocs(docs)} or \verb{$setApiSpec()} for more customization.} -\item{\code{debug}}{Deprecated. See \verb{$setDebug()}} +\item{\code{debug}}{If \code{TRUE}, it will provide more insight into your API errors. Using this value will only last for the duration of the run. See \verb{$setDebug()}} \item{\code{swaggerCallback}}{An optional single-argument function that is called back with the URL to an OpenAPI user interface when one becomes -ready. If missing, defaults to \verb{$setDocsCallback()}.} +ready. If missing, defaults to information previously set with \verb{$setDocsCallback()}. +This value will only be used while running the router.} + +\item{\code{...}}{Should be empty.} + +\item{\code{docs}}{Visual documentation value to use while running the API. +This value will only be used while running the router. +If missing, defaults to information previously set with \code{setDocs()}. +For more customization, see \verb{$setDocs()} or \code{\link[=pr_set_docs]{pr_set_docs()}} for examples.} \item{\code{quiet}}{If \code{TRUE}, don't print routine startup messages.} } diff --git a/man/pr_run.Rd b/man/pr_run.Rd index a9a104df2..ac6496c26 100644 --- a/man/pr_run.Rd +++ b/man/pr_run.Rd @@ -8,7 +8,9 @@ pr_run( pr, host = "127.0.0.1", port = getOption("plumber.port", NULL), - swaggerCallback, + ..., + docs = missing_arg(), + swaggerCallback = missing_arg(), quiet = FALSE ) } @@ -23,9 +25,17 @@ all IPv4 addresses and "::/0" represents all IPv6 addresses.} be listened on. Note that on most Unix-like systems including Linux and Mac OS X, port numbers smaller than 1025 require root privileges.} +\item{...}{Should be empty.} + +\item{docs}{Visual documentation value to use while running the API. +This value will only be used while running the router. +If missing, defaults to information previously set with \code{\link[=pr_set_docs]{pr_set_docs()}}. +For more customization, see \code{\link[=pr_set_docs]{pr_set_docs()}} for examples.} + \item{swaggerCallback}{An optional single-argument function that is called back with the URL to an OpenAPI user interface when one becomes ready. If -missing, defaults to \verb{$setDocsCallback()}.} +missing, defaults to information set with \code{\link[=pr_set_docs_callback]{pr_set_docs_callback()}}. +This value will only be used while running the router.} \item{quiet}{If \code{TRUE}, don't print routine startup messages.} } @@ -38,7 +48,11 @@ pr() \%>\% pr_run() pr() \%>\% - pr_run(port = 5762) + pr_run( + port = 5762, + docs = FALSE, + quiet = TRUE + ) } } From fc33270f604413def4e0338c9a6ad288f6cc6a68 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 26 Feb 2021 13:43:11 -0500 Subject: [PATCH 07/20] Test that the router is not permanently updated by `$run()` parameters --- tests/testthat/test-plumber-run.R | 66 +++++++++++++++++++++++++++++++ tests/testthat/test-plumber.R | 11 ------ 2 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 tests/testthat/test-plumber-run.R diff --git a/tests/testthat/test-plumber-run.R b/tests/testthat/test-plumber-run.R new file mode 100644 index 000000000..995bb05ad --- /dev/null +++ b/tests/testthat/test-plumber-run.R @@ -0,0 +1,66 @@ + +with_interrupt <- function(expr) { + # Causes pr_run() to immediately exit + later::later(httpuv::interrupt) + force(expr) +} + +test_that("quiet=TRUE suppresses startup messages", { + with_interrupt({ + expect_message(pr() %>% pr_run(quiet = TRUE), NA) + }) + with_interrupt({ + expect_message(pr()$run(quiet = TRUE), NA) + }) +}) + +test_that("`docs` does not not permanetly set pr information", { + doc_name <- "swagger" + root <- pr() %>% pr_set_docs(doc_name) + with_interrupt({ + expect_failure( + expect_message({ + root %>% pr_run(docs = FALSE) + }, doc_name) + ) + }) + with_interrupt({ + expect_failure( + expect_message({ + root$run(docs = FALSE) + }, doc_name) + ) + }) + with_interrupt({ + expect_message({ + root %>% pr_run(quiet = FALSE) + }, doc_name) + }) +}) + +test_that("`swaggerCallback` does not not permanetly set pr information", { + msg <- "test got here!" + my_func <- function(url) { + message(msg) + } + root <- pr() %>% pr_set_docs_callback(my_func) + with_interrupt({ + expect_failure( + expect_message({ + root %>% pr_run(swaggerCallback = NULL) + }, msg) + ) + }) + with_interrupt({ + expect_failure( + expect_message({ + root$run(swaggerCallback = NULL) + }, msg) + ) + }) + with_interrupt({ + expect_message({ + root %>% pr_run(quiet = FALSE) + }, msg) + }) +}) diff --git a/tests/testthat/test-plumber.R b/tests/testthat/test-plumber.R index 4883afadb..b141a531f 100644 --- a/tests/testthat/test-plumber.R +++ b/tests/testthat/test-plumber.R @@ -602,14 +602,3 @@ test_that("handle method rejects forbidden arguments", { expect_error(pr$handle("GET", "nested/path/here", function(){}, expr = function(){}), "can not be supplied to") }) - -test_that("quiet=TRUE suppresses startup messages", { - later::later(httpuv::interrupt) # Causes pr_run() to immediately exit - expect_message(pr() %>% pr_run()) - - later::later(httpuv::interrupt) - expect_message(pr() %>% pr_run(quiet = TRUE), NA) - - later::later(httpuv::interrupt) - expect_message(pr()$run(quiet = TRUE), NA) -}) From 07430f4891effb1b3769d7f75c036cdb9ee38abc Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 26 Feb 2021 15:45:43 -0500 Subject: [PATCH 08/20] Pass through debug --- R/pr.R | 5 +++++ man/pr_run.Rd | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/R/pr.R b/R/pr.R index 9088f6f0b..c388a9235 100644 --- a/R/pr.R +++ b/R/pr.R @@ -476,6 +476,9 @@ pr_filter <- function(pr, #' be listened on. Note that on most Unix-like systems including Linux and #' Mac OS X, port numbers smaller than 1025 require root privileges. #' @param ... Should be empty. +#' @param debug If `TRUE`, it will provide more insight into your API errors. +#' Using this value will only last for the duration of the run. +#' If [pr_set_debug()] has not been called, `debug` will default to `interactive()` at [pr_run()] time #' @param docs Visual documentation value to use while running the API. #' This value will only be used while running the router. #' If missing, defaults to information previously set with [pr_set_docs()]. @@ -504,6 +507,7 @@ pr_run <- function(pr, host = '127.0.0.1', port = getOption('plumber.port', NULL), ..., + debug = missing_arg(), docs = missing_arg(), swaggerCallback = missing_arg(), quiet = FALSE @@ -512,6 +516,7 @@ pr_run <- function(pr, ellipsis::check_dots_empty() pr$run(host = host, port = port, + debug = debug, docs = docs, swaggerCallback = swaggerCallback, quiet = quiet) diff --git a/man/pr_run.Rd b/man/pr_run.Rd index ac6496c26..3fcf24c82 100644 --- a/man/pr_run.Rd +++ b/man/pr_run.Rd @@ -9,6 +9,7 @@ pr_run( host = "127.0.0.1", port = getOption("plumber.port", NULL), ..., + debug = missing_arg(), docs = missing_arg(), swaggerCallback = missing_arg(), quiet = FALSE @@ -27,6 +28,10 @@ Mac OS X, port numbers smaller than 1025 require root privileges.} \item{...}{Should be empty.} +\item{debug}{If \code{TRUE}, it will provide more insight into your API errors. +Using this value will only last for the duration of the run. +If \code{\link[=pr_set_debug]{pr_set_debug()}} has not been called, \code{debug} will default to \code{interactive()} at \code{\link[=pr_run]{pr_run()}} time} + \item{docs}{Visual documentation value to use while running the API. This value will only be used while running the router. If missing, defaults to information previously set with \code{\link[=pr_set_docs]{pr_set_docs()}}. From f43ce6c2059787b6de4284c329ff2e9bab2fe0cc Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 26 Feb 2021 15:46:14 -0500 Subject: [PATCH 09/20] Delay the retrieval of debug value until run time. Provide good fallback --- NAMESPACE | 1 + R/plumber.R | 56 +++++++++++++++++++++++++++++++++++--------------- man/Plumber.Rd | 6 +++--- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 16ba38749..fa6fd8477 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -104,6 +104,7 @@ importFrom(jsonlite,parse_json) importFrom(jsonlite,toJSON) importFrom(lifecycle,deprecated) importFrom(magrittr,"%>%") +importFrom(rlang,"%||%") importFrom(rlang,missing_arg) importFrom(stats,runif) importFrom(stats,setNames) diff --git a/R/plumber.R b/R/plumber.R index 0743015e2..80684d2af 100644 --- a/R/plumber.R +++ b/R/plumber.R @@ -1,5 +1,6 @@ #' @import R6 #' @import stringi +#' @importFrom rlang %||% NULL # used to identify annotation flags. @@ -86,7 +87,6 @@ Plumber <- R6Class( } # Initialize - private$maxSize <- getOption('plumber.maxRequestSize', 0) #0 Unlimited self$setSerializer(serializer_json()) # Default parsers to maintain legacy features self$setParsers(c("json", "form", "text", "octet", "multi")) @@ -94,8 +94,8 @@ Plumber <- R6Class( self$set404Handler(default404Handler) self$setDocs(TRUE) private$docs_info$has_not_been_set <- TRUE # set to know if `$setDocs()` has been called before `$run()` - self$setDocsCallback(getOption('plumber.docs.callback', getOption('plumber.swagger.url'))) - self$setDebug(interactive()) + private$docs_callback <- rlang::missing_arg() + private$debug <- NULL self$setApiSpec(NULL) # Add in the initial filters @@ -143,7 +143,7 @@ Plumber <- R6Class( #' Mac OS X, port numbers smaller than 1025 require root privileges. #' #' This value does not need to be explicitly assigned. To explicitly set it, see [options_plumber()]. - #' @param debug If `TRUE`, it will provide more insight into your API errors. Using this value will only last for the duration of the run. See `$setDebug()` + #' @param debug If `TRUE`, it will provide more insight into your API errors. Using this value will only last for the duration of the run. If a `$setDebug()` has not been called, `debug` will default to `interactive()` at `$run()` time. See `$setDebug()` for more details. #' @param swagger Deprecated. Please use `docs` instead. See `$setDocs(docs)` or `$setApiSpec()` for more customization. #' @param swaggerCallback An optional single-argument function that is #' called back with the URL to an OpenAPI user interface when one becomes @@ -208,15 +208,35 @@ Plumber <- R6Class( } } - swaggerCallback <- rlang::maybe_missing(swaggerCallback, private$docs_callback) port <- findPort(port) - if (!rlang::is_missing(debug)) { - prev_debug <- self$getDebug() - self$setDebug(debug) - on.exit({ - self$setDebug(prev_debug) - }, add = TRUE) - } + + # Delay setting max size option. It could be set in `plumber.R`, which is after initialization + private$maxSize <- getOption('plumber.maxRequestSize', 0) #0 Unlimited + + # Delay the setting of swaggerCallback as long as possible. + # An option could be set in `plumber.R`, which is after initialization + # Order: Run method parameter, internally set value, option, fallback option, NULL + swaggerCallback <- + rlang::maybe_missing(swaggerCallback, + rlang::maybe_missing(private$docs_callback, + getOption('plumber.docs.callback', getOption('plumber.swagger.url', NULL)) + ) + ) + + # Delay the setting of debug as long as possible. + # The router could be made in an interactive setting and used in background process. + # Do not determine if interactive until run time + prev_debug <- private$debug + # Fix the debug value while running. + self$setDebug( + # Order: Run method param, internally set value, is interactive() + # `$getDebug()` is dynamic given `setDebug()` has never been called. + rlang::maybe_missing(debug, self$getDebug()) + ) + on.exit({ + private$debug <- prev_debug + }, add = TRUE) + docs_info <- if (!rlang::is_missing(docs)) { # Manually provided. Need to upgrade the parameter @@ -939,7 +959,7 @@ Plumber <- R6Class( } private$docs_callback <- callback }, - #' @description Set debug value to include error messages + #' @description Set debug value to include error messages. If never set, the result of `interactive()` will be used. #' #' See also: `$getDebug()` and [pr_set_debug()] #' @param debug `TRUE` provides more insight into your API errors. @@ -947,11 +967,11 @@ Plumber <- R6Class( stopifnot(length(debug) == 1) private$debug <- isTRUE(debug) }, - #' @description Retrieve the `debug` value. + #' @description Retrieve the `debug` value. If it has never been set, the result of `interactive()` will be used. #' #' See also: `$getDebug()` and [pr_set_debug()] getDebug = function() { - private$debug + private$debug %||% default_debug() }, #' @description Add a filter to plumber router #' @@ -1197,7 +1217,7 @@ Plumber <- R6Class( errorHandler = NULL, notFoundHandler = NULL, - maxSize = NULL, # Max request size in bytes + maxSize = 0, # Max request size in bytes. (0 is a no-op) api_spec_handler = NULL, docs_info = NULL, @@ -1315,7 +1335,9 @@ upgrade_docs_parameter <- function(docs, ...) { - +default_debug <- function() { + interactive() +} urlHost <- function(scheme = "http", host, port, path = "", changeHostLocation = FALSE) { diff --git a/man/Plumber.Rd b/man/Plumber.Rd index bda264463..67a9133f2 100644 --- a/man/Plumber.Rd +++ b/man/Plumber.Rd @@ -242,7 +242,7 @@ This value does not need to be explicitly assigned. To explicitly set it, see \c \item{\code{swagger}}{Deprecated. Please use \code{docs} instead. See \verb{$setDocs(docs)} or \verb{$setApiSpec()} for more customization.} -\item{\code{debug}}{If \code{TRUE}, it will provide more insight into your API errors. Using this value will only last for the duration of the run. See \verb{$setDebug()}} +\item{\code{debug}}{If \code{TRUE}, it will provide more insight into your API errors. Using this value will only last for the duration of the run. If a \verb{$setDebug()} has not been called, \code{debug} will default to \code{interactive()} at \verb{$run()} time. See \verb{$setDebug()} for more details.} \item{\code{swaggerCallback}}{An optional single-argument function that is called back with the URL to an OpenAPI user interface when one becomes @@ -780,7 +780,7 @@ See also: \code{\link[=pr_set_docs_callback]{pr_set_docs_callback()}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-setDebug}{}}} \subsection{Method \code{setDebug()}}{ -Set debug value to include error messages +Set debug value to include error messages. If never set, the result of \code{interactive()} will be used. See also: \verb{$getDebug()} and \code{\link[=pr_set_debug]{pr_set_debug()}} \subsection{Usage}{ @@ -799,7 +799,7 @@ See also: \verb{$getDebug()} and \code{\link[=pr_set_debug]{pr_set_debug()}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-getDebug}{}}} \subsection{Method \code{getDebug()}}{ -Retrieve the \code{debug} value. +Retrieve the \code{debug} value. If it has never been set, the result of \code{interactive()} will be used. See also: \verb{$getDebug()} and \code{\link[=pr_set_debug]{pr_set_debug()}} \subsection{Usage}{ From a8948f5e531e2ad03c3f3e807774fb70d016a1e6 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 26 Feb 2021 15:47:05 -0500 Subject: [PATCH 10/20] Actually restore options altered in test --- tests/testthat/test-options.R | 37 ++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/tests/testthat/test-options.R b/tests/testthat/test-options.R index e9ca6f00c..8fa6e53af 100644 --- a/tests/testthat/test-options.R +++ b/tests/testthat/test-options.R @@ -52,22 +52,31 @@ test_that("all options used are `options_plumber()` parameters", { test_that("Legacy swagger redirect can be disabled", { - with_options(list(), { - options_plumber(legacyRedirects = TRUE) - redirects <- swagger_redirects() - expect_gt(length(redirects), 0) + with_options( + list( + plumber.legacyRedirets = getOption("plumber.legacyRedirects") + ), { + options_plumber(legacyRedirects = TRUE) + redirects <- swagger_redirects() + expect_gt(length(redirects), 0) - options_plumber(legacyRedirects = FALSE) - redirects <- swagger_redirects() - expect_equal(length(redirects), 0) - }) + options_plumber(legacyRedirects = FALSE) + redirects <- swagger_redirects() + expect_equal(length(redirects), 0) + } + ) }) test_that("docs.callback sync plumber.swagger.url", { - with_options(list(), { - options("plumber.swagger.url" = function(api_url) {cat(api_url)}) - opt <- options_plumber(docs.callback = NULL) - expect_null(getOption("plumber.swagger.url")) - expect_null(opt$plumber.docs.callback) - }) + with_options( + list( + plumber.swagger.url = getOption("plumber.swagger.url"), + plumber.docs.callback = getOption("plumber.docs.callback") + ), { + options("plumber.swagger.url" = function(api_url) {cat(api_url)}) + opt <- options_plumber(docs.callback = NULL) + expect_null(getOption("plumber.swagger.url")) + expect_null(opt$plumber.docs.callback) + } + ) }) From 755b71f1ac288fff5b565a6320ee8cc1361acea5 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 26 Feb 2021 15:52:30 -0500 Subject: [PATCH 11/20] Test swaggerCallback option can be set after init --- tests/testthat/test-plumber-run.R | 57 +++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/tests/testthat/test-plumber-run.R b/tests/testthat/test-plumber-run.R index 995bb05ad..451094384 100644 --- a/tests/testthat/test-plumber-run.R +++ b/tests/testthat/test-plumber-run.R @@ -39,28 +39,55 @@ test_that("`docs` does not not permanetly set pr information", { }) test_that("`swaggerCallback` does not not permanetly set pr information", { - msg <- "test got here!" + counter <- 0 my_func <- function(url) { - message(msg) + counter <<- counter + 1 } root <- pr() %>% pr_set_docs_callback(my_func) + # not used with_interrupt({ - expect_failure( - expect_message({ - root %>% pr_run(swaggerCallback = NULL) - }, msg) - ) + expect_equal(counter, 0) + root %>% pr_run(swaggerCallback = NULL) + expect_equal(counter, 0) }) + # not used with_interrupt({ - expect_failure( - expect_message({ - root$run(swaggerCallback = NULL) - }, msg) - ) + expect_equal(counter, 0) + root$run(swaggerCallback = NULL) + expect_equal(counter, 0) }) + # used with_interrupt({ - expect_message({ - root %>% pr_run(quiet = FALSE) - }, msg) + expect_equal(counter, 0) + root %>% pr_run(quiet = FALSE) + expect_equal(counter, 1) }) }) + +test_that("`swaggerCallback` can be set by option after the pr is created", { + counter <- 0 + my_func <- function(url) { + counter <<- counter + 1 + } + + # must initialize before options are set + root <- pr() + + with_options( + list( + plumber.swagger.url = getOption("plumber.swagger.url"), + plumber.docs.callback = getOption("plumber.docs.callback") + ), + { + # set option after init + options_plumber(docs.callback = my_func) + with_interrupt({ + expect_equal(counter, 0) + pr_run(root) + }) + } + ) + # used + expect_equal(counter, 1) + +}) From d12bd1997cdd3b995e17de01fb001a80f11f5602 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 26 Feb 2021 15:54:27 -0500 Subject: [PATCH 12/20] Test that debug does not ask for interactive() unless it is unset --- tests/testthat/test-plumber-run.R | 58 +++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/testthat/test-plumber-run.R b/tests/testthat/test-plumber-run.R index 451094384..547a14490 100644 --- a/tests/testthat/test-plumber-run.R +++ b/tests/testthat/test-plumber-run.R @@ -91,3 +91,61 @@ test_that("`swaggerCallback` can be set by option after the pr is created", { expect_equal(counter, 1) }) + + +### Test does not work as expected with R6 objects. +test_that("`debug` is not set until runtime", { + skip_on_cran() + + counter <- 0 + + local({ + # Could not get `mockery` or `mockr` packages to work as expected. + # So shiming the function here... + plumber_env <- asNamespace("plumber") + prev_default_debug <- plumber_env$default_debug + on.exit({ + plumber_env$default_debug <- prev_default_debug + }, add = TRUE) + plumber_env$default_debug <- function() { + counter <<- 1 + TRUE + } + + root <- pr() + expect_equal(counter, 0) + + # Use default value + with_interrupt({ + root %>% pr_run(quiet = TRUE) + }) + expect_equal(counter, 1) + + # listen to set value + with_interrupt({ + root %>% + pr_set_debug(TRUE) %>% + pr_run(quiet = TRUE) + }) + # not updated + expect_equal(counter, 1) + + # listen to run value + with_interrupt({ + root %>% + pr_run(debug = FALSE, quiet = TRUE) + }) + # not updated + expect_equal(counter, 1) + + # TODO test that run(debug=) has preference over pr_set_debug() + }) + + + # make sure the function is restored + expect_equal(default_debug(), interactive()) + # counter should not increment + expect_equal(counter, 1) + + +}) From 8a0240ba01951d3d95d7bf0d2f1283e694670d9f Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 1 Mar 2021 10:04:34 -0500 Subject: [PATCH 13/20] Update NEWS.md --- NEWS.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 30f622785..bf986bd31 100644 --- a/NEWS.md +++ b/NEWS.md @@ -23,7 +23,11 @@ plumber 1.0.0.9999 Development version * OpenAPI Specification can be set using a file path. (@meztez #696) -* Added `quiet = TRUE` to `Plumber$run()` to suppress routine startup messages. (@jcheng5 #765) +* Un-deprecated `Plumber$run(debug=, swaggerCallback=)` and added the parameters for `Plumber$run(docs=, quiet=)` and `pr_run(debug=, docs=, swaggerCallback=, quiet=)`. Now, all four parameters will not produce lingering effects on the `Plumber` router. (@jcheng5 #765) + * Setting `quiet = TRUE` will suppress routine startup messages. + * Setting `debug = TRUE`, will display information when an error occurs. See `pr_set_debug()`. + * Setting `docs` will update the visual documentation. See `pr_set_docs()`. + * Setting `swaggerCallback` to a function which will be called with a url to the documentation. See `pr_set_docs_callback()`. * To update a `PlumberEndpoint` path after initialization, call the new `PlumberEndpoint$setPath(path)`. This will update internal path matching meta data. (Active bindings were not used to avoid breaking changes.) (@blairj09 #770) From 8f172bc63d7f4f8511ea144e9afc9bd18faf9049 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 1 Mar 2021 10:58:27 -0500 Subject: [PATCH 14/20] Use mockery package instead of global counters --- DESCRIPTION | 3 +- tests/testthat/test-plumber-run.R | 76 ++++++++++++------------------- 2 files changed, 30 insertions(+), 49 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 7547f6559..317519e26 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -47,7 +47,8 @@ Suggests: yaml, feather, future, - rstudioapi + rstudioapi, + mockery (>= 0.4.2) RoxygenNote: 7.1.1 Collate: 'async.R' diff --git a/tests/testthat/test-plumber-run.R b/tests/testthat/test-plumber-run.R index 547a14490..cc161f3a0 100644 --- a/tests/testthat/test-plumber-run.R +++ b/tests/testthat/test-plumber-run.R @@ -39,36 +39,35 @@ test_that("`docs` does not not permanetly set pr information", { }) test_that("`swaggerCallback` does not not permanetly set pr information", { - counter <- 0 - my_func <- function(url) { - counter <<- counter + 1 - } - root <- pr() %>% pr_set_docs_callback(my_func) + skip_if_not_installed("mockery", "0.4.2") + + m <- mockery::mock(TRUE, cycle = TRUE) + m() # call once so that `length(m)` > 0 as `length(m)` represents the number of calls to `m` + root <- pr() %>% pr_set_docs_callback(m) # not used with_interrupt({ - expect_equal(counter, 0) + mockery::expect_called(m, 1) root %>% pr_run(swaggerCallback = NULL) - expect_equal(counter, 0) + mockery::expect_called(m, 1) }) # not used with_interrupt({ - expect_equal(counter, 0) + mockery::expect_called(m, 1) root$run(swaggerCallback = NULL) - expect_equal(counter, 0) + mockery::expect_called(m, 1) }) # used with_interrupt({ - expect_equal(counter, 0) + mockery::expect_called(m, 1) root %>% pr_run(quiet = FALSE) - expect_equal(counter, 1) + mockery::expect_called(m, 2) }) }) test_that("`swaggerCallback` can be set by option after the pr is created", { - counter <- 0 - my_func <- function(url) { - counter <<- counter + 1 - } + skip_if_not_installed("mockery", "0.4.2") + + m <- mockery::mock(TRUE) # must initialize before options are set root <- pr() @@ -80,46 +79,34 @@ test_that("`swaggerCallback` can be set by option after the pr is created", { ), { # set option after init - options_plumber(docs.callback = my_func) + options_plumber(docs.callback = m) with_interrupt({ - expect_equal(counter, 0) + mockery::expect_called(m, 0) pr_run(root) }) } ) # used - expect_equal(counter, 1) + mockery::expect_called(m, 1) }) ### Test does not work as expected with R6 objects. test_that("`debug` is not set until runtime", { - skip_on_cran() - - counter <- 0 - - local({ - # Could not get `mockery` or `mockr` packages to work as expected. - # So shiming the function here... - plumber_env <- asNamespace("plumber") - prev_default_debug <- plumber_env$default_debug - on.exit({ - plumber_env$default_debug <- prev_default_debug - }, add = TRUE) - plumber_env$default_debug <- function() { - counter <<- 1 - TRUE - } + skip_if_not_installed("mockery", "0.4.2") + m <- mockery::mock(TRUE, cycle = TRUE) + with_mock(default_debug = m, { root <- pr() - expect_equal(counter, 0) + root$getDebug() + mockery::expect_called(m, 1) - # Use default value with_interrupt({ root %>% pr_run(quiet = TRUE) }) - expect_equal(counter, 1) + # increase by 1 + mockery::expect_called(m, 2) # listen to set value with_interrupt({ @@ -127,25 +114,18 @@ test_that("`debug` is not set until runtime", { pr_set_debug(TRUE) %>% pr_run(quiet = TRUE) }) - # not updated - expect_equal(counter, 1) + # not updated. stay at 2 + mockery::expect_called(m, 2) # listen to run value with_interrupt({ root %>% pr_run(debug = FALSE, quiet = TRUE) }) - # not updated - expect_equal(counter, 1) + # not updated. stay at 2 + mockery::expect_called(m, 2) # TODO test that run(debug=) has preference over pr_set_debug() }) - - # make sure the function is restored - expect_equal(default_debug(), interactive()) - # counter should not increment - expect_equal(counter, 1) - - }) From 274b5303922d07cabff31f540b4e42e3f7d87d98 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 1 Mar 2021 11:17:16 -0500 Subject: [PATCH 15/20] Update test-plumber-run.R --- tests/testthat/test-plumber-run.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/testthat/test-plumber-run.R b/tests/testthat/test-plumber-run.R index cc161f3a0..14b74edfc 100644 --- a/tests/testthat/test-plumber-run.R +++ b/tests/testthat/test-plumber-run.R @@ -97,7 +97,9 @@ test_that("`debug` is not set until runtime", { skip_if_not_installed("mockery", "0.4.2") m <- mockery::mock(TRUE, cycle = TRUE) - with_mock(default_debug = m, { + # https://github.com/r-lib/testthat/issues/734#issuecomment-377367516 + # > It should work if you fully qualify the function name (include the pkgname) + with_mock("plumber:::default_debug" = m, { root <- pr() root$getDebug() mockery::expect_called(m, 1) From ae6c7d82d2190346eadd00202792485d5b09e312 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 1 Mar 2021 11:31:45 -0500 Subject: [PATCH 16/20] clean up --- R/plumber.R | 8 ++++---- R/pr.R | 3 +++ man/Plumber.Rd | 2 +- man/pr_run.Rd | 3 +++ tests/testthat/test-plumber-run.R | 15 +++++++++------ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/R/plumber.R b/R/plumber.R index 80684d2af..fe9aab86c 100644 --- a/R/plumber.R +++ b/R/plumber.R @@ -227,15 +227,15 @@ Plumber <- R6Class( # The router could be made in an interactive setting and used in background process. # Do not determine if interactive until run time prev_debug <- private$debug + on.exit({ + private$debug <- prev_debug + }, add = TRUE) # Fix the debug value while running. self$setDebug( # Order: Run method param, internally set value, is interactive() # `$getDebug()` is dynamic given `setDebug()` has never been called. rlang::maybe_missing(debug, self$getDebug()) ) - on.exit({ - private$debug <- prev_debug - }, add = TRUE) docs_info <- if (!rlang::is_missing(docs)) { @@ -959,7 +959,7 @@ Plumber <- R6Class( } private$docs_callback <- callback }, - #' @description Set debug value to include error messages. If never set, the result of `interactive()` will be used. + #' @description Set debug value to include error messages. #' #' See also: `$getDebug()` and [pr_set_debug()] #' @param debug `TRUE` provides more insight into your API errors. diff --git a/R/pr.R b/R/pr.R index c388a9235..4e7c2e6d1 100644 --- a/R/pr.R +++ b/R/pr.R @@ -496,8 +496,11 @@ pr_filter <- function(pr, #' #' pr() %>% #' pr_run( +#' # manually set port #' port = 5762, +#' # turn of visual documentation #' docs = FALSE, +#' # do not display startup messages #' quiet = TRUE #' ) #' } diff --git a/man/Plumber.Rd b/man/Plumber.Rd index 67a9133f2..f1a1bf705 100644 --- a/man/Plumber.Rd +++ b/man/Plumber.Rd @@ -780,7 +780,7 @@ See also: \code{\link[=pr_set_docs_callback]{pr_set_docs_callback()}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-setDebug}{}}} \subsection{Method \code{setDebug()}}{ -Set debug value to include error messages. If never set, the result of \code{interactive()} will be used. +Set debug value to include error messages. See also: \verb{$getDebug()} and \code{\link[=pr_set_debug]{pr_set_debug()}} \subsection{Usage}{ diff --git a/man/pr_run.Rd b/man/pr_run.Rd index 3fcf24c82..25eac06f6 100644 --- a/man/pr_run.Rd +++ b/man/pr_run.Rd @@ -54,8 +54,11 @@ pr() \%>\% pr() \%>\% pr_run( + # manually set port port = 5762, + # turn of visual documentation docs = FALSE, + # do not display startup messages quiet = TRUE ) } diff --git a/tests/testthat/test-plumber-run.R b/tests/testthat/test-plumber-run.R index 14b74edfc..df98e3bbc 100644 --- a/tests/testthat/test-plumber-run.R +++ b/tests/testthat/test-plumber-run.R @@ -17,6 +17,7 @@ test_that("quiet=TRUE suppresses startup messages", { test_that("`docs` does not not permanetly set pr information", { doc_name <- "swagger" root <- pr() %>% pr_set_docs(doc_name) + # no docs. do not find printed message with_interrupt({ expect_failure( expect_message({ @@ -24,6 +25,7 @@ test_that("`docs` does not not permanetly set pr information", { }, doc_name) ) }) + # no docs. do not find printed message with_interrupt({ expect_failure( expect_message({ @@ -31,6 +33,7 @@ test_that("`docs` does not not permanetly set pr information", { }, doc_name) ) }) + # docs enabled by default. Find printed message with_interrupt({ expect_message({ root %>% pr_run(quiet = FALSE) @@ -44,19 +47,19 @@ test_that("`swaggerCallback` does not not permanetly set pr information", { m <- mockery::mock(TRUE, cycle = TRUE) m() # call once so that `length(m)` > 0 as `length(m)` represents the number of calls to `m` root <- pr() %>% pr_set_docs_callback(m) - # not used + # m not used with_interrupt({ mockery::expect_called(m, 1) root %>% pr_run(swaggerCallback = NULL) mockery::expect_called(m, 1) }) - # not used + # m not used with_interrupt({ mockery::expect_called(m, 1) root$run(swaggerCallback = NULL) mockery::expect_called(m, 1) }) - # used + # m is used with_interrupt({ mockery::expect_called(m, 1) root %>% pr_run(quiet = FALSE) @@ -69,7 +72,7 @@ test_that("`swaggerCallback` can be set by option after the pr is created", { m <- mockery::mock(TRUE) - # must initialize before options are set + # must initialize before options are set for this test root <- pr() with_options( @@ -86,7 +89,7 @@ test_that("`swaggerCallback` can be set by option after the pr is created", { }) } ) - # used + # m is used mockery::expect_called(m, 1) }) @@ -99,7 +102,7 @@ test_that("`debug` is not set until runtime", { m <- mockery::mock(TRUE, cycle = TRUE) # https://github.com/r-lib/testthat/issues/734#issuecomment-377367516 # > It should work if you fully qualify the function name (include the pkgname) - with_mock("plumber:::default_debug" = m, { + mockr::with_mock("plumber:::default_debug" = m, { root <- pr() root$getDebug() mockery::expect_called(m, 1) From 077aa53f4070dd7d5ae08dbf9dc8d0204a1d7848 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 1 Mar 2021 12:05:41 -0500 Subject: [PATCH 17/20] Use `testthat`, not `mockr` --- tests/testthat/test-plumber-run.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-plumber-run.R b/tests/testthat/test-plumber-run.R index df98e3bbc..ffdf829bf 100644 --- a/tests/testthat/test-plumber-run.R +++ b/tests/testthat/test-plumber-run.R @@ -102,7 +102,7 @@ test_that("`debug` is not set until runtime", { m <- mockery::mock(TRUE, cycle = TRUE) # https://github.com/r-lib/testthat/issues/734#issuecomment-377367516 # > It should work if you fully qualify the function name (include the pkgname) - mockr::with_mock("plumber:::default_debug" = m, { + with_mock("plumber:::default_debug" = m, { root <- pr() root$getDebug() mockery::expect_called(m, 1) From e32103f7421d8c5d0310b9dab9c16da34a6564c1 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Mon, 1 Mar 2021 10:37:02 -0800 Subject: [PATCH 18/20] Update NEWS.md --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index bf986bd31..cdb077463 100644 --- a/NEWS.md +++ b/NEWS.md @@ -27,7 +27,7 @@ plumber 1.0.0.9999 Development version * Setting `quiet = TRUE` will suppress routine startup messages. * Setting `debug = TRUE`, will display information when an error occurs. See `pr_set_debug()`. * Setting `docs` will update the visual documentation. See `pr_set_docs()`. - * Setting `swaggerCallback` to a function which will be called with a url to the documentation. See `pr_set_docs_callback()`. + * Set `swaggerCallback` to a function which will be called with a url to the documentation, or `NULL` to do nothing. See `pr_set_docs_callback()`. * To update a `PlumberEndpoint` path after initialization, call the new `PlumberEndpoint$setPath(path)`. This will update internal path matching meta data. (Active bindings were not used to avoid breaking changes.) (@blairj09 #770) From a0b0825ce7a91a3f7636f9bd340bc1fb9893d3d0 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Mon, 1 Mar 2021 10:58:29 -0800 Subject: [PATCH 19/20] Spelling --- R/pr.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/pr.R b/R/pr.R index 4e7c2e6d1..6843bfb8a 100644 --- a/R/pr.R +++ b/R/pr.R @@ -498,7 +498,7 @@ pr_filter <- function(pr, #' pr_run( #' # manually set port #' port = 5762, -#' # turn of visual documentation +#' # turn off visual documentation #' docs = FALSE, #' # do not display startup messages #' quiet = TRUE From e90deca3ffe9fff42058686dcb84d7c9ae982140 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 1 Mar 2021 16:43:47 -0500 Subject: [PATCH 20/20] document --- man/pr_run.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/pr_run.Rd b/man/pr_run.Rd index 25eac06f6..e674b7ed4 100644 --- a/man/pr_run.Rd +++ b/man/pr_run.Rd @@ -56,7 +56,7 @@ pr() \%>\% pr_run( # manually set port port = 5762, - # turn of visual documentation + # turn off visual documentation docs = FALSE, # do not display startup messages quiet = TRUE