Skip to content

Commit

Permalink
Merge branch 'main' into issue836-docs-path
Browse files Browse the repository at this point in the history
  • Loading branch information
schloerke authored Feb 5, 2025
2 parents 5980d1e + e1a30b0 commit 07c9455
Show file tree
Hide file tree
Showing 19 changed files with 225 additions and 80 deletions.
5 changes: 4 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ Suggests:
rapidoc,
sf,
ragg,
svglite
svglite,
readxl,
writexl,
utils
RoxygenNote: 7.3.2
Collate:
'async.R'
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export(include_rmd)
export(is_plumber)
export(options_plumber)
export(parser_csv)
export(parser_excel)
export(parser_feather)
export(parser_form)
export(parser_geojson)
Expand Down Expand Up @@ -80,6 +81,7 @@ export(serializer_cat)
export(serializer_content_type)
export(serializer_csv)
export(serializer_device)
export(serializer_excel)
export(serializer_feather)
export(serializer_format)
export(serializer_geojson)
Expand Down
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@
* Fixes #956, allowing a port to be specified as an environment variable. User-provided ports must be between 1024 and 49151 (following [IANA guidelines](https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml)) and may not be a known unsafe port. plumber will now throw an error if an invalid port is requested. (@shikokuchuo @gadenbuie #963)

