Skip to content

Commit

Permalink
chore: Remove lifecycle warnings and error on lintr checks (#321)
Browse files Browse the repository at this point in the history
Co-authored-by: Toph Allen <toph.allen@gmail.com>
  • Loading branch information
schloerke and toph-allen authored Nov 7, 2024
1 parent b140544 commit 282a50a
Show file tree
Hide file tree
Showing 72 changed files with 814 additions and 356 deletions.
12 changes: 10 additions & 2 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,17 @@ jobs:

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::lintr, local::.
extra-packages: local::., any::lintr, any::devtools, any::testthat
needs: lint

- name: Lint
run: lintr::lint_package()
shell: Rscript {0}
run: |
Sys.setlocale(locale = "C")
devtools::load_all() # helps with object usage linter
package_lints <- devtools::lint(cache = FALSE)
# Assert that there are no lint errors
if (length(package_lints) > 0) print(package_lints)
testthat::expect_length(package_lints, 0)
10 changes: 9 additions & 1 deletion .lintr
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
linters: linters_with_defaults(
line_length_linter = line_length_linter(100)
line_length_linter = line_length_linter(120L),
object_name_linter = object_name_linter(styles = c("snake_case", "symbols", "CamelCase")),
cyclocomp_linter = cyclocomp_linter(30L),
object_length_linter(32L),
indentation_linter = indentation_linter(hanging_indent_style = "tidy")
)
exclusions: list(
"tests/testthat/2024.08.0/",
"tests/testthat/2024.09.0/"
)
2 changes: 0 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ Suggests:
withr
VignetteBuilder:
knitr
RdMacros:
lifecycle
Encoding: UTF-8
Language: en-US
RoxygenNote: 7.3.2
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export(vanity_is_available)
export(variant_render)
export(verify_content_name)
importFrom(lifecycle,deprecate_warn)
importFrom(lifecycle,deprecated)
importFrom(magrittr,"%>%")
importFrom(rlang,"%||%")
importFrom(rlang,":=")
Expand Down
13 changes: 8 additions & 5 deletions R/audits.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#' Check to see if a vanity URL is currently in use
#'
#' \lifecycle{experimental}
#' `r lifecycle::badge('experimental')`
#'
#' @param connect A Connect R6 object
#' @param vanity string of the vanity URL to check
Expand All @@ -23,7 +23,7 @@ vanity_is_available <- function(connect, vanity) {

#' Audit R Versions
#'
#' \lifecycle{experimental}
#' `r lifecycle::badge('experimental')`
#'
#' @param content `data.frame` of content information, as from [get_content()]
#'
Expand Down Expand Up @@ -53,7 +53,10 @@ audit_r_versions <- function(content) {

# timeline
p2 <- ggplot2::ggplot(timeline) +
ggplot2::geom_point(pch = 4, ggplot2::aes(x = last_deployed_time, color = r_version, y = r_version)) +
ggplot2::geom_point(
pch = 4,
ggplot2::aes(x = last_deployed_time, color = r_version, y = r_version)
) +
ggplot2::theme_minimal() +
ggplot2::labs(
title = "Content by Time",
Expand All @@ -67,7 +70,7 @@ audit_r_versions <- function(content) {

#' Audit Run As Settings
#'
#' \lifecycle{experimental}
#' `r lifecycle::badge('experimental')`
#'
#' @param content `data.frame` of content information, as from [get_content()]
#'
Expand All @@ -85,7 +88,7 @@ audit_runas <- function(content) {
# type can be all, logged_in, acl
#' Audit Access Controls
#'
#' \lifecycle{experimental}
#' `r lifecycle::badge('experimental')`
#'
#' @param content `data.frame` of content information, as from [get_content()]
#' @param type One of "all" or "logged_in". If "all", return a list of apps
Expand Down
12 changes: 10 additions & 2 deletions R/connect.R
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,15 @@ Connect <- R6::R6Class(
print = function(...) {
cat("Posit Connect API Client: \n")
cat(" Posit Connect Server: ", self$server, "\n", sep = "")
cat(" Posit Connect API Key: ", paste0(strrep("*", 11), substr(self$api_key, nchar(self$api_key) - 3, nchar(self$api_key))), "\n", sep = "")
cat(
" Posit Connect API Key: ",
paste0(
strrep("*", 11),
substr(self$api_key, nchar(self$api_key) - 3, nchar(self$api_key))
),
"\n",
sep = ""
)
# TODO: something about API key... role... ?
# TODO: point to docs on methods... how to see methods?
cat("\n")
Expand Down Expand Up @@ -358,7 +366,7 @@ Connect <- R6::R6Class(
count = min(page_size, .limit)
)
if (!is.null(filter)) {
query$filter <- paste(sapply(1:length(filter), function(i) {
query$filter <- paste(sapply(seq_along(filter), function(i) {
sprintf("%s:%s", names(filter)[i], filter[[i]])
}), collapse = .collapse)
}
Expand Down
7 changes: 7 additions & 0 deletions R/connectapi-package.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#' @keywords internal
"_PACKAGE"

## usethis namespace: start
#' @importFrom lifecycle deprecated
## usethis namespace: end
NULL
78 changes: 52 additions & 26 deletions R/content.R
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ Content <- R6::R6Class(
#' @param bundle_id The bundle identifer.
#' @param filename Where to write the result.
#' @param overwrite Overwrite an existing filename.
bundle_download = function(bundle_id, filename = tempfile(pattern = "bundle", fileext = ".tar.gz"), overwrite = FALSE) {
bundle_download = function(
bundle_id,
filename = tempfile(pattern = "bundle", fileext = ".tar.gz"),
overwrite = FALSE
) {
url <- v1_url("content", self$get_content()$guid, "bundles", bundle_id, "download")
self$get_connect()$GET(url, httr::write_disk(filename, overwrite = overwrite), parser = "raw")
return(filename)
Expand Down Expand Up @@ -285,7 +289,7 @@ Content <- R6::R6Class(
is_rendered = function() {
self$content$app_mode %in% c("rmd-static", "jupyter-static", "quarto-static")
},

#' @field is_interactive TRUE if this is a rendered content type, otherwise FALSE.
is_interactive = function() {
interactive_app_modes <- c(
Expand Down Expand Up @@ -513,7 +517,14 @@ content_title <- function(connect, guid, default = "Unknown Content") {
}

#' @importFrom uuid UUIDgenerate
content_ensure <- function(connect, name = uuid::UUIDgenerate(), title = name, guid = NULL, ..., .permitted = c("new", "existing")) {
content_ensure <- function(
connect,
name = uuid::UUIDgenerate(),
title = name,
guid = NULL,
...,
.permitted = c("new", "existing")
) {
if (!is.null(guid)) {
# guid-based deployment
# just in case we get a 404 back...
Expand Down Expand Up @@ -577,7 +588,7 @@ content_ensure <- function(connect, name = uuid::UUIDgenerate(), title = name, g

#' Get Jobs
#'
#' \lifecycle{experimental} Retrieve details about jobs associated with a `content_item`.
#' `r lifecycle::badge('experimental')` Retrieve details about jobs associated with a `content_item`.
#' "Jobs" in Posit Connect are content executions
#'
#' @param content A Content object, as returned by `content_item()`
Expand Down Expand Up @@ -708,7 +719,7 @@ content_delete <- function(content, force = FALSE) {
content_update <- function(content, ...) {
validate_R6_class(content, "Content")

res <- content$update(...)
content$update(...)

content$get_content_remote()

Expand Down Expand Up @@ -747,7 +758,10 @@ content_update_owner <- function(content, owner_guid) {
#' @export
verify_content_name <- function(name) {
if (grepl("[^\\-\\_a-zA-Z0-9]", name, perl = TRUE) || nchar(name) < 3 || nchar(name) > 64) {
stop(glue::glue("ERROR: content name '{name}' must be between 3 and 64 alphanumeric characters, dashes, and underscores"))
stop(glue::glue(
"ERROR: content name '{name}' must be between 3 and 64 alphanumeric characters, ",
"dashes, and underscores"
))
}
return(name)
}
Expand Down Expand Up @@ -831,7 +845,7 @@ content_add_user <- function(content, guid, role = c("viewer", "owner")) {
validate_R6_class(content, "Content")
role <- .define_role(role)

res <- purrr::map(guid, ~ .content_add_permission_impl(content, "user", .x, role))
purrr::map(guid, ~ .content_add_permission_impl(content, "user", .x, role))

return(content)
}
Expand All @@ -840,10 +854,9 @@ content_add_user <- function(content, guid, role = c("viewer", "owner")) {
#' @export
content_add_group <- function(content, guid, role = c("viewer", "owner")) {
validate_R6_class(content, "Content")
existing <- .get_permission(content, "group", guid)
role <- .define_role(role)

res <- purrr::map(guid, ~ .content_add_permission_impl(content = content, type = "group", guid = .x, role = role))
purrr::map(guid, ~ .content_add_permission_impl(content = content, type = "group", guid = .x, role = role))

return(content)
}
Expand Down Expand Up @@ -885,15 +898,15 @@ content_add_group <- function(content, guid, role = c("viewer", "owner")) {
#' @export
content_delete_user <- function(content, guid) {
validate_R6_class(content, "Content")
res <- purrr::map(guid, ~ .content_delete_permission_impl(content = content, type = "user", guid = .x))
purrr::map(guid, ~ .content_delete_permission_impl(content = content, type = "user", guid = .x))
return(content)
}

#' @rdname permissions
#' @export
content_delete_group <- function(content, guid) {
validate_R6_class(content, "Content")
res <- purrr::map(guid, ~ .content_delete_permission_impl(content = content, type = "group", guid = .x))
purrr::map(guid, ~ .content_delete_permission_impl(content = content, type = "group", guid = .x))
return(content)
}

Expand Down Expand Up @@ -957,33 +970,36 @@ get_content_permissions <- function(content, add_owner = TRUE) {
}

#' Render a content item.
#'
#'
#' @description Submit a request to render a content item. Once submitted, the
#' server runs an asynchronous process to render the content. This might be
#' useful if content needs to be updated after its source data has changed,
#' especially if this doesn't happen on a regular schedule.
#'
#'
#' Only valid for rendered content (e.g., most Quarto documents, Jupyter
#' notebooks, R Markdown reports).
#'
#'
#' @param content The content item you wish to render.
#' @param variant_key If a variant key is provided, render that variant. Otherwise, render the default variant.
#' @return A [VariantTask] object that can be used to track completion of the render.
#'
#'
#' @examples
#' \dontrun{
#' client <- connect()
#' item <- content_item(client, "951bf3ad-82d0-4bca-bba8-9b27e35c49fa")
#' task <- content_render(item)
#' poll_task(task)
#' }
#'
#'
#' @export
content_render <- function(content, variant_key = NULL) {
scoped_experimental_silence()
validate_R6_class(content, "Content")
if (!content$is_rendered) {
stop(glue::glue("Render not supported for application mode: {content$content$app_mode}. Did you mean content_restart()?"))
stop(glue::glue(
"Render not supported for application mode: {content$content$app_mode}. ",
"Did you mean content_restart()?"
))
}
if (is.null(variant_key)) {
target_variant <- get_variant(content, "default")
Expand All @@ -992,42 +1008,52 @@ content_render <- function(content, variant_key = NULL) {
}
render_task <- target_variant$render()

VariantTask$new(connect = content$connect, content = content$content, key = target_variant$key, task = render_task)
VariantTask$new(
connect = content$connect,
content = content$content,
key = target_variant$key,
task = render_task
)
}

#' Restart a content item.
#'
#'
#' @description Submit a request to restart a content item. Once submitted, the
#' server performs an asynchronous request to kill all processes associated with
#' the content item, starting new processes as needed. This might be useful if
#' the application relies on data that is loaded at startup, or if its memory
#' usage has grown over time.
#'
#'
#' Note that users interacting with certain types of applications may have their
#' workflows interrupted.
#'
#'
#' Only valid for interactive content (e.g., applications, APIs).
#'
#'
#' @param content The content item you wish to restart.
#'
#'
#' @examples
#' \dontrun{
#' client <- connect()
#' item <- content_item(client, "8f37d6e0-3395-4a2c-aa6a-d7f2fe1babd0")
#' content_restart(item)
#' }
#'
#'
#' @importFrom rlang :=
#' @export
content_restart <- function(content) {
validate_R6_class(content, "Content")
if (!content$is_interactive) {
stop(glue::glue("Restart not supported for application mode: {content$content$app_mode}. Did you mean content_render()?"))
stop(glue::glue(
"Restart not supported for application mode: {content$content$app_mode}. ",
"Did you mean content_render()?"
))
}
unix_epoch_in_seconds <- as.integer(Sys.time())
env_var_name <- glue::glue("_CONNECT_RESTART_{unix_epoch_in_seconds}")
# nolint start: object_usage_linter, object_name_linter
# https://rlang.r-lib.org/reference/glue-operators.html#using-glue-syntax-in-packages
env_var_name <- glue::glue("_CONNECT_RESTART_{unix_epoch_in_seconds}")
content$environment_set("{env_var_name}" := unix_epoch_in_seconds)
content$environment_set("{env_var_name}" := NA)
# nolint end
invisible(NULL)
}
20 changes: 16 additions & 4 deletions R/deploy.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ Bundle <- R6::R6Class(
self$path <- path
self$size <- fs::file_size(path = path)
if (fs::file_exists(path) && self$size > fs::as_fs_bytes(max_bundle_size)) {
warning(glue::glue("Bundle size is greater than {max_bundle_size}. Please ensure your bundle is not including too much."))
warning(glue::glue(
"Bundle size is greater than {max_bundle_size}. ",
"Please ensure your bundle is not including too much."
))
}
},

Expand Down Expand Up @@ -302,7 +305,12 @@ bundle_path <- function(path) {
#'
#' @family deployment functions
#' @export
download_bundle <- function(content, filename = fs::file_temp(pattern = "bundle", ext = ".tar.gz"), bundle_id = NULL, overwrite = FALSE) {
download_bundle <- function(
content,
filename = fs::file_temp(pattern = "bundle", ext = ".tar.gz"),
bundle_id = NULL,
overwrite = FALSE
) {
validate_R6_class(content, "Content")

from_content <- content$get_content_remote()
Expand Down Expand Up @@ -345,7 +353,8 @@ download_bundle <- function(content, filename = fs::file_temp(pattern = "bundle"
#' @param title optional The title to be used for the content on the server
#' @param guid optional The GUID if the content already exists on the server
#' @param ... Additional arguments passed along to the content creation
#' @param .pre_deploy An expression to execute before deploying the new bundle. The variables `content` and `bundle_id` are supplied
#' @param .pre_deploy An expression to execute before deploying the new bundle.
#' The variables `content` and `bundle_id` are supplied
#' @param content A Content object
#'
#' @return Task A task object
Expand Down Expand Up @@ -382,7 +391,10 @@ deploy <- function(connect, bundle, name = create_random_name(), title = name, g
new_bundle_id <- con$content_upload(bundle_path = bundle$path, guid = content$guid)[["id"]]

pre_deploy_expr <- rlang::enexpr(.pre_deploy)
rlang::eval_bare(pre_deploy_expr, env = rlang::env(content = content_item(con, content$guid), bundle_id = new_bundle_id))
rlang::eval_bare(
pre_deploy_expr,
env = rlang::env(content = content_item(con, content$guid), bundle_id = new_bundle_id)
)

message("Deploying bundle")
# deploy
Expand Down
Loading

0 comments on commit 282a50a

Please sign in to comment.