diff --git a/DESCRIPTION b/DESCRIPTION index 40f0b42d7..c87d132a5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -40,7 +40,8 @@ Suggests: htmlwidgets, visNetwork, analogsea (>= 0.7.0), - later + later, + readr Remotes: rstudio/swagger Collate: @@ -68,6 +69,7 @@ Collate: 'post-parsers.R' 'response.R' 'serializer-content-type.R' + 'serializer-csv.R' 'serializer-html.R' 'serializer-htmlwidget.R' 'serializer-rds.R' diff --git a/NAMESPACE b/NAMESPACE index f88bbfbcd..a3c24d47f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -26,6 +26,7 @@ export(plumb) export(plumber) export(randomCookieKey) export(serializer_content_type) +export(serializer_csv) export(serializer_html) export(serializer_htmlwidget) export(serializer_json) diff --git a/NEWS.md b/NEWS.md index 02d607cfd..f96dce42f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,11 @@ plumber (version) -------------------------------------------------------------------------------- + +### New features + +* CSV (UTF-8) serialization (@pachamaltese, #520) +* SVG (UTF-8) serialization (@pachamaltese, #398) + ### Bug fixes * Fix `plumb()` function when `plumb()`ing a directory so that `plumber.R` is diff --git a/R/serializer-csv.R b/R/serializer-csv.R new file mode 100644 index 000000000..d9cb0eb1e --- /dev/null +++ b/R/serializer-csv.R @@ -0,0 +1,20 @@ +#' @rdname serializers +#' @export +serializer_csv <- function(...) { + if (!requireNamespace("readr", quietly = TRUE)) { + stop("`readr` must be installed for `serializer_csv` to work") + } + + function(val, req, res, errorHandler) { + tryCatch({ + res$setHeader("Content-Type", "text/plain; charset=UTF-8") + res$body <- readr::format_csv(val, ...) + return(res$toResponse()) + }, error = function(e){ + errorHandler(req, res, e) + }) + } +} + +#' @include globals.R +.globals$serializers[["csv"]] <- serializer_csv diff --git a/man/plumber.Rd b/man/plumber.Rd index ded441ed1..bb6a4e98e 100644 --- a/man/plumber.Rd +++ b/man/plumber.Rd @@ -5,7 +5,7 @@ \alias{plumber} \title{Plumber Router} \usage{ -plumb(file, dir = ".") +plumb(file = NULL, dir = ".") } \arguments{ \item{file}{path to file to plumb} diff --git a/man/serializers.Rd b/man/serializers.Rd index 9ef89c884..bdb3f4e20 100644 --- a/man/serializers.Rd +++ b/man/serializers.Rd @@ -1,11 +1,12 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/serializer-json.R, R/serializer-content-type.R, -% R/serializer-html.R, R/serializer-htmlwidget.R, R/serializer-rds.R, -% R/serializer.R +% R/serializer-csv.R, R/serializer-html.R, R/serializer-htmlwidget.R, +% R/serializer-rds.R, R/serializer.R \name{serializer_json} \alias{serializer_json} \alias{serializer_unboxed_json} \alias{serializer_content_type} +\alias{serializer_csv} \alias{serializer_html} \alias{serializer_htmlwidget} \alias{serializer_rds} @@ -18,6 +19,8 @@ serializer_unboxed_json(auto_unbox = TRUE, ...) serializer_content_type(type) +serializer_csv(...) + serializer_html() serializer_htmlwidget(...) diff --git a/tests/testthat/test-serializer-csv.R b/tests/testthat/test-serializer-csv.R new file mode 100644 index 000000000..43842bdef --- /dev/null +++ b/tests/testthat/test-serializer-csv.R @@ -0,0 +1,36 @@ +context("CSV serializer") + +test_that("CSV serializes properly", { + skip_if_not_installed("readr") + + d <- data.frame(a=1, b=2, c="hi") + val <- serializer_csv()(d, data.frame(), PlumberResponse$new(), stop) + expect_equal(val$status, 200L) + expect_equal(val$headers$`Content-Type`, "text/plain; charset=UTF-8") + expect_equal(val$body, readr::format_csv(d)) + + d <- data.frame(a=1, b=2, c="hi", na=NA) + val <- serializer_csv()(d, data.frame(), PlumberResponse$new(), stop) + expect_equal(val$status, 200L) + expect_equal(val$headers$`Content-Type`, "text/plain; charset=UTF-8") + expect_equal(val$body, readr::format_csv(d, na = "NA")) + + d <- data.frame(a=1, b=2, c="hi", na=NA) + val <- serializer_csv(na = 'string')(d, data.frame(), PlumberResponse$new(), stop) + expect_equal(val$status, 200L) + expect_equal(val$headers$`Content-Type`, "text/plain; charset=UTF-8") + expect_equal(val$body, readr::format_csv(d, na = 'string')) +}) + +test_that("Errors call error handler", { + skip_if_not_installed("readr") + + errors <- 0 + errHandler <- function(req, res, err){ + errors <<- errors + 1 + } + + expect_equal(errors, 0) + serializer_csv()(parse(text="hi"), data.frame(), PlumberResponse$new("csv"), err = errHandler) + expect_equal(errors, 1) +})