The port many now be specified as an environment variable. User-provided ports must be between 1024 and 49151 (following IANA guidelines) 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).
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 there is just one
worksheet.Mounts now have a dynamic req$PATH_INFO instead of a
pre-computed value. (#888)
validate_api_spec() now uses
@redocly/cli to validate the API spec. (#986)
Added operationId to each operation within the
auto-generated OpenAPI output. The value is similar to the
PATH-VERB, e.g. /users/create-POST.
(#986)
?options_plumber. (@meztez #934)parseUTF8 return value attribute
srcfile on Windows. (#930)Static file handler now serves HEAD requests. (#798)
Introduces new GeoJSON serializer and parser. GeoJSON objects are
parsed into sf objects and sf or
sfc objects will be serialized into GeoJSON. (@josiahparry,
#830)
Add new Octet-Stream serializer. This is a wrapper around the
Content Type serializer with type application/octet-stream.
(#864)
Update feather serializer to use the arrow package. The new
default feather MIME type is
application/vnd.apache.arrow.file. (@pachadotdev #849)
Add parquet serializer and parser by using the arrow package (@pachadotdev #849)
Updated example 14-future to use
promises::future_promise() and added an endpoint that uses
{coro} to write simpler async /
{promises} code (#785)
Add path argument to pr_cookie()
allowing Secure cookies to define where they are served (@jtlandis #850)
OpenAPI specification collision when using examples.
(@meztez
#820)
Static handler returns Last-Modified response header. (#798)
OpenAPI response type detection had a scoping issue. Use
serializer defined Content-Type header instead. (@meztez, #789)
The default shared secret filter returns error responses without throwing an error. (#808)
Remove response bodies (and therefore the Content-Length header) for status codes which forbid it under the HTTP specification (e.g. 1xx, 204, 304). (@atheriel #758, @meztez #760)
Decode path URI before attempting to serve static assets (@meztez #754).
Force json serialization of endpoint error responses instead of using endpoint serializer. (@meztez, #689)
When plumbing a Plumber file and using a Plumber router modifier
(#* @plumber), an error will be thrown if the original
router is not returned. (#738)
options_plumber() now requires that all options are
named. If no option name is provided, an error with be thrown.
(#746)
Added option plumber.trailingSlash. This option
(which is disabled by default) allows routes to be
redirected to route definitions with a trailing slash. For example, if a
GET request is submitted to /test?a=1 with no
/test route is defined, but a GET
/test/ route definition does exist, then the original
request will respond with a 307 to reattempt against
GET /test/?a=1. This option will be
enabled by default in a future release. This logic executed for
before calling the 404 handler. (#746)
Added an experimental option
plumber.methodNotAllowed. This option (which is enabled by
default) allows for a status of 405 to be returned if an
invalid method is used when requesting a valid route. This logic
executed for before calling the default 404 handler.
(#746)
Passing edit = TRUE to plumb_api() will
open the API source file. (#699)
OpenAPI Specification can be set using a file path. (@meztez #696)
Guess OpenAPI response content type from serializer. (@meztez #684)
Undeprecated 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)
quiet = TRUE will suppress routine startup
messages.debug = TRUE, will display information when an
error occurs. See pr_set_debug().docs will update the visual documentation. See
pr_set_docs().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)
PlumberStep (and PlumberEndpoint and
PlumberFilter) received a new field $srcref
and method $getFunc(). $srcref will contain
the corresponding srcref information from original source
file. $getFunc() will return the evaluated function.
(#782)
Allow for spaces in @apiTag and @tag
when tag is surrounded by single or double quotes. (#685)
Ignore regular comments in block parsing. (@meztez #718)
Block parsing comments, tags and responses ordering match plumber api ordering. (#722)
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)
Fixed bug where all pr_*() returned invisibly. Now
all pr_*() methods will print the router if displayed in
the console. (#740)
When calling Plumber$handle() and defining a new
PlumberEndpoint, ... will be checked for
invalid names. (@meztez, #677)
/__swagger__/ now always redirects to
/__docs__/, even when Swagger isn’t the selected interface.
Use options(plumber.legacyRedirects = FALSE) to disable
this behavior. (@blairj09 #694)
Fixed available_apis() bug where all packages
printed all available APIs. (@meztez #708)
Fixed Plumber $routes resolution bugs. Routes are
now returned in lexicographical order. (@meztez #702)
Plumber will now display a circular reference if one is found while printing. (#738)
Changed future::plan() from
multiprocess to multisession in example API
14-future as “Strategy ‘multiprocess’ is deprecated in
future (>= 1.20.0)”. (#747)
Setting options plumber.docs.callback to
NULL will also set deprecated but supported option
plumber.swagger.url. (#766)
Added support for promises in endpoints, filters, and hooks. This
allows for multi-core execution when paired with future.
See plumb_api("plumber", "13-promises") and
plumb_api("plumber", "14-future") for example
implementations. (#248)
Added a Tidy API for more natural usage with magrittr’s
%>%. For example, a plumber object can now be initiated
and run with pr() %>% pr_run(port = 8080). For more
examples, see here
(@blairj09,
#590)
Added support for #' @plumber tag to gain
programmatic access to the plumber router via
function(pr) {....}. See
system.file("plumber/06-sessions/plumber.R", package = "plumber")
and how it adds cookie support from within plumber.R.
(@meztez and @blairj09, #568)
Added plumb_api() for standardizing where to locate
(inst/plumber) and how to run
(plumb_api(package, name)) plumber apis inside an R
package. To view the available Plumber APIs, call
available_apis(). (#631)
Improved argument handling in Plumber Endpoint route definitions.
See
system.file("plumber/17-arguments/plumber.R", package = "plumber")
to view an example with expected output and
plumb_api("plumber", "17-arguments") to retrieve the api.
Improvements include:
req and res
arguments in a route definition are now always Plumber request
and response objects. In the past, this was not guaranteed. (#666,
#637)req$argsQuery, req$argsPath, and
req$argsBody have been added to access query, path, and
req$body parameters, respectively. For this reason, we
suggest defining routes with only req and res
(i.e., function(req, res) {}) and accessing argument(s)
under these new fields to avoid naming conflicts. (#637)req$argsQuery is 1st priority, then
req$argsPath, then req$argsBody). (#666)req$args by filters
or creating req$argsBody will no longer throw an error.
They will only be passed through via ... (#666)API Documentation is now hosted at /__docs__. If
swagger documentation is being used,
/__swagger__ will redirect to /__docs__.
(#654)
Added OpenAPI support for array parameters using syntax
name:[type] and new type list (synonym df,
data.frame). (@meztez,
#532)
Added user provided OpenAPI Specification handler to Plumber
router. Use $setApiSpec() to provide a function to alter
the Plumber generated OpenAPI Specification returned by Plumber router
method $getApiSpec(). This also affects
/openapi.json and /openapi.yaml (#365)(@meztez, #562)
Added validate_api_spec() to validate a Plumber API
produces a valid OpenAPI Specification. (Experimental!) (#633)
Added as_attachment(value, filename) method which
allows routes to return a file attachment with a custom name.
(#585)
Serializer functions can now return PlumberEndpoint
preexec and postexec hooks in addition to a
serializer function by using
endpoint_serializer(). This allows for image serializers to
turn on their corresponding graphics device before the route executes
and turn the graphics device off after the route executes.
(#630)
PNG, JPEG, and SVG image serializers have been exported in
methods serializer_png(), serializer_jpeg(),
and serializer_svg() respectively. In addition to these
methods, serializer_tiff(), serializer_bmp(),
and serializer_pdf() have been added. Each graphics device
serializer wraps around serializer_device(), which should
be used when making more graphics device serializers. (#630)
New serializers
serializer_yaml(): Return an object serialized by
yaml (@meztez, #556)serializer_csv(): Return a comma separated value (@pachamaltese,
#520)serializer_tsv(): Return a tab separated value
(#630)serializer_feather(): Return a object serialized by
feather (#626)serializer_text(): Return text content (#585)serializer_cat(): Return text content after calling
cat() (#585)serializer_print(): Return text content after calling
print() (#585)serializer_format(): Return text content after calling
format() (#585)serializer_svg(): Return an image saved as an SVG
(@pachamaltese,
#398)serializer_headers(header_list): Method which sets a
list of static headers for each serialized value. Heavily inspired from
@ycphs (#455).
(#585)serializer_write_file(): Method which wraps
serializer_content_type(), but orchestrates creating,
writing serialized content to, reading from, and removing a temp file.
(#660)Added support for request body parsing (@meztez, #532)
New request body parsers
parser_csv(): Parse request body as a commas separated
value (#584)parser_json(): Parse request body as JSON (@meztez, #532)parser_multi(): Parse multi part request bodies (@meztez, #532) and
(#663)parser_octet(): Parse request body octet stream (@meztez, #532)parser_form(): Parse request body as form input (@meztez, #532)parser_rds(): Parse request body as RDS file input
(@meztez, #532)parser_text(): Parse request body plain text (@meztez, #532)parser_tsv(): Parse request body a tab separated value
(#584)parser_yaml(): Parse request body as yaml
(#584)parser_none(): Do not parse the request body
(#584)parser_yaml(): Parse request body (@meztez, #556)parser_feather(): Parse request body using
feather (#626)"all" to allow for using all
parsers. (Not recommended in production!) (#584)The parsed request body values is stored at
req$body. (#663)
If multipart/* content is parsed,
req$body will contain named output from
webutils::parse_multipart() and add the parsed value to
each part. Look here for access to all provided information (e.g.,
name, filename, content_type,
etc). In addition, req$argsBody (which is used for route
argument matching) will contain a named reduced form of this information
where parsed values (and filenames) are
combined on the same name. (#663)
Generalize user interface integration. Plumber can now use other
OpenAPI compatible user interfaces like RapiDoc
(https://github.com/mrin9/RapiDoc) and Redoc
(https://github.com/Redocly/redoc). Pending CRAN approbations,
development R packages are available from
https://github.com/meztez/rapidoc/ and https://github.com/meztez/redoc/.
(@meztez,
#562)
Changed Swagger UI to use swagger R package to display the swagger page. (#365)
Added support for swagger for mounted routers (@bradleyhd, #274).
Secret session cookies are now encrypted using
sodium. All prior req$session information will
be lost. Please see ?session_cookie for more information.
(#404)
Session cookies set the HttpOnly flag by default to
mitigate cross-site scripting (XSS). Please see
?session_cookie for more information. (#404)
Wrap jsonlite::fromJSON to ensure that
jsonlite never reads input as a remote address (such as a
file path or URL) and attempts to parse that. The only known way to
exploit this behavior in plumber unless an API were using encrypted
cookies and an attacker knew the encryption key in order to craft
arbitrary cookies. (#325)
When plumb()ing a file (or
Plumber$new(file)), the working directory is set to the
file’s directory before parsing the file. When running the Plumber API,
the working directory will be set to file’s directory before
running.(#631)
Plumber’s OpenAPI Specification is now defined using OpenAPI 3, upgrading from Swagger Specification. (#365)
Plumber router $run() method arguments
swagger, swaggerCallback and
debug are now deprecated. User interface and url callback
are now enabled by default and managed through Plumber router
$setDocs(), $setDocsCallback(), and
$setDebug() methods and options plumber.docs
and plumber.docs.callback. (@meztez, #562)
plumb() now returns an object of class
"Plumber" (previously "plumber"). To check if
an object is a Plumber router, use new method is_plumber().
(#653)
PlumberStatic objects now have a class of
"PlumberStatic" (previously "plumberstatic").
(#653)
The source files used in plumber must use the UTF-8 encoding if they contain non-ASCII characters (@shrektan, #312, #328).
options(plumber.debug) is not set anymore when
running the plumber application. Instead retrieve the debug value using
$getDebug() on the Plumber router directly. Ex:
function(req, res) { req$pr$getDebug() }. (#639)
PlumberEndpoint’s method $exec() now
has a shape of $exec(req, res) (vs
$exec(...)). This allows for fine tune control over the
arguments being sent to the endpoint function.
When creating a PlumberFilter or
PlumberEndpoint, an error will be thrown if
expr does not evaluate to a function. (#666)
Shorthand serializers are now deprecated. @html,
@json, @png, @jpeg,
@svg should be replaced with the @serializer
syntax. Ex: @serializer html or
@serializer jpeg (#630)
plumber R6 object has been deprecated and renamed to
Plumber. PlumberStatic’s
inherited class has been updated to Plumber.
(#653)
hookable R6 object has been deprecated and renamed
to Hookable. Plumber and
PlumberStep’s inherited class has been updated
to Hookable. (#653)
addSerializer() has been deprecated in favor of
register_serializer() (#584)
getCharacterSet() has been deprecated in favor of
get_character_set() (#651)
randomCookieKey() has been deprecated in favor of
random_cookie_key() (#651)
sessionCookie() has been deprecated in favor of
session_cookie() (#651)
DigitalOcean helper functions are now defunct
(do_*()). The functionality and documentation on how to
deploy to DigitalOcean has been moved to plumberDeploy
(by @meztez)
(#649)
Documentation is updated and now presented using
pkgdown (#570)
New hex logo! Thank you @allisonhorst ! (#570)
Added helper method is_plumber(pr) to determine if
an object is a Plumber router. (#653)
Added support for the SameSite Cookie attribute.
(@chris-dudley,
#640)
When calling include_file(), the
content_type is automatically inferred from the file
extension if content_type is not provided. (#631)
When plumb()ing a file, arguments supplied to
parsers and serializers may be values defined earlier in the file.
(@meztez,
#620)
Updated Docker files. New Docker repo is now rstudio/plumber.
Updates heavily inspired from @mskyttner (#459). (#589)
Support HTTP 405 Code. (@meztez, #554)
Attached the Plumber router to the incoming request object at
req$pr. (@meztez, #554)
Documented plumber options. Added options_plumber().
(@meztez,
#555)
Update documentation on R6 objects (@meztez, #530)
Fix plumb() function when plumb()ing a
directory so that plumber.R is not a requirement if a valid
entrypoint.R file is found. (@blairj09, #471).
If cookie information is too large (> 4093 bytes), a warning will be displayed. (#404)
Added new shorthand types for url parameters. (@byzheng, #388)
Plumber files are now only evaluated once. Prior plumber behavior sourced endpoint functions twice and non-endpoint code blocks once. (#328)
Improve speed of canServe() method of the
PlumberEndpoint class (@atheriel, #484)
Get more file extension content types using the mime
package. (#660)
Endpoints that produce images within a
promises::promise() will now use the expected graphics
device. (#669)
Handle plus signs in URI as space characters instead of actual plus signs (@meztez, #618)
Paths that are missing a leading / have a
/ prepended to the path location. (#656)
Fix possible bugs due to mounted routers without leading slashes (@atheriel, #476 #501).
Modified images serialization to use content-type serializer. Fixes issue with images pre/postserialize hooks (@meztez, #518).
Fix bug preventing error handling when a serializer fails (@antoine-sachet, #490)
Fix URL-decoding of query parameters and URL-encoding/decoding of
cookies. Both now use httpuv::decodeURIComponent instead of
httpuv::decodeURI. (@antoine-sachet, #462)
Fixed bug where functions defined earlier in the file could not
be found when plumb()ing a file. (#416)
A multiline request body is now collapsed to a single line (@robertdj, #270 #297).
Bumped version of httpuv to >= 1.4.5.9000 to address an unexpected segfault (@shapenaji, #289)
Date response header is now supplied by httpuv and not plumber. Fixes non standard date response header issues when using different locales. (@shrektan, #319, #380)
Due to incompatibilities with multipart body values,
req$postBody will only be calculated if accessed. It is
strongly recommended to use req$bodyRaw when trying to
create content from the input body. (#665)
value argument
(postroute, preserialize, and
postserialize) now modify the incoming value as
documented.postserialize hook is now given the
serialized data as its value parameter.swaggerCallback parameter for
run() to supply a callback function for reporting the url
for swagger page.@png and
@jpeg now accept a parenthetical list of arguments that
will be passed into the png() or jpeg() call.
This enables annotations like
#' @png (width = 200, height=500).ByteCompile flagsetErrorHandlerPlumberStaticplumber.r and
entrypoint.r when plumb()ing a directory.swagger.json
file was hosted in such a way that a hosted validator service would not
have been able to access it. For now we just suppress validation of
swagger.json files. (#149)do_configure_https()do_provision() so
you can now call that on a single droplet multiple times.exit hook which can define a function that
will be evaluated when the API is interrupted. e.g.
pr <- plumb("plumber.R"); pr$registerHook("exit", function(){ print("Bye bye!") }). in string path segmentsAccess-Control-Allow-Origin HTTP header to *.
This was previously done for convenience but we’ve decided to prioritize
security here by removing this default. You can still add this header to
any route you want to be accessible from other origins.port parameter in
run(), or by setting the plumber.port
option.PlumberProcessor class and replaced
with a notion of hooks. See registerHook and
registerHooks on the Plumber router.addGlobalProcessor method on Plumber routers
now takes a list which are added as hooks instead of a Processor. Note
that sessionCookie has also been updated to behave
accordingly, meaning that the convention of
pr$addGlobalProcessor(sessionCookie("secret", "cookieName"))
will continue to work for this release.sessionCookie now returns a list instead of a
Processor. Note that addGlobalProcessor has also been
updated to behave accordingly, meaning that the convention of
pr$addGlobalProcessor(sessionCookie("secret", "cookieName"))
will continue to work for this release.addAssets method on Plumber
routers. Use PlumberStatic and the mount
method to attach a static router.addEndpoint method in favor
of the handle method for Plumber routers. Removed support
for the processors, params, and
comments parameters are no longer supported.addFilter method on Plumber
routers in favor of the new filter method. Removed support
for the processor parameter.addGlobalProcessor method
on Plumber routers.setDefaultErrorHandler method on
Plumber routers now takes a function that returns the error handler
function. The top-level function takes a single param named
debug which is managed by the debug parameter
in the run() method.OPTIONS HTTP requests via the
@options annotation.entrypoint.R when
plumb()ing a directory. If this file exists, it is expected
to return a Plumber router representing the API contained in this
directory. If it doesn’t exist, the behavior is unaltered. If both
plumber.R and entrypoint.R exist,
entrypoint.R takes precedence.plumb() the current directory by default if no
arguments are provided.debug parameter to the run method
which can be set to TRUE in order to get more insight into
your API errors.plumb() now accepts an argument dir,
referring to a directory containing plumber.R, which may be
provided instead of file.do_provision(),
do_deploy_api(), do_remove_api() and
do_configure_https() functions to provision and manage your
APIs on a cloud server running on DigitalOcean.source() the referenced R file to plumb inside of a new
environment that inherits directly from the GlobalEnv. This provides
more explicit control over exactly how this environment should
behave.@serializer htmlwidget to support rendering and
returning a self-contained htmlwidget from a plumber endpoint.+ character in a query string to a
space.addSerializer() now
expects a function that returns a serializer, and
Response$new() now expects a serializer itself rather than
a character string naming a serializer. Internally it is the serializer
itself that is attached to the response rather than the name of the
serializer. This allows for a serializer to customize its behavior.@serializer
annotation – R code that will be passed in as an argument to the
serializer factory. See example 09-content-type.sessionCookie function to define a processor that
can be used as a globalProcessor on a router to encrypt values from
req$session and store them as an encrypted cookie in on the user’s
browser.setCookie method to response which (primitively)
allows you to set a cookie to be included in the response.addGlobalProcessor method on plumber
class to support a processor that runs a processor only a single time,
before and then after all other filters and the endpoint.roxygen2 documentation for exported
functionsPlumberRouter R6 object to just
Plumber.addEndpoint() and addFilter() on
the Plumber object.#* prefix.