* Added support for graphic devices provided by ragg and svglite (@thomasp85 #964)

* `parse_rds()`, `parse_feather()`, and `parse_parquet()` no longer writes data to disk during parsing (@thomasp85, #942)

* Fixed a bug where setting the `apiPath` option wouldn't be honored when running the app (@thomasp85, #836)

* Returning error messages are now turned off by default rather than being turned on if running interactively and turned off if not (@thomasp85, #962)

* New serializers
* `serializer_excel()`: Return an object serialized by `writexl::write_xlsx` (@r2evans, #973).

* New request body parsers
* `parser_excel()`: Parse request body as an excel workbook using `readxl::read_excel` (@r2evans, #973). This defaults to loading in the first worksheet only, you can use `@parse excel list(sheet=NA)` to import all worksheets. This always returns a list of frames, even if just one worksheet.

# plumber 1.2.2

* Allow to set plumber options using environment variables `?options_plumber`. (@meztez #934)
Expand Down
36 changes: 36 additions & 0 deletions R/parse-body.R
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,41 @@ parser_parquet <- function(...) {
}
}

# readxl's default behavior is to read only one worksheet at a time; in order for an endpoint to
# read multiple worksheets, its documentation suggests to iterate over discovered names (c.f.,
# https://readxl.tidyverse.org/articles/readxl-workflows.html#iterate-over-multiple-worksheets-in-a-workbook);
# for this reason, this parser detects an NA in the 'sheet=' argument and replaces it with all
# worksheet names found in the workbook

#' @describeIn parsers excel parser. See [readxl::read_excel()] for more details. (Defaults to reading in the first worksheet only, use `@parser excel list(sheet=NA)` to read in all worksheets.)
#' @param sheet Sheet to read. Either a string (the name of a sheet), or an
#' integer (the position of the sheet). Defaults to the first sheet. To read all
#' sheets, use `NA`.
#' @export
parser_excel <- function(..., sheet = NULL) {
if (!requireNamespace("readxl", quietly = TRUE)) {
stop("`readxl` must be installed for `parser_excel` to work")
}
parse_fn <- parser_read_file(function(tmpfile) {
if (is.null(sheet)) {
# we have to hard-code this since lapply won't iterate if NULL
sheet <- 1L
} else if (anyNA(sheet)) {
sheet <- readxl::excel_sheets(tmpfile)
}
if (is.character(sheet)) names(sheet) <- sheet
out <- suppressWarnings(
lapply(sheet, function(sht) {
readxl::read_excel(path = tmpfile, sheet = sht, ...)
})
)
out
})
function(value, ...) {
parse_fn(value)
}
}

#' @describeIn parsers Octet stream parser. Returns the raw content.
#' @export
parser_octet <- function() {
Expand Down Expand Up @@ -588,6 +623,7 @@ register_parsers_onLoad <- function() {
register_parser("rds", parser_rds, fixed = "application/rds")
register_parser("feather", parser_feather, fixed = c("application/vnd.apache.arrow.file", "application/feather"))
register_parser("parquet", parser_parquet, fixed = "application/vnd.apache.parquet")
register_parser("excel", parser_excel, fixed = c("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.ms-excel"))
register_parser("text", parser_text, fixed = "text/plain", regex = "^text/")
register_parser("tsv", parser_tsv, fixed = c("application/tab-separated-values", "text/tab-separated-values"))
# yaml types: https://stackoverflow.com/a/38000954/591574
Expand Down
15 changes: 14 additions & 1 deletion R/plumber-step.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,21 @@ forward_class <- "plumber_forward"
#' to pass control off to the next handler in the chain. If this is not called
#' by a filter, the assumption is that the filter fully handled the request
#' itself and no other filters or endpoints should be evaluated for this
#' request.
#' request. `forward()` cannot be used within handlers to trigger the next
#' matching handler in the router. It only has relevance for filters.
#'
#' @export
#'
#' @examples
#' \dontrun{
#' pr() %>%
#' pr_filter("foo", function(req, res) {
#' print("This is filter foo")
#' forward()
#' }) %>%
#' pr_run()
#' }
#'
forward <- function() {
exec <- getCurrentExec()
exec$forward <- TRUE
Expand Down
34 changes: 17 additions & 17 deletions R/plumber.R
Original file line number Diff line number Diff line change
Expand Up @@ -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. If a `$setDebug()` has not been called, `debug` will default to `interactive()` at `$run()` time. See `$setDebug()` for more details.
#' @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 `FALSE` 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
Expand Down Expand Up @@ -223,17 +223,16 @@ Plumber <- R6Class(
)
)

# 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
# Temporarily set the debug value
prev_debug <- private$debug
on.exit({
private$debug <- prev_debug
}, add = TRUE)
# Fix the debug value while running.
# Determine if the user should be informed about default behavior
inform_debug <- is.null(prev_debug) && rlang::is_missing(debug)

# Set debug value, defaulting to already set value (which returns FALSE if not set)
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())
)

Expand Down Expand Up @@ -266,6 +265,14 @@ Plumber <- R6Class(
#on.exit(unmount_docs(self, docs_info), add = TRUE)
}

if (!isTRUE(quiet) && inform_debug && rlang::is_interactive()) {
rlang::inform(
"Error reporting has been turned off by default. See `pr_set_debug()` for more details.\nTo disable this message, set a debug value.",
.frequency="once",
.frequency_id="pr_set_debug_message"
)
}

on.exit(private$runHooks("exit"), add = TRUE)

httpuv::runServer(host, port, self)
Expand Down Expand Up @@ -962,15 +969,15 @@ Plumber <- R6Class(
#'
#' See also: `$getDebug()` and [pr_set_debug()]
#' @param debug `TRUE` provides more insight into your API errors.
setDebug = function(debug = interactive()) {
setDebug = function(debug = FALSE) {
stopifnot(length(debug) == 1)
private$debug <- isTRUE(debug)
},
#' @description Retrieve the `debug` value. If it has never been set, the result of `interactive()` will be used.
#' @description Retrieve the `debug` value. If it has never been set, it will return `FALSE`.
#'
#' See also: `$getDebug()` and [pr_set_debug()]
getDebug = function() {
private$debug %||% default_debug()
private$debug %||% FALSE
},
#' @description Add a filter to plumber router
#'
Expand Down Expand Up @@ -1332,13 +1339,6 @@ upgrade_docs_parameter <- function(docs, ...) {
)
}



default_debug <- function() {
interactive()
}


urlHost <- function(scheme = "http", host, port, path = "", changeHostLocation = FALSE) {
if (isTRUE(changeHostLocation)) {
# upgrade callback location to be localhost and not catch-all addresses
Expand Down
14 changes: 11 additions & 3 deletions R/pr_set.R
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ pr_set_error <- function(pr, fun) {

#' Set debug value to include error messages of routes cause an error
#'
#' To hide any error messages in production, set the debug value to `FALSE`.
#' The `debug` value is enabled by default for [interactive()] sessions.
#' By default, error messages from your plumber routes are hidden, but can be
#' turned on by setting the debug value to `TRUE` using this setter.
#'
#' @template param_pr
#' @param debug `TRUE` provides more insight into your API errors.
Expand All @@ -118,7 +118,15 @@ pr_set_error <- function(pr, fun) {
#' pr_get("/boom", function() stop("boom")) %>%
#' pr_run()
#' }
pr_set_debug <- function(pr, debug = interactive()) {
#'
#' # Setting within a plumber file
#' #* @plumber
#' function(pr) {
#' pr %>%
#' pr_set_debug(TRUE)
#' }
#'
pr_set_debug <- function(pr, debug = FALSE) {
validate_pr(pr)
pr$setDebug(debug = debug)
pr
Expand Down
17 changes: 17 additions & 0 deletions R/serializer.R
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,22 @@ serializer_parquet <- function(type = "application/vnd.apache.parquet") {
)
}

#' @describeIn serializers excel serializer. See also: [writexl::write_xlsx()]
#' @export
serializer_excel <- function(..., type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") {
if (!requireNamespace("writexl", quietly = TRUE)) {
stop("The writexl package is not available but is required in order to use the writexl serializer",
call. = FALSE)
}

serializer_write_file(
fileext = ".xlsx",
type = type,
write_fn = function(val, tmpfile) {
writexl::write_xlsx(x = val, path = tmpfile, ...)
}
)
}

#' @describeIn serializers YAML serializer. See also: [yaml::as.yaml()]
#' @export
Expand Down Expand Up @@ -693,6 +709,7 @@ add_serializers_onLoad <- function() {
register_serializer("tsv", serializer_tsv)
register_serializer("feather", serializer_feather)
register_serializer("parquet", serializer_parquet)
register_serializer("excel", serializer_excel)
register_serializer("yaml", serializer_yaml)
register_serializer("geojson", serializer_geojson)

Expand Down
13 changes: 7 additions & 6 deletions inst/WORDLIST
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ HSTS
HTTPOnly
HTTPS
Hookable
IANA
IPs
IPv
JSON
Expand All @@ -35,6 +36,7 @@ Phusion
PlumberEndpoint's
Quickstart
README
RStudio
RedHat
RedShift
Rscript
Expand All @@ -47,6 +49,7 @@ TLS
TSV
UI
URI
Undeprecated
Unencrypted
Unmount
WebSocket
Expand Down Expand Up @@ -82,8 +85,9 @@ haproxy
htmlwidget
http
httpuv
httr2
httr
imposter
ing
integrations
interpretable
javascript
Expand Down Expand Up @@ -114,6 +118,7 @@ proc
programmatically
pseudorandom
pwd
ragg
randomPort
renderers
repo
Expand All @@ -130,6 +135,7 @@ setosa
ssl
subfolder
subrouters
svglite
systemd
transactional
unmount
Expand All @@ -139,8 +145,3 @@ untrusted
validator
webhook
webhook’s

ing
Undeprecated
httr
RStudio
6 changes: 3 additions & 3 deletions man/Plumber.Rd

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

14 changes: 13 additions & 1 deletion man/forward.Rd

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

Loading

0 comments on commit 07c9455

Please sign in to comment.