diff --git a/NEWS.md b/NEWS.md index 73c129872..e4ebb3786 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,6 +14,7 @@ on the Connect server. (#272, #447) - New `lock_content()` and `unlock_content()` functions for locking and unlocking content items. (#453) +- Updated git-backed deployment functions to use v1 APIs (#459) # connectapi 0.8.0 diff --git a/R/content.R b/R/content.R index f3a1eef6d..794506c26 100644 --- a/R/content.R +++ b/R/content.R @@ -63,11 +63,6 @@ Content <- R6::R6Class( url <- v1_url("content", self$content$guid, "bundles", bundle_id) self$connect$DELETE(url) }, - #' @description Get this (remote) content item. - internal_content = function() { - url <- unversioned_url("applications", self$content$guid) - self$connect$GET(url) - }, #' @description Update this content item. #' @param ... Content fields. update = function(...) { @@ -161,18 +156,13 @@ Content <- R6::R6Class( #' @param key The job key. job = function(key) { warn_experimental("job") - url <- unversioned_url( - "applications", - self$content$guid, - "job", - key - ) + guid <- self$content$guid + url <- unversioned_url("applications", guid, "job", key) res <- self$connect$GET(url) - content_guid <- self$content$guid purrr::map( list(res), - ~ purrr::list_modify(.x, app_guid = content_guid) + ~ purrr::list_modify(.x, app_guid = guid) )[[1]] }, #' @description Terminate a single job for this content item. @@ -189,11 +179,8 @@ Content <- R6::R6Class( #' @description Return the variants for this content. variants = function() { warn_experimental("variants") - url <- unversioned_url( - "applications", - self$content$guid, - "variants" - ) + guid <- self$content$guid + url <- unversioned_url("applications", guid, "variants") self$connect$GET(url) }, #' @description Set a tag for this content. @@ -330,29 +317,60 @@ Content <- R6::R6Class( body = body ) }, + #' @description Get Git repository details + #' @return NULL if no repo is set, otherwise a list with fields: + #' - repository + #' - branch + #' - directory + #' - polling + #' - last_error + #' - last_known_commit + repository = function() { + con <- self$connect + error_if_less_than(con$version, "2022.10.0") + guid <- self$content$guid + resp <- con$GET( + v1_url("content", guid, "repository"), + parser = NULL + ) + if (httr::status_code(resp) == 404) { + # 404 means there is no repository set + return(NULL) + } + con$raise_error(resp) + httr::content(resp, as = "parsed") + }, #' @description Adjust Git polling. - #' @param enabled Polling enabled. - repo_enable = function(enabled = TRUE) { - warn_experimental("repo_enable") - self$connect$PUT( - unversioned_url("applications", self$content$guid, "repo"), - body = list( - enabled = enabled - ) + #' @param polling Polling enabled. + repo_enable = function(polling = TRUE) { + con <- self$connect + error_if_less_than(con$version, "2022.10.0") + guid <- self$content$guid + con$PATCH( + v1_url("content", guid, "repository"), + body = list(polling = polling) ) }, #' @description Adjust Git repository #' @param repository Git repository URL #' @param branch Git repository branch - #' @param subdirectory Git repository directory - repo_set = function(repository, branch, subdirectory) { - warn_experimental("repo_set") - self$connect$POST( - unversioned_url("applications", self$content$guid, "repo"), + #' @param directory Git repository directory + #' @param polling Whether to check for updates + repo_set = function( + repository, + branch = "main", + directory = ".", + polling = FALSE + ) { + guid <- self$content$guid + error_if_less_than(self$connect$version, "2022.10.0") + self$connect$PUT( + v1_url("content", guid, "repository"), body = list( repository = repository, branch = branch, - subdirectory = subdirectory + directory = directory, + polling = polling ) ) }, diff --git a/R/git.R b/R/git.R index 4837299c1..6c8af65c6 100644 --- a/R/git.R +++ b/R/git.R @@ -117,7 +117,6 @@ deploy_repo <- function( ... ) { validate_R6_class(client, "Connect") - warn_experimental("deploy_repo") content_metadata <- content_ensure( connect = client, @@ -131,7 +130,7 @@ deploy_repo <- function( deployed_content$repo_set( repository = repository, branch = branch, - subdirectory = subdirectory + directory = subdirectory ) task <- deployed_content$deploy() @@ -143,48 +142,36 @@ deploy_repo <- function( #' @export deploy_repo_enable <- function(content, enabled = TRUE) { validate_R6_class(content, "Content") - warn_experimental("deploy_repo_enable") - invisible(content$repo_enable(enabled)) - invisible(content$get_content_remote()) - return(content) + content$repo_enable(enabled) + content$get_content_remote() + content } #' @rdname deploy_repo #' @export deploy_repo_update <- function(content) { validate_R6_class(content, "Content") - warn_experimental("deploy_repo_update") - scoped_experimental_silence() con <- content$connect - internal_meta <- content$internal_content() - repo_data <- tryCatch( - { - internal_meta$git - }, - error = function(e) { - message(e) - return(NULL) - } - ) + repo_data <- content$repository() if (is.null(repo_data)) { stop(glue::glue( - "Content item '{internal_meta$guid}' is not git-backed content" + "Content item '{content$content$guid}' is not git-backed content" )) } - branch_status <- repo_check_branches_ref(con, repo_data$repository_url) + branch_status <- repo_check_branches_ref(con, repo_data$repository) if (!repo_data$branch %in% names(branch_status)) { stop(glue::glue( - "Branch '{repo_data$branch}' was no longer found on repository '{repo_data$repository_url}'" + "Branch '{repo_data$branch}' was no longer found on repository '{repo_data$repository}'" )) } if ( identical(repo_data$last_known_commit, branch_status[[repo_data$branch]]) ) { message(glue::glue( - "No changes were found in the Git repository: {repo_data$repository_url}@{repo_data$branch}" + "No changes were found in the Git repository: {repo_data$repository}@{repo_data$branch}" )) return(content) } diff --git a/R/variant.R b/R/variant.R index a526f3946..17cb55d19 100644 --- a/R/variant.R +++ b/R/variant.R @@ -87,12 +87,8 @@ Variant <- R6::R6Class( #' @param guid User GUID. remove_subscriber = function(guid) { warn_experimental("subscribers") - path <- unversioned_url( - "variants", - self$variant$id, - "subscribers", - guid - ) + id <- self$variant$id + path <- unversioned_url("variants", id, "subscribers", guid) self$connect$DELETE(path) }, #' @description Add named subscribers. diff --git a/man/Content.Rd b/man/Content.Rd index b2d9656b1..c10a910aa 100644 --- a/man/Content.Rd +++ b/man/Content.Rd @@ -52,7 +52,6 @@ Other R6 classes: \item \href{#method-Content-get_bundles}{\code{Content$get_bundles()}} \item \href{#method-Content-bundle_download}{\code{Content$bundle_download()}} \item \href{#method-Content-bundle_delete}{\code{Content$bundle_delete()}} -\item \href{#method-Content-internal_content}{\code{Content$internal_content()}} \item \href{#method-Content-update}{\code{Content$update()}} \item \href{#method-Content-danger_delete}{\code{Content$danger_delete()}} \item \href{#method-Content-get_url}{\code{Content$get_url()}} @@ -72,6 +71,7 @@ Other R6 classes: \item \href{#method-Content-environment_set}{\code{Content$environment_set()}} \item \href{#method-Content-environment_all}{\code{Content$environment_all()}} \item \href{#method-Content-deploy}{\code{Content$deploy()}} +\item \href{#method-Content-repository}{\code{Content$repository()}} \item \href{#method-Content-repo_enable}{\code{Content$repo_enable()}} \item \href{#method-Content-repo_set}{\code{Content$repo_set()}} \item \href{#method-Content-packages}{\code{Content$packages()}} @@ -159,16 +159,6 @@ Delete a content bundle. } \if{html}{\out{}} } -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Content-internal_content}{}}} -\subsection{Method \code{internal_content()}}{ -Get this (remote) content item. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Content$internal_content()}\if{html}{\out{
}} -} - } \if{html}{\out{
}} \if{html}{\out{}} @@ -465,18 +455,39 @@ Deploy this content } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-Content-repository}{}}} +\subsection{Method \code{repository()}}{ +Get Git repository details +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{Content$repository()}\if{html}{\out{
}} +} + +\subsection{Returns}{ +NULL if no repo is set, otherwise a list with fields: +\itemize{ +\item repository +\item branch +\item directory +\item polling +\item last_error +\item last_known_commit +} +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-Content-repo_enable}{}}} \subsection{Method \code{repo_enable()}}{ Adjust Git polling. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Content$repo_enable(enabled = TRUE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Content$repo_enable(polling = TRUE)}\if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ -\item{\code{enabled}}{Polling enabled.} +\item{\code{polling}}{Polling enabled.} } \if{html}{\out{
}} } @@ -487,7 +498,7 @@ Adjust Git polling. \subsection{Method \code{repo_set()}}{ Adjust Git repository \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Content$repo_set(repository, branch, subdirectory)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Content$repo_set(repository, branch = "main", directory = ".", polling = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -497,7 +508,9 @@ Adjust Git repository \item{\code{branch}}{Git repository branch} -\item{\code{subdirectory}}{Git repository directory} +\item{\code{directory}}{Git repository directory} + +\item{\code{polling}}{Whether to check for updates} } \if{html}{\out{}} } diff --git a/man/ContentTask.Rd b/man/ContentTask.Rd index f83bff856..fa71fc342 100644 --- a/man/ContentTask.Rd +++ b/man/ContentTask.Rd @@ -61,7 +61,6 @@ Other R6 classes:
  • connectapi::Content$get_content_remote()
  • connectapi::Content$get_dashboard_url()
  • connectapi::Content$get_url()
  • -
  • connectapi::Content$internal_content()
  • connectapi::Content$job()
  • connectapi::Content$jobs()
  • connectapi::Content$packages()
  • @@ -72,6 +71,7 @@ Other R6 classes:
  • connectapi::Content$register_job_kill_order()
  • connectapi::Content$repo_enable()
  • connectapi::Content$repo_set()
  • +
  • connectapi::Content$repository()
  • connectapi::Content$tag_delete()
  • connectapi::Content$tag_set()
  • connectapi::Content$tags()
  • diff --git a/man/EnvironmentR6.Rd b/man/EnvironmentR6.Rd index f179e4dae..5fb5ceeda 100644 --- a/man/EnvironmentR6.Rd +++ b/man/EnvironmentR6.Rd @@ -59,7 +59,6 @@ Other R6 classes:
  • connectapi::Content$get_content_remote()
  • connectapi::Content$get_dashboard_url()
  • connectapi::Content$get_url()
  • -
  • connectapi::Content$internal_content()
  • connectapi::Content$job()
  • connectapi::Content$jobs()
  • connectapi::Content$packages()
  • @@ -70,6 +69,7 @@ Other R6 classes:
  • connectapi::Content$register_job_kill_order()
  • connectapi::Content$repo_enable()
  • connectapi::Content$repo_set()
  • +
  • connectapi::Content$repository()
  • connectapi::Content$tag_delete()
  • connectapi::Content$tag_set()
  • connectapi::Content$tags()
  • diff --git a/man/Vanity.Rd b/man/Vanity.Rd index 80a1cdc66..a4f871292 100644 --- a/man/Vanity.Rd +++ b/man/Vanity.Rd @@ -57,7 +57,6 @@ Other R6 classes:
  • connectapi::Content$get_content_remote()
  • connectapi::Content$get_dashboard_url()
  • connectapi::Content$get_url()
  • -
  • connectapi::Content$internal_content()
  • connectapi::Content$job()
  • connectapi::Content$jobs()
  • connectapi::Content$packages()
  • @@ -68,6 +67,7 @@ Other R6 classes:
  • connectapi::Content$register_job_kill_order()
  • connectapi::Content$repo_enable()
  • connectapi::Content$repo_set()
  • +
  • connectapi::Content$repository()
  • connectapi::Content$tag_delete()
  • connectapi::Content$tag_set()
  • connectapi::Content$tags()
  • diff --git a/man/VariantR6.Rd b/man/VariantR6.Rd index cdf922d69..b8ffe18a9 100644 --- a/man/VariantR6.Rd +++ b/man/VariantR6.Rd @@ -71,7 +71,6 @@ Other R6 classes:
  • connectapi::Content$environment_set()
  • connectapi::Content$get_bundles()
  • connectapi::Content$get_content_remote()
  • -
  • connectapi::Content$internal_content()
  • connectapi::Content$packages()
  • connectapi::Content$permissions()
  • connectapi::Content$permissions_add()
  • @@ -80,6 +79,7 @@ Other R6 classes:
  • connectapi::Content$register_job_kill_order()
  • connectapi::Content$repo_enable()
  • connectapi::Content$repo_set()
  • +
  • connectapi::Content$repository()
  • connectapi::Content$tag_delete()
  • connectapi::Content$tag_set()
  • connectapi::Content$tags()
  • diff --git a/man/VariantSchedule.Rd b/man/VariantSchedule.Rd index d7c23e299..b813ab11b 100644 --- a/man/VariantSchedule.Rd +++ b/man/VariantSchedule.Rd @@ -62,7 +62,6 @@ Other R6 classes:
  • connectapi::Content$environment_set()
  • connectapi::Content$get_bundles()
  • connectapi::Content$get_content_remote()
  • -
  • connectapi::Content$internal_content()
  • connectapi::Content$packages()
  • connectapi::Content$permissions()
  • connectapi::Content$permissions_add()
  • @@ -71,6 +70,7 @@ Other R6 classes:
  • connectapi::Content$register_job_kill_order()
  • connectapi::Content$repo_enable()
  • connectapi::Content$repo_set()
  • +
  • connectapi::Content$repository()
  • connectapi::Content$tag_delete()
  • connectapi::Content$tag_set()
  • connectapi::Content$tags()
  • diff --git a/man/VariantTask.Rd b/man/VariantTask.Rd index 316a53a1d..13d264852 100644 --- a/man/VariantTask.Rd +++ b/man/VariantTask.Rd @@ -59,7 +59,6 @@ Other R6 classes:
  • connectapi::Content$environment_set()
  • connectapi::Content$get_bundles()
  • connectapi::Content$get_content_remote()
  • -
  • connectapi::Content$internal_content()
  • connectapi::Content$packages()
  • connectapi::Content$permissions()
  • connectapi::Content$permissions_add()
  • @@ -68,6 +67,7 @@ Other R6 classes:
  • connectapi::Content$register_job_kill_order()
  • connectapi::Content$repo_enable()
  • connectapi::Content$repo_set()
  • +
  • connectapi::Content$repository()
  • connectapi::Content$tag_delete()
  • connectapi::Content$tag_set()
  • connectapi::Content$tags()
  • diff --git a/tests/integrated/test-git.R b/tests/integrated/test-git.R index 3a6cdb362..db5f98778 100644 --- a/tests/integrated/test-git.R +++ b/tests/integrated/test-git.R @@ -1,3 +1,5 @@ +skip_if_connect_older_than(test_conn_1, "2022.10.0") + cont1_name <- uuid::UUIDgenerate() cont1_title <- "Test Content 1" cont1_guid <- NULL @@ -106,13 +108,17 @@ test_that("deploy_repo_enable works", { expect_true(validate_R6_class(cont1, "Content")) expect_true(validate_R6_class(cont1, "ContentTask")) + polling_enabled <- function(content) { + git <- content$repository() + isTRUE(git$polling) + } # TODO: flaky... how to be safer? Sys.sleep(5) # sleep for deployment...? - expect_true(cont1$internal_content()$git$enabled) + expect_true(polling_enabled(cont1)) res <- deploy_repo_enable(cont1, FALSE) - expect_false(cont1$internal_content()$git$enabled) + expect_false(polling_enabled(cont1)) res <- deploy_repo_enable(cont1, TRUE) - expect_true(cont1$internal_content()$git$enabled) + expect_true(polling_enabled(cont1)) }) test_that("deploy_repo_update works", { diff --git a/tests/testthat/2024.08.0/__api__/v1/content/c3426b0b/README.md b/tests/testthat/2024.08.0/__api__/v1/content/c3426b0b/README.md index 7c6442bb3..2082a7995 100644 --- a/tests/testthat/2024.08.0/__api__/v1/content/c3426b0b/README.md +++ b/tests/testthat/2024.08.0/__api__/v1/content/c3426b0b/README.md @@ -1 +1,3 @@ -This content item returns 403 errors when setting and deleting vanity URLs. \ No newline at end of file +This content item returns 403 errors when setting and deleting vanity URLs. + +It is also not git-backed, so repository is a 404 diff --git a/tests/testthat/2024.08.0/__api__/v1/content/c3426b0b/repository.R b/tests/testthat/2024.08.0/__api__/v1/content/c3426b0b/repository.R new file mode 100644 index 000000000..d0dcf2fdf --- /dev/null +++ b/tests/testthat/2024.08.0/__api__/v1/content/c3426b0b/repository.R @@ -0,0 +1,10 @@ +structure( + list( + url = "__api__/v1/content/c3426b0b/repository", + status_code = 404L, + content = charToRaw( + "{}" + ) + ), + class = "response" +) diff --git a/tests/testthat/2024.08.0/__api__/v1/content/f2f37341/repository.json b/tests/testthat/2024.08.0/__api__/v1/content/f2f37341/repository.json new file mode 100644 index 000000000..e38100310 --- /dev/null +++ b/tests/testthat/2024.08.0/__api__/v1/content/f2f37341/repository.json @@ -0,0 +1,8 @@ +{ + "repository": "https://github.com/dbkegley/rsc-sample-content", + "branch": "main", + "directory": "local/python-shiny", + "polling": true, + "last_error": "thread caused non-unwinding panic. aborting. (signal: aborted (core dumped))", + "last_known_commit": "5e69ff238376182fa7ed38b30497d457684c6e24" +} diff --git a/tests/testthat/test-git.R b/tests/testthat/test-git.R index 2e3e0c257..d2f365eb1 100644 --- a/tests/testthat/test-git.R +++ b/tests/testthat/test-git.R @@ -37,3 +37,38 @@ without_internet({ ) }) }) + +with_mock_api({ + test_that("we can retrieve a repository information if it exists", { + con <- Connect$new(server = "https://connect.example", api_key = "fake") + item <- content_item(con, "f2f37341-e21d-3d80-c698-a935ad614066") + expect_true(item$repository()$polling) + }) + + test_that("repository is null if it is not set", { + con <- Connect$new(server = "https://connect.example", api_key = "fake") + item <- content_item(con, "c3426b0b-e21d-3d80-c698-a935ad614066") + expect_null(item$repository()) + }) + + test_that("we can set a repository", { + con <- Connect$new(server = "https://connect.example", api_key = "fake") + item <- content_item(con, "c3426b0b-e21d-3d80-c698-a935ad614066") + expect_PUT( + item$repo_set(repository = "https://github.com/posit-dev/connectapi"), + "https://connect.example/__api__/v1/content/c3426b0b/repository", + '{"repository":"https://github.com/posit-dev/connectapi",', + '"branch":"main","directory":".","polling":false}' + ) + }) + + test_that("we can enable polling", { + con <- Connect$new(server = "https://connect.example", api_key = "fake") + item <- content_item(con, "f2f37341-e21d-3d80-c698-a935ad614066") + expect_PATCH( + item$repo_enable(TRUE), + "https://connect.example/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/repository", + '{"polling":true}' + ) + }) +})