Title: | Turn a Package into an HTTP API |
---|---|
Description: | Wrapper around the plumber package to turn a package into an HTTP API. This adds some conventions that we find useful, such as some testing infrastructure and automatic validation of responses against a json schema. |
Authors: | Rich FitzJohn [aut, cre], Imperial College of Science, Technology and Medicine [cph] |
Maintainer: | Rich FitzJohn <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.1.15 |
Built: | 2024-10-26 04:11:06 UTC |
Source: | https://github.com/reside-ic/porcelain |
porcelain
objectA porcelain
object. This extends (via
inheritance) a plumber object, and so only changes to the
plumber API are documented here.
plumber::Hookable
-> plumber::Plumber
-> porcelain
plumber::Hookable$registerHooks()
plumber::Plumber$addAssets()
plumber::Plumber$addEndpoint()
plumber::Plumber$addFilter()
plumber::Plumber$addGlobalProcessor()
plumber::Plumber$call()
plumber::Plumber$filter()
plumber::Plumber$getApiSpec()
plumber::Plumber$getDebug()
plumber::Plumber$mount()
plumber::Plumber$onHeaders()
plumber::Plumber$onWSOpen()
plumber::Plumber$openAPIFile()
plumber::Plumber$print()
plumber::Plumber$registerHook()
plumber::Plumber$removeHandle()
plumber::Plumber$route()
plumber::Plumber$run()
plumber::Plumber$serve()
plumber::Plumber$set404Handler()
plumber::Plumber$setApiSpec()
plumber::Plumber$setDebug()
plumber::Plumber$setDocs()
plumber::Plumber$setDocsCallback()
plumber::Plumber$setErrorHandler()
plumber::Plumber$setParsers()
plumber::Plumber$setSerializer()
plumber::Plumber$swaggerFile()
plumber::Plumber$unmount()
new()
Create a porcelain object
porcelain$new(..., validate = FALSE, logger = NULL)
...
Parameters passed to plumber
validate
Logical, indicating if any validation
(implemented by the validate_response
argument) should
be enabled. This should be set to FALSE
in production
environments. By default (if validate
is NULL
),
we look at the value of the environment PORCELAIN_VALIDATE
-
if true
(case insensitive) then we will validate.
This is intended to support easy use of validation on
continuous integration systems.
logger
Optional logger, from the lgr
package, perhaps
created with porcelain_logger. If given, then we
will log at the beginning and end of the request.
include_package_endpoints()
Include package endpoints
porcelain$include_package_endpoints(state = NULL, package = NULL)
state
A named list of state, if your package requires any state-binding endpoints. Typically these will be mutable state (database connections, job queues, or similar). You must provide all states as required by the combination of all endpoints.
package
Either a package name or environment (optional, usually we'll find the right thing)
handle()
Handle an endpoint
porcelain$handle(...)
...
Either a single argument, being a
porcelain_endpoint
object representing an endpoint, or
arguments to pass through to plumber
.
request()
Send a request to plumber for debugging
Sends a request to plumber so that the API can be easily
tested without running the whole API. The interface here will
probably change, and may end up using the interface of httr
.
porcelain$request( method, path, query = NULL, body = NULL, content_type = NULL, request_id = NULL )
method
Name of HTTP method to use (e.g., GET
)
path
Path to send the request to
query
Optional query parameters as a named list or character vector.
body
Optional body (only valid with PUT
, POST
,
etc).
content_type
Optional content type (mime) which can be
provided alongside body
. If not provided it is set to
application/octet-stream
if body
is raw, or
application/json
otherwise.
request_id
Optional request ID. An ID which is attached to every log raised by this request. Used for tracing purposes.
clone()
The objects of this class are cloneable with this method.
porcelain$clone(deep = FALSE)
deep
Whether to make a deep clone.
Intended to be used from endpoint target function. Note Content-Type
headers are handled by returning arg to endpoint.
porcelain_add_headers(data, headers)
porcelain_add_headers(data, headers)
data |
Response data |
headers |
Named list of headers to add. |
Data from endpoint target with headers
porcelain_add_headers("output", list("Content-Dispotition" = "output_file.txt"))
porcelain_add_headers("output", list("Content-Dispotition" = "output_file.txt"))
While porcelain makes it easy to test endpoints individually, you may still want some integration or end-to-end tests where you bring the entire API up and interact with it from your tests. This class provides a helper for doing this in a way that is reasonably tidy.
While porcelain makes it easy to test endpoints individually, you may still want some integration or end-to-end tests where you bring the entire API up and interact with it from your tests. This class provides a helper for doing this in a way that is reasonably tidy.
log
The path to the log file (read-only)
port
The port used by the background server (read-only)
new()
Create a background server object
porcelain_background$new( create, args = NULL, port = NULL, log = NULL, verbose = FALSE, timeout = 60, env = NULL )
create
A function that will create an api object
args
Arguments that will be passed to create
when creating
the api object in the background process
port
The port to use for the background server.
If not given then a random free port will be used in the range
8000 to 10000 - you can find the created port using the port
field in the resulting object, or use the $url()
or $request()
methods.
log
The path to a log file to use
verbose
Logical, indicating if we should print informational messages to the console on start/stop etc.
timeout
The number of seconds to wait for the server to become available. This needs to cover the time taken to spawn the R process, and create your API object (loading all packages needed) up to the point where the server is responsive. In most cases this will take 1-2s but if you use packages that use many S4 methods or run this on a slow computer (e.g., a continuous integration server) it may take longer than you expect. The default is one minute which should be sufficient in almost all cases.
env
A named character vector of environment variables (e.g.,
c(VARIABLE = "value")
) to set in the background process before
launching the server. You can use this to control the behaviour of
the background server using variables your api recognises. In
addition, we export callr::rcmd_safe_env()
and the value of
PORCELAIN_VALIDATE
.
start()
Start the server. It is an error to try and start a server that is already running.
porcelain_background$start()
status()
Return the background server status. This will be one of:
running
: The server is running
stopped
: The server is stopped
blocked
: The server is stopped, but something else is running
on the port that we would use
starting
: The server is starting up (not visible in normal usage)
porcelain_background$status()
stop()
Stop a running server. If the server is not running, this has no effect.
porcelain_background$stop()
url()
Create a url string for the server, interpolating the
(possibly random) port number. You can use this in your tests
like bg$url("/path")
porcelain_background$url(path)
path
String representing the absolute path
request()
Run a request to the server, using httr
. This presents
a similar inteface to the request
method on the porcelain object.
porcelain_background$request(method, path, ...)
method
The http method as a string (e.g., "GET"
), passed
to httr::VERB as the verb
argument
path
String representing the absolute path, passed to $url()
...
Additional arguments passed to httr::VERB
, such as
query
, or the body for a POST
request.
Create a porcelain_endpoint
object that collects
together an HTTP method (e.g., GET
), a path (e.g.,
/path
) and a target R function. Unlike plumber
endpoints, porcelain endpoints are meant to be used in testing.
method
HTTP method
path
HTTP path
target
R function used for the endpoint
validate
Logical, indicating if response validation is used
inputs
Input control
state
Possibly mutable state
returning
An porcelain_returning
object
controlling the return type (content type, status code,
serialisation and validation information).
new()
Create an endpoint
porcelain_endpoint$new(method, path, target, ..., returning, validate = NULL)
method
The HTTP method to support
path
The server path for the endpoint
target
An R function to run as the endpoint
...
Additional parameters, currently representing
inputs. You can use the functions
porcelain_input_query
,
porcelain_input_body_binary
and
porcelain_input_body_json
to define inputs and pass
them into this method. The names used must match those in
target
.
returning
Information about what the endpoint returns,
as created by porcelain_returning
validate
Logical, indicating if any validation
(implemented by the validate_response
argument) should
be enabled. This should be set to FALSE
in production
environments. By default (if validate
is NULL
),
we look at the value of the environment PORCELAIN_VALIDATE
-
if true
(case insensitive) then we will validate.
This is intended to support easy use of validation on
continuous integration systems.
validate_response
Optional function that throws an error of the processed body is "invalid".
run()
Run the endpoint. This will produce a
standardised response object that contains status_code
,
content_type
, body
(the serialised output as run
through the process
method and returned by plumber) and
data
(the result of running the target function)
porcelain_endpoint$run(...)
...
Arguments passed through to the target
function
request()
Test the endpoint. This creates a full plumber
object and serves one request to the endpoint. Argument are as
passed through to porcelain
's $request()
method, except that method
and path
are
automatically taken from the endpoint itself.
porcelain_endpoint$request(...)
...
Arguments passed through to the request
method
(query
, body
and content_type
).
plumber()
Helper method for use with plumber - not designed for end-user use. This is what gets called by plumber when the endpoint receives a request.
porcelain_endpoint$plumber(req, res, ...)
req, res
Conventional plumber request/response objects
...
Additional arguments passed through to run
create()
Create a plumber endpoint
porcelain_endpoint$create(envir, validate)
envir
Environment as used by plumber (currently unclear)
validate
Logical, allowing override of validation at the api level. This takes precedence over the value set when creating the endpoint.
clone()
The objects of this class are cloneable with this method.
porcelain_endpoint$clone(deep = FALSE)
deep
Whether to make a deep clone.
Control for body parameters. This might change. There are several types of HTTP bodies that we want to consider here - the primary ones are a body uploaded in binary, the other is a json object. In the latter we want to validate the body against a schema (at least if validation is used). In future we might also support a form input here too.
porcelain_input_body_binary(name, content_type = NULL) porcelain_input_body_json(name, schema = NULL, root = NULL, extract = NULL)
porcelain_input_body_binary(name, content_type = NULL) porcelain_input_body_json(name, schema = NULL, root = NULL, extract = NULL)
name |
Name of the parameter |
content_type |
Content type for the input. If not given, then
|
schema |
The name of the json schema to use |
root |
The root of the schema directory. |
extract |
Optionally, the name of an element to extract from
the json. If given, then the body must be a json object (not an
array, for example) and |
Control for query parameters.
porcelain_input_query(..., .parameters = list(...))
porcelain_input_query(..., .parameters = list(...))
... |
Named arguments representing accepted parameters. The value of each must be a type. |
.parameters |
A list of named parameters to accept, instead
of using |
porcelain::porcelain_input_query(number = "integer")
porcelain::porcelain_input_query(number = "integer")
Create a json-emitting logger, using the 'lgr' package.
porcelain_logger(log_level = "info", name = NULL, path = NULL)
porcelain_logger(log_level = "info", name = NULL, path = NULL)
log_level |
The level of detail to log to. See
|
name |
The name of the logger. By default we use one derived from the package name, though this may not always be accurate. |
path |
Optionally, the path to log into. If not given then we log to the console. |
A "Logger" object (see lgr::Logger)
logger <- porcelain::porcelain_logger(name = "example") logger$log("info", "hello") logger$log("trace", "silent")
logger <- porcelain::porcelain_logger(name = "example") logger$log("info", "hello") logger$log("trace", "silent")
Find an endpoint defined implicitly through roxygen comments (rather than explicitly via writing porcelain_endpoint.
porcelain_package_endpoint( package, method, path, state = NULL, validate = NULL )
porcelain_package_endpoint( package, method, path, state = NULL, validate = NULL )
package |
The name of the package to look in, provided as a string or as a namespace |
method |
The HTTP method (i.e., verb), such as |
path |
The path of the method (e.g., |
state |
A list of state to bind into the method, if your endpoint requires any |
validate |
Logical, indicating if the method should be created with schema validation enabled. |
The endpoint, a porcelain_endpoint object
Support for describing and controlling expected return types. The
high-level functions (porcelain_returning_json
and
porcelain_returning_binary
) should be generally used.
porcelain_returning(content_type, process, validate, status_code = 200L) porcelain_returning_json(schema = NULL, root = NULL, status_code = 200L) porcelain_returning_binary(status_code = 200L) porcelain_returning_text(status_code = 200L)
porcelain_returning(content_type, process, validate, status_code = 200L) porcelain_returning_json(schema = NULL, root = NULL, status_code = 200L) porcelain_returning_binary(status_code = 200L) porcelain_returning_text(status_code = 200L)
content_type |
The MIME content type for the endpoint,
e.g. |
process |
A processing function that will convert the output
of the handler function into something of the type
|
validate |
A function that validates the return value and
throws an error if the output is not expected. This will only
be used if the endpoint is created with |
status_code |
The HTTP status code that the endpoint will use on a successful return. The default of 200 should be reasonable. |
schema |
The name of the json schema to use |
root |
The root of the schema directory. |
A roclet for processing @porcelain
tags within a package. This
presents an automated declarative approach to defining porcelain
APIs using roxygen tags. When you roxygenise your package (e.g.,
with devtools::document()
or roxygen2::roxygenise()
) this
roclet will create a file R/porcelain.R
within your package that
will be included into your package API.
porcelain_roclet()
porcelain_roclet()
A roclet, used by roxygen2
(not typically called by
users directly)
Bind state into an endpoint
porcelain_state(..., .state = list(...))
porcelain_state(..., .state = list(...))
... |
Named arguments representing state to bind; see
|
.state |
A list of named state to bind, instead
of using |
This method allows state to be bound to the target function. Each
element of ...
(or .state
) is named with the
argument to the target function being bound, and the value is the
value that argument will take. Once bound, the arguments to the
target function may not be provided by an input.
The primary use case for this is to bind mutable state (database connections, etc) that may be shared amongst different endpoints within an API.
Throw an error from an endpoint. This function is intended to allow target functions to throw nice errors back through the API.
porcelain_stop(message, code = "ERROR", errors = NULL, status_code = 400L, ...)
porcelain_stop(message, code = "ERROR", errors = NULL, status_code = 400L, ...)
message |
The human-readable message of the error. Ignored
if |
code |
Optional code for the error - if not given, then
|
errors |
A named list of errors - use this to signal multiple error conditions as key/value pairs. |
status_code |
The HTTP status code to use. The default (400) means "bad request" which should be a reasonable catch-all for bad user data. |
... |
Additional named args to be included as fields in the
error response JSON. The values must be in format ready for
serialization to JSON using |
Nothing, as this function throws an error