From 1c93c5819da64c2c66668ce51daaacdab489009e Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Thu, 18 Mar 2021 18:12:12 -0700 Subject: [PATCH 1/6] Add `srcref` field to PlumberStep/Filter/Endpoint Needed for plumbertableau to read additional comments from the plumber.R file for each PlumberEndpoint --- R/plumb-block.R | 9 ++++++--- R/plumber-step.R | 19 ++++++++++++++++--- R/plumber.R | 4 ++-- man/PlumberEndpoint.Rd | 5 ++++- man/PlumberStep.Rd | 6 +++++- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/R/plumb-block.R b/R/plumb-block.R index 88ca9b75c..501949cd7 100644 --- a/R/plumb-block.R +++ b/R/plumb-block.R @@ -260,7 +260,8 @@ plumbBlock <- function(lineNum, file, envir = parent.frame()){ #' Evaluate and activate a "block" of code found in a plumber API file. #' @noRd evaluateBlock <- function(srcref, file, expr, envir, addEndpoint, addFilter, pr) { - lineNum <- srcref[1] - 1 + lines <- srcref[c(1,3)] + lineNum <- lines[1] - 1 block <- plumbBlock(lineNum, file, envir) @@ -278,7 +279,8 @@ evaluateBlock <- function(srcref, file, expr, envir, addEndpoint, addFilter, pr) envir = envir, serializer = block$serializer, parsers = block$parsers, - lines = srcref, + lines = lines, + srcref = srcref, params = block$params, comments = block$comments, responses = block$responses, @@ -288,7 +290,8 @@ evaluateBlock <- function(srcref, file, expr, envir, addEndpoint, addFilter, pr) addEndpoint(ep, block$preempt) }) } else if (!is.null(block$filter)){ - filter <- PlumberFilter$new(block$filter, expr, envir, block$serializer, srcref) + filter <- PlumberFilter$new(block$filter, expr, envir, block$serializer, + lines = lines, srcref = srcref) addFilter(filter) } else if (!is.null(block$assets)){ diff --git a/R/plumber-step.R b/R/plumber-step.R index 299d5101b..7fc0b3fdd 100644 --- a/R/plumber-step.R +++ b/R/plumber-step.R @@ -27,6 +27,8 @@ PlumberStep <- R6Class( "PlumberStep", inherit=Hookable, public = list( + #' @field srcref from step block + srcref = NULL, #' @field lines lines from step block lines = NA, #' @field serializer step serializer function @@ -36,8 +38,9 @@ PlumberStep <- R6Class( #' @param envir step environment #' @param lines step block #' @param serializer step serializer + #' @param srcref `srcref` attribute from block #' @return A new `PlumberStep` object - initialize = function(expr, envir, lines, serializer){ + initialize = function(expr, envir, lines, serializer, srcref){ private$expr <- expr if (is.expression(expr)) { private$func <- eval(expr, envir) @@ -47,6 +50,9 @@ PlumberStep <- R6Class( throw_if_func_is_not_a_function(private$func) private$envir <- envir + if (!missing(srcref)) { + self$srcref <- srcref + } if (!missing(lines)){ self$lines <- lines } @@ -211,13 +217,14 @@ PlumberEndpoint <- R6Class( #' @param envir Endpoint environment #' @param serializer Endpoint serializer. Ex: [serializer_json()] #' @template pr_setParsers__parsers + #' @param srcref `srcref` attribute from block #' @param lines Endpoint block #' @param params Endpoint params #' @param comments,responses,tags Values to be used within the OpenAPI Spec #' @details Parameters values are obtained from parsing blocks of lines in a plumber file. #' They can also be provided manually for historical reasons. #' @return A new `PlumberEndpoint` object - initialize = function(verbs, path, expr, envir, serializer, parsers, lines, params, comments, responses, tags) { + initialize = function(verbs, path, expr, envir, serializer, parsers, lines, params, comments, responses, tags, srcref) { self$verbs <- verbs @@ -239,6 +246,9 @@ PlumberEndpoint <- R6Class( if (!missing(parsers) && !is.null(parsers)) { self$parsers <- make_parser(parsers) } + if (!missing(srcref)) { + self$srcref <- srcref + } if (!missing(lines)){ self$lines <- lines } @@ -300,7 +310,7 @@ PlumberFilter <- R6Class( inherit = PlumberStep, public = list( name = NA, - initialize = function(name, expr, envir, serializer, lines){ + initialize = function(name, expr, envir, serializer, lines, srcref){ self$name <- name private$expr <- expr if (is.expression(expr)){ @@ -314,6 +324,9 @@ PlumberFilter <- R6Class( if (!missing(serializer)){ self_set_serializer(self, serializer) } + if (!missing(srcref)) { + self$srcref <- srcref + } if (!missing(lines)){ self$lines <- lines } diff --git a/R/plumber.R b/R/plumber.R index fe9aab86c..d4dfebd3a 100644 --- a/R/plumber.R +++ b/R/plumber.R @@ -100,7 +100,7 @@ Plumber <- R6Class( # Add in the initial filters for (fn in names(filters)){ - fil <- PlumberFilter$new(fn, filters[[fn]], private$envir, private$default_serializer, NULL) + fil <- PlumberFilter$new(fn, filters[[fn]], private$envir, private$default_serializer, NULL, NULL) private$filts <- c(private$filts, fil) } @@ -122,7 +122,7 @@ Plumber <- R6Class( for (i in seq_len(length(private$parsed))) { e <- private$parsed[i] - srcref <- attr(e, "srcref")[[1]][c(1,3)] + srcref <- attr(e, "srcref")[[1]] evaluateBlock(srcref, private$lines, e, private$envir, private$addEndpointInternal, private$addFilterInternal, self) diff --git a/man/PlumberEndpoint.Rd b/man/PlumberEndpoint.Rd index 752a230a6..54d5add5a 100644 --- a/man/PlumberEndpoint.Rd +++ b/man/PlumberEndpoint.Rd @@ -128,7 +128,8 @@ Create a new \code{PlumberEndpoint} object params, comments, responses, - tags + tags, + srcref )}\if{html}{\out{}} } @@ -174,6 +175,8 @@ parsers = c("json", "form", "text", "octet", "multi") \item{\code{params}}{Endpoint params} \item{\code{comments, responses, tags}}{Values to be used within the OpenAPI Spec} + +\item{\code{srcref}}{\code{srcref} attribute from block} } \if{html}{\out{}} } diff --git a/man/PlumberStep.Rd b/man/PlumberStep.Rd index 4c1634627..813fe0a02 100644 --- a/man/PlumberStep.Rd +++ b/man/PlumberStep.Rd @@ -13,6 +13,8 @@ of a request by a plumber router. \section{Public fields}{ \if{html}{\out{
}} \describe{ +\item{\code{srcref}}{from step block} + \item{\code{lines}}{lines from step block} \item{\code{serializer}}{step serializer function} @@ -41,7 +43,7 @@ of a request by a plumber router. \subsection{Method \code{new()}}{ Create a new \code{\link[=PlumberStep]{PlumberStep()}} object \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{PlumberStep$new(expr, envir, lines, serializer)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{PlumberStep$new(expr, envir, lines, serializer, srcref)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -54,6 +56,8 @@ Create a new \code{\link[=PlumberStep]{PlumberStep()}} object \item{\code{lines}}{step block} \item{\code{serializer}}{step serializer} + +\item{\code{srcref}}{\code{srcref} attribute from block} } \if{html}{\out{
}} } From 9e7624765f370e05b6906ad5853fcfcffbbe1b56 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Thu, 18 Mar 2021 18:16:21 -0700 Subject: [PATCH 2/6] Add PlumberEndpoint$getFunc() --- R/plumber-step.R | 4 ++++ man/PlumberEndpoint.Rd | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/R/plumber-step.R b/R/plumber-step.R index 7fc0b3fdd..bf442523c 100644 --- a/R/plumber-step.R +++ b/R/plumber-step.R @@ -273,6 +273,10 @@ PlumberEndpoint <- R6Class( getPathParams = function(path){ extractPathParams(private$regex, path) }, + #' @description retrieve endpoint function + getFunc = function() { + private$func + }, #' @description retrieve endpoint expression parameters getFuncParams = function() { getArgsMetadata(private$func) diff --git a/man/PlumberEndpoint.Rd b/man/PlumberEndpoint.Rd index 54d5add5a..0bbaeda4f 100644 --- a/man/PlumberEndpoint.Rd +++ b/man/PlumberEndpoint.Rd @@ -46,6 +46,7 @@ each separate verb/path into its own endpoint, so we just do that.} \item \href{#method-matchesPath}{\code{PlumberEndpoint$matchesPath()}} \item \href{#method-new}{\code{PlumberEndpoint$new()}} \item \href{#method-getPathParams}{\code{PlumberEndpoint$getPathParams()}} +\item \href{#method-getFunc}{\code{PlumberEndpoint$getFunc()}} \item \href{#method-getFuncParams}{\code{PlumberEndpoint$getFuncParams()}} \item \href{#method-getEndpointParams}{\code{PlumberEndpoint$getEndpointParams()}} \item \href{#method-setPath}{\code{PlumberEndpoint$setPath()}} @@ -200,6 +201,16 @@ retrieve endpoint path parameters } \if{html}{\out{}} } +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-getFunc}{}}} +\subsection{Method \code{getFunc()}}{ +retrieve endpoint function +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{PlumberEndpoint$getFunc()}\if{html}{\out{
}} +} + } \if{html}{\out{
}} \if{html}{\out{}} From 8e55805f4070aa90987224795adf110aa09159ad Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Fri, 19 Mar 2021 06:36:37 -0700 Subject: [PATCH 3/6] Bump version --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 317519e26..12eb50101 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Encoding: UTF-8 Package: plumber Type: Package Title: An API Generator for R -Version: 1.0.0.90001 +Version: 1.0.0.90002 Roxygen: list(markdown = TRUE) Authors@R: c( person("Barret", "Schloerke", role = c("cre", "aut"), email = "barret@rstudio.com"), From 158c4d06192ccdecc3ae9eacc046996fde433aeb Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 22 Mar 2021 13:20:20 -0400 Subject: [PATCH 4/6] Update NEWS.md --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index cdb077463..f04c937c6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,6 +31,8 @@ plumber 1.0.0.9999 Development version * 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) + ### Bug fixes * 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) From 9a1cf52a39b8c996ea922352a035f58ff4704ed4 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 22 Mar 2021 14:35:39 -0400 Subject: [PATCH 5/6] add a PlumberStep srcref test --- tests/testthat/test-parse-block.R | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/testthat/test-parse-block.R b/tests/testthat/test-parse-block.R index f5eb9d496..b84273c1d 100644 --- a/tests/testthat/test-parse-block.R +++ b/tests/testthat/test-parse-block.R @@ -289,4 +289,18 @@ test_that("block respect original order of lines for comments, tags and response expect_equal(b$responses, list(`200`=list(description="ok"), `404` = list(description="not ok"))) }) +test_that("srcref values are set while plumbing from a file", { + + withr::local_options(list(keep.source = FALSE)) + + root <- plumb_api("plumber", "01-append") + endpt <- root$endpoints[[1]][[1]] + expect_s3_class(endpt$srcref, "srcref") + + root_with_no_srcref <- pr() %>% pr_get("/", force) + endpt_with_no_srcref <- root_with_no_srcref$endpoints[[1]][[1]] + expect_equal(endpt_with_no_srcref$srcref, NULL) +}) + + # TODO: more testing around filter, assets, endpoint, etc. From 10c7db9c5a28c1039de4dc4f0734e77546a394e0 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 22 Mar 2021 14:55:21 -0400 Subject: [PATCH 6/6] Remove unneeded withr call --- tests/testthat/test-parse-block.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/testthat/test-parse-block.R b/tests/testthat/test-parse-block.R index b84273c1d..426501d4a 100644 --- a/tests/testthat/test-parse-block.R +++ b/tests/testthat/test-parse-block.R @@ -291,8 +291,6 @@ test_that("block respect original order of lines for comments, tags and response test_that("srcref values are set while plumbing from a file", { - withr::local_options(list(keep.source = FALSE)) - root <- plumb_api("plumber", "01-append") endpt <- root$endpoints[[1]][[1]] expect_s3_class(endpt$srcref, "srcref")