Skip to content

Commit edd1a08

Browse files
committed
Fix #12 add redirect functionality
1 parent 6b1d471 commit edd1a08

File tree

8 files changed

+204
-16
lines changed

8 files changed

+204
-16
lines changed

NAMESPACE

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ S3method(roxy_tag_parse,roxy_tag_except)
1818
S3method(roxy_tag_parse,roxy_tag_get)
1919
S3method(roxy_tag_parse,roxy_tag_head)
2020
S3method(roxy_tag_parse,roxy_tag_header)
21-
S3method(roxy_tag_parse,roxy_tag_logger)
2221
S3method(roxy_tag_parse,roxy_tag_message)
2322
S3method(roxy_tag_parse,roxy_tag_noDoc)
2423
S3method(roxy_tag_parse,roxy_tag_options)
@@ -28,6 +27,7 @@ S3method(roxy_tag_parse,roxy_tag_plumber)
2827
S3method(roxy_tag_parse,roxy_tag_post)
2928
S3method(roxy_tag_parse,roxy_tag_put)
3029
S3method(roxy_tag_parse,roxy_tag_query)
30+
S3method(roxy_tag_parse,roxy_tag_redirect)
3131
S3method(roxy_tag_parse,roxy_tag_rejectMissingMethods)
3232
S3method(roxy_tag_parse,roxy_tag_response)
3333
S3method(roxy_tag_parse,roxy_tag_routeName)
@@ -78,6 +78,7 @@ export(api_post)
7878
export(api_post_header)
7979
export(api_put)
8080
export(api_put_header)
81+
export(api_redirect)
8182
export(api_run)
8283
export(api_session_cookie)
8384
export(api_statics)

