Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mount docs as first mount #882

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# plumber (development version)

* Always mount OpenAPI documentation at `/__docs__/` as the first mount to have preference when using conflicting mount locations (e.g. a static mount at `/`). (#882)


# plumber 1.2.1

Expand Down
16 changes: 14 additions & 2 deletions R/plumber.R
Original file line number Diff line number Diff line change
Expand Up @@ -250,24 +250,34 @@ Plumber <- R6Class(
}

# Set and restore the wd to make it appear that the proc is running local to the file's definition.
old_wd <- NULL
if (!is.null(private$filename)) {
old_wd <- setwd(dirname(private$filename))
on.exit({setwd(old_wd)}, add = TRUE)
schloerke marked this conversation as resolved.
Show resolved Hide resolved
}

# Run user exit hooks given docs and wd are still set
on.exit(private$runHooks("exit"), add = TRUE)

if (isTRUE(docs_info$enabled)) {
mount_docs(
pr = self,
pr_private = private,
host = host,
port = port,
docs_info = docs_info,
callback = swaggerCallback,
quiet = quiet
)
# Unmount the docs before restoring the wd
# Unmount needs to happen after exit hooks
# No guarantee that new exit hooks are not added after running API
on.exit(unmount_docs(self, docs_info), add = TRUE)
}

on.exit(private$runHooks("exit"), add = TRUE)
# Restore the prior working directory after exit hooks have run
if (!is.null(old_wd)) {
on.exit(setwd(old_wd), add = TRUE)
}

httpuv::runServer(host, port, self)
},
Expand Down Expand Up @@ -299,6 +309,8 @@ Plumber <- R6Class(
path <- paste0(path, "/")
}

# Mount order matters
# Append a mounted router
private$mnts[[path]] <- router
},
#' @description Unmount a Plumber router
Expand Down
14 changes: 12 additions & 2 deletions R/ui.R
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#' @include globals.R
docs_root <- paste0("/__docs__/")

# Mount OpenAPI and Docs
#' @noRd
mount_docs <- function(pr, host, port, docs_info, callback, quiet = FALSE) {
mount_docs <- function(pr, pr_private, host, port, docs_info, callback, quiet = FALSE) {

# return early if not enabled
if (!isTRUE(docs_info$enabled)) {
Expand Down Expand Up @@ -34,7 +35,17 @@ mount_docs <- function(pr, host, port, docs_info, callback, quiet = FALSE) {

if (is_docs_available(docs_info$docs)) {
docs_mount <- .globals$docs[[docs_info$docs]]$mount
current_mnt_names <- names(pr_private$mnts)
docs_url <- do.call(docs_mount, c(list(pr, api_url), docs_info$args))
# Mount order matters
# Move new & ordered docs mounts to the front to be processed first
post_mnt_names <- names(pr_private$mnts)
doc_paths <- setdiff(post_mnt_names, current_mnt_names)
pr_private$mnts <- c(
pr_private$mnts[doc_paths],
pr_private$mnts[setdiff(post_mnt_names, doc_paths)]
)

if (!isTRUE(quiet)) {
message("Running ", docs_info$docs, " Docs at ", docs_url, sep = "")
}
Expand Down Expand Up @@ -184,7 +195,6 @@ register_docs <- function(name, index, static = NULL) {
stopifnot(is.function(index))
if (!is.null(static)) stopifnot(is.function(static))

docs_root <- paste0("/__docs__/")
docs_paths <- c("/index.html", "/")
mount_docs_func <- function(pr, api_url, ...) {
# Save initial extra argument values
Expand Down
6 changes: 6 additions & 0 deletions tests/testthat/helper-interrupt.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

with_interrupt <- function(expr, delay = 0) {
# Causes pr_run() to immediately exit
later::later(httpuv::interrupt, delay = delay)
force(expr)
}
6 changes: 0 additions & 6 deletions tests/testthat/test-plumber-run.R
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@

with_interrupt <- function(expr) {
# Causes pr_run() to immediately exit
later::later(httpuv::interrupt)
force(expr)
}

test_that("quiet=TRUE suppresses startup messages", {
with_interrupt({
expect_message(pr() %>% pr_run(quiet = TRUE), NA)
Expand Down
26 changes: 26 additions & 0 deletions tests/testthat/test-ui.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

test_that("doc mounts are prepended", {

assertions <- 0

root <-
test_path("files/static.R") %>%
pr() %>%
pr_set_docs(TRUE) %>%
pr_hook("exit", function(...) {
expect_length(root$mounts, 3)
assertions <<- assertions + 1
expect_equal(names(root$mounts)[1], "/__docs__/")
assertions <<- assertions + 1
})

expect_length(root$mounts, 2)
assertions <- assertions + 1

# no docs. do not find printed message
with_interrupt({
root %>% pr_run()
})

expect_equal(assertions, 3)
})