R/Plumber.R

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,11 @@ Plumber <- R6Class(
140140
...,
141141
silent = FALSE
142142
) {
143-
if (length(private$OPENAPI) != 0 && !is.null(private$DOC_TYPE) && private$DOC_TYPE != "") {
143+
if (
144+
length(private$OPENAPI) != 0 &&
145+
!is.null(private$DOC_TYPE) &&
146+
private$DOC_TYPE != ""
147+
) {
144148
openapi_file <- tempfile(fileext = ".json")
145149
write_json(private$OPENAPI, openapi_file, auto_unbox = TRUE)
146150
api_route <- openapi_route(
@@ -263,11 +267,14 @@ Plumber <- R6Class(
263267
function(p) p$`in` == "path",
264268
logical(1)
265269
)
266-
doc$parameters <- c(combine_parameters(
267-
path_info$params,
268-
doc$parameters[doc_path_param],
269-
from_block = FALSE
270-
), doc$parameters[!doc_path_param])
270+
doc$parameters <- c(
271+
combine_parameters(
272+
path_info$params,
273+
doc$parameters[doc_path_param],
274+
from_block = FALSE
275+
),
276+
doc$parameters[!doc_path_param]
277+
)
271278
operation_id <- paste0(path_info$path, "-", method)
272279
doc$parameters <- lapply(doc$parameters, function(par) {
273280
par$operationId <- par$operationId %||% operation_id
@@ -324,6 +331,41 @@ Plumber <- R6Class(
324331
handler <- create_plumber_message_handler(handler)
325332
self$on("message", handler)
326333
},
334+
#' @description Add a redirect to the header router. Depending on the value
335+
#' of `permanent` it will respond with a 307 Temporary Redirect or 308
336+
#' Permanent Redirect. `from` and `to` can contain path parameters and
337+
#' wildcards which will be matched between the two to construct the correct
338+
#' redirect path.
339+
#' @param method The HTTP method the redirect should respond to
340+
#' @param from The path the redirect should respond to
341+
#' @param to The path/URL to redirect the incoming request towards. After
342+
#' resolving any path parameters and wildcards it will be used in the
343+
#' `Location` header
344+
#' @param permanent Logical. Is the redirect considered permanent or
345+
#' temporary? Determines the type of redirct status code to use
346+
redirect = function(method, from, to, permanent = TRUE) {
347+
method <- arg_match0(
348+
tolower(method),
349+
c(
350+
"get",
351+
"head",
352+
"post",
353+
"put",
354+
"delete",
355+
"connect",
356+
"options",
357+
"trace",
358+
"patch",
359+
"any",
360+
"all"
361+
)
362+
)
363+
if (method == "any") method <- "all"
364+
check_string(from)
365+
check_string(to)
366+
check_bool(permanent)
367+
self$header_router(method, from, to, permanent)
368+
},
327369
#' @description Parses a plumber file and updates the app according to it
328370
#' @param file The path to a file to parse
329371
#' @param env The parent environment to the environment the file should be
@@ -363,6 +405,14 @@ Plumber <- R6Class(
363405
for (handler in parsed$message_handlers) {
364406
self$message_handler(handler)
365407
}
408+
for (redirect in parsed$redirects) {
409+
self$redirect(
410+
redirect$method,
411+
redirect$from,
412+
redirect$to,
413+
redirect$permanent
414+
)
415+
}
366416
self$add_api_doc(parsed$api)
367417
parsed$modifier(self)
368418
},

R/api_handlers.R

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,13 @@ api_any_header <- handle_constructor("any", header = TRUE)
362362
#'
363363
#' @export
364364
#'
365-
api_add_route <- function(api, name, route = NULL, header = FALSE, after = NULL) {
365+
api_add_route <- function(
366+
api,
367+
name,
368+
route = NULL,
369+
header = FALSE,
370+
after = NULL
371+
) {
366372
api$add_route(name = name, route = route, header = header, after = after)
367373
api
368374
}
@@ -408,3 +414,34 @@ api_message <- function(api, handler) {
408414
api$message_handler(handler)
409415
api
410416
}
417+
418+
#' Redirect request to another resource
419+
#'
420+
#' While it is optimal that an API remains stable over its lifetime it is often
421+
#' not fully attainable. In order to direct requests for ressources that has
422+
#' been moved to the new location you can add a redirect that ensures a smooth
423+
#' transition for clients still using the old path. Depending on the value
424+
#' of `permanent` the redirect will respond with a `307 Temporary Redirect` or
425+
#' `308 Permanent Redirect`. `from` and `to` can contain path parameters and
426+
#' wildcards which will be matched between the two to construct the correct
427+
#' redirect path. Further, `to` can either be a path to the same server or a
428+
#' fully qualified URL to redirect requests to another server alltogether.
429+
#'
430+
#' @param api A plumber2 api object to add the redirect to
431+
#' @param method The HTTP method the redirect should respond to
432+
#' @param from The path the redirect should respond to
433+
#' @param to The path/URL to redirect the incoming request towards. After
434+
#' resolving any path parameters and wildcards it will be used in the
435+
#' `Location` header
436+
#' @param permanent Logical. Is the redirect considered permanent or
437+
#' temporary? Determines the type of redirct status code to use
438+
#'
439+
#' @return This functions return the `api` object allowing for easy chaining
440+
#' with the pipe
441+
#'
442+
#' @export
443+
#'
444+
api_redirect <- function(api, method, from, to, permanent = TRUE) {
445+
api$redirect(method, from, to, permanent)
446+
api
447+
}

R/parse_plumber_file.R

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ parse_plumber_file <- function(
9696
}
9797
}
9898

99+
redirects <- blocks[vapply(blocks, inherits, logical(1), "plumber_redirect")]
100+
redirects <- unlist(redirects, recursive = FALSE)
101+
99102
list(
100103
route = route,
101104
header_route = header_route,
@@ -106,6 +109,7 @@ parse_plumber_file <- function(
106109
logical(1),
107110
"message_call"
108111
)],
112+
redirects = redirects,
109113
api = c(globals, paths),
110114
modifiers = modifier
111115
)
@@ -116,11 +120,13 @@ parse_block <- function(block, route, header_route, env = caller_env()) {
116120
tags <- vapply(block$tags, `[[`, character(1), "tag")
117121
values <- lapply(block$tags, `[[`, "raw")
118122
if (any(tags == "assets")) {
119-
parse_asset_block(call, block, tags, values, header_route, env)
123+
parse_asset_block(call, block, tags, values, route, env)
120124
} else if (any(tags == "statics")) {
121125
parse_static_block(call, block, tags, values, env)
122126
} else if (any(tags == "message")) {
123127
parse_message_block(call, block, tags, values)
128+
} else if (any(tags == "redirect")) {
129+
parse_redirect_block(call, block, tags, values)
124130
} else if (any(tags == "plumber")) {
125131
parse_plumber_block(call, tags)
126132
} else if (
@@ -290,3 +296,25 @@ parse_message_block <- function(call, block, tags, values) {
290296
}
291297
structure(call, class = "message_call")
292298
}
299+
300+
parse_redirect_block <- function(call, block, tags, values) {
301+
res <- lapply(values[tags == "redirect"], function(x) {
302+
x <- stringi::stri_split_fixed(x, " ", n = 3)[[1]]
303+
if (length(x) != 3) {
304+
cli::cli_warn(c(
305+
"Malformed {.field @redirect} tag",
306+
i = "The format must conform to: <method> <from path> <to path>"
307+
))
308+
return(NULL)
309+
}
310+
is_permanent <- grepl("^\\!", x[1])
311+
x[1] <- sub("!", "", x[1], fixed = TRUE)
312+
list(
313+
method = x[1],
314+
from = x[2],
315+
to = x[3],
316+
permanent = is_permanent
317+
)
318+
})
319+
class(res) <- "plumber2_redirect"
320+
}

R/roxygen_tags.R

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ roxy_tag_parse.roxy_tag_message <- function(x) {
138138
x
139139
}
140140

141-
## Asset annotations ---------------------------------------------------------
141+
## Asset annotations -----------------------------------------------------------
142142

143143
#' @export
144144
#' @importFrom roxygen2 roxy_tag_parse
@@ -156,7 +156,15 @@ roxy_tag_parse.roxy_tag_except <- function(x) {
156156
x
157157
}
158158

159-
## Other annotations ---------------------------------------------------------
159+
## Redirect annotations --------------------------------------------------------
160+
161+
#' @export
162+
#' @importFrom roxygen2 roxy_tag_parse
163+
roxy_tag_parse.roxy_tag_redirect <- function(x) {
164+
x
165+
}
166+
167+
## Other annotations -----------------------------------------------------------
160168

161169
#' @export
162170
#' @importFrom roxygen2 roxy_tag_parse
@@ -181,8 +189,3 @@ roxy_tag_parse.roxy_tag_rejectMissingMethods <- function(x) {
181189
roxy_tag_parse.roxy_tag_routeName <- function(x) {
182190
x
183191
}
184-
#' @export
185-
#' @importFrom roxygen2 roxy_tag_parse
186-
roxy_tag_parse.roxy_tag_logger <- function(x) {
187-
x
188-
}

_pkgdown.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ reference:
4444
- api_assets
4545
- api_message
4646
- api_add_route
47+
- api_redirect
4748
- title: "Parsers"
4849
contents:
4950
- register_parser

man/Plumber.Rd

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/api_redirect.Rd

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)