From 1b318a413da19b2bf3b580595134311e50fc8763 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Mon, 18 Nov 2024 18:10:40 -0500 Subject: [PATCH 01/15] add functionality --- R/connect.R | 20 ++++++++++++++++++-- R/get.R | 31 +++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/R/connect.R b/R/connect.R index 22e21184..21408b05 100644 --- a/R/connect.R +++ b/R/connect.R @@ -506,12 +506,28 @@ Connect <- R6::R6Class( #' @param page_number The page number. #' @param prefix The search term. #' @param page_size The page size. - users = function(page_number = 1, prefix = NULL, page_size = 500) { + #' @param user_role Filter by user role. + #' @param account_status Filter by account status. + users = function( + page_number = 1, + prefix = NULL, + page_size = 500, + user_role = NULL, + account_status = NULL + ) { path <- v1_url("users") + if (!is.null(user_role)) { + user_role <- paste(user_role, collapse = "|") + } + if (!is.null(account_status)) { + account_status <- paste(account_status, collapse = "|") + } query <- list( page_number = page_number, page_size = valid_page_size(page_size), - prefix = prefix + prefix = prefix, + user_role = user_role, + account_status = account_status ) self$GET(path, query = query) }, diff --git a/R/get.R b/R/get.R index ce85e68b..e63934b0 100644 --- a/R/get.R +++ b/R/get.R @@ -5,6 +5,10 @@ #' @param prefix Filters users by prefix (username, first name, or last name). #' The filter is case insensitive. #' @param limit The max number of records to return +#' @param user_role Filter by user role ("administrator", "publisher", +#' "viewer"). Pass in a vector of multiple roles to match any value. +#' @param account_status Filter by account status ("locked", "licensed", +#' "inactive"). Pass a vector of multiple statuses to match any value. #' #' @return #' A tibble with the following columns: @@ -34,17 +38,36 @@ #' library(connectapi) #' client <- connect() #' -#' # get all users -#' get_users(client, limit = Inf) +#' # Get all users +#' get_users(client) +#' +#' # Get all licensed users +#' get_users(client, account_status = "licensed") +#' +#' # Get all users who are administrators or publishers +#' get_users(client, user_role = c("administrator", "publisher")) #' } #' #' @export -get_users <- function(src, page_size = 500, prefix = NULL, limit = Inf) { +get_users <- function( + src, + page_size = 500, + prefix = NULL, + limit = Inf, + page_number = NULL, + user_role = NULL, + account_status = NULL +) { validate_R6_class(src, "Connect") res <- page_offset( src, - src$users(page_size = page_size, prefix = prefix), + src$users( + page_size = page_size, + prefix = prefix, + user_role = user_role, + account_status = account_status + ), limit = limit ) From 5b6bac41e4cec8a1fe2fe4dcb4881ad66ed0b4d0 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Tue, 19 Nov 2024 14:04:02 -0500 Subject: [PATCH 02/15] integration tests --- tests/integrated/test-get.R | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/integrated/test-get.R b/tests/integrated/test-get.R index b1bca147..5776c9cc 100644 --- a/tests/integrated/test-get.R +++ b/tests/integrated/test-get.R @@ -14,6 +14,12 @@ test_that("get_users works", { purrr::map_chr(vctrs::vec_ptype(users), typeof), purrr::map_chr(vctrs::vec_ptype(connectapi_ptypes$users), typeof) ) + + publishers <- get_users(test_conn_1, user_role = "publisher") + expect_equal(nrow(publishers), 0) + + locked <- get_users(test_conn_1, account_status = "locked") + expect_equal(nrow(publishers), 0) }) test_that("get_groups works", { From 2189156a402e8276e7666d8bf087de449ac0d4af Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Tue, 19 Nov 2024 15:16:00 -0500 Subject: [PATCH 03/15] better test failure messages hopefully --- tests/integrated/test-get.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integrated/test-get.R b/tests/integrated/test-get.R index 5776c9cc..d24b2d64 100644 --- a/tests/integrated/test-get.R +++ b/tests/integrated/test-get.R @@ -16,10 +16,10 @@ test_that("get_users works", { ) publishers <- get_users(test_conn_1, user_role = "publisher") - expect_equal(nrow(publishers), 0) + expect_equal(nrow(publishers), 0, label = capture.output(print(publishers))) locked <- get_users(test_conn_1, account_status = "locked") - expect_equal(nrow(publishers), 0) + expect_equal(nrow(publishers), 0, label = capture.output(print(publishers))) }) test_that("get_groups works", { From 127a06388aedbb8dd807afcedcb439d5a61d2847 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Tue, 19 Nov 2024 15:33:11 -0500 Subject: [PATCH 04/15] update integration tests, write news --- .github/workflows/integration-tests.yaml | 1 + DESCRIPTION | 2 +- NEWS.md | 8 ++++++++ tests/integrated/test-get.R | 8 ++++---- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/integration-tests.yaml b/.github/workflows/integration-tests.yaml index 23e740ad..0cb5330e 100644 --- a/.github/workflows/integration-tests.yaml +++ b/.github/workflows/integration-tests.yaml @@ -15,6 +15,7 @@ jobs: fail-fast: false matrix: version: + - "2024.10.0" # jammy - "2024.03.0" # jammy - "2023.09.0" # jammy - "2023.03.0" # bionic diff --git a/DESCRIPTION b/DESCRIPTION index 6b6220fa..d1512cb4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: connectapi Title: Utilities for Interacting with the 'Posit Connect' Server API -Version: 0.4.0 +Version: 0.4.0.9000 Authors@R: c( person(given = "Toph", family = "Allen", diff --git a/NEWS.md b/NEWS.md index 16e0e394..24a161aa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,11 @@ +# connectapi (development version) + +## New features + +- `get_users()` now supports filtering users with the `account_status` and + `user_role` parameters. This allows you to, for example, find all licensed + users on a Connect server. (#311) + # connectapi 0.4.0 ## New features diff --git a/tests/integrated/test-get.R b/tests/integrated/test-get.R index d24b2d64..ef2c2a5b 100644 --- a/tests/integrated/test-get.R +++ b/tests/integrated/test-get.R @@ -15,11 +15,11 @@ test_that("get_users works", { purrr::map_chr(vctrs::vec_ptype(connectapi_ptypes$users), typeof) ) - publishers <- get_users(test_conn_1, user_role = "publisher") - expect_equal(nrow(publishers), 0, label = capture.output(print(publishers))) + viewers <- get_users(test_conn_1, user_role = "viewer") + expect_equal(nrow(viewers), 0, label = capture.output(print(viewers))) - locked <- get_users(test_conn_1, account_status = "locked") - expect_equal(nrow(publishers), 0, label = capture.output(print(publishers))) + locked <- get_users(test_conn_1, account_status = "inactive") + expect_equal(nrow(locked), 0, label = capture.output(print(locked))) }) test_that("get_groups works", { From 5d95dafd8e2c9252d4517bd8cea78e8f92d469c9 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Tue, 19 Nov 2024 15:34:36 -0500 Subject: [PATCH 05/15] update docs --- man/PositConnect.Rd | 12 +++++++++++- man/get_users.Rd | 26 +++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/man/PositConnect.Rd b/man/PositConnect.Rd index 2170309a..44a53b76 100644 --- a/man/PositConnect.Rd +++ b/man/PositConnect.Rd @@ -808,7 +808,13 @@ Get user details. \subsection{Method \code{users()}}{ Get users. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Connect$users(page_number = 1, prefix = NULL, page_size = 500)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Connect$users( + page_number = 1, + prefix = NULL, + page_size = 500, + user_role = NULL, + account_status = NULL +)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -819,6 +825,10 @@ Get users. \item{\code{prefix}}{The search term.} \item{\code{page_size}}{The page size.} + +\item{\code{user_role}}{Filter by user role.} + +\item{\code{account_status}}{Filter by account status.} } \if{html}{\out{}} } diff --git a/man/get_users.Rd b/man/get_users.Rd index 0791927a..40edade6 100644 --- a/man/get_users.Rd +++ b/man/get_users.Rd @@ -4,7 +4,15 @@ \alias{get_users} \title{Get user information from the Posit Connect server} \usage{ -get_users(src, page_size = 500, prefix = NULL, limit = Inf) +get_users( + src, + page_size = 500, + prefix = NULL, + limit = Inf, + page_number = NULL, + user_role = NULL, + account_status = NULL +) } \arguments{ \item{src}{The source object} @@ -15,6 +23,12 @@ get_users(src, page_size = 500, prefix = NULL, limit = Inf) The filter is case insensitive.} \item{limit}{The max number of records to return} + +\item{user_role}{Filter by user role ("administrator", "publisher", +"viewer"). Pass in a vector of multiple roles to match any value.} + +\item{account_status}{Filter by account status ("locked", "licensed", +"inactive"). Pass a vector of multiple statuses to match any value.} } \value{ A tibble with the following columns: @@ -48,8 +62,14 @@ Please see https://docs.posit.co/connect/api/#get-/v1/users for more information library(connectapi) client <- connect() -# get all users -get_users(client, limit = Inf) +# Get all users +get_users(client) + +# Get all licensed users +get_users(client, account_status = "licensed") + +# Get all users who are administrators or publishers +get_users(client, user_role = c("administrator", "publisher")) } } From 30c33038ae05af44319765ba6d93427c9222598f Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Tue, 19 Nov 2024 17:00:48 -0500 Subject: [PATCH 06/15] too soon --- .github/workflows/integration-tests.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/integration-tests.yaml b/.github/workflows/integration-tests.yaml index 0cb5330e..23e740ad 100644 --- a/.github/workflows/integration-tests.yaml +++ b/.github/workflows/integration-tests.yaml @@ -15,7 +15,6 @@ jobs: fail-fast: false matrix: version: - - "2024.10.0" # jammy - "2024.03.0" # jammy - "2023.09.0" # jammy - "2023.03.0" # bionic From 6ac0380a7fbcb41df2ef64578cdc7b6d1668e58d Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Tue, 19 Nov 2024 17:01:36 -0500 Subject: [PATCH 07/15] update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1a265198..9d44b7d8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ tests/integrated/integrated-results-check.txt tests/integrated/testthat-problems.rds CRAN-SUBMISSION tests/testthat/Rplots.pdf +*.lic From bf8d435ea04227cc33c3af12a89a0884272cffbe Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Tue, 19 Nov 2024 17:26:53 -0500 Subject: [PATCH 08/15] cleanup --- .Rbuildignore | 3 +++ R/get.R | 1 - man/get_users.Rd | 1 - tests/integrated/test-get.R | 10 ++++++---- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.Rbuildignore b/.Rbuildignore index 770351c1..c686e51e 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -2,6 +2,9 @@ ^\.Rproj\.user$ ^\.Renviron$ ^\.github$ +^\.envrc$ +^\.vscode$ +^.*\.lic$ ^renv$ ^renv\.lock$ ^README\.Rmd$ diff --git a/R/get.R b/R/get.R index e63934b0..f732c939 100644 --- a/R/get.R +++ b/R/get.R @@ -54,7 +54,6 @@ get_users <- function( page_size = 500, prefix = NULL, limit = Inf, - page_number = NULL, user_role = NULL, account_status = NULL ) { diff --git a/man/get_users.Rd b/man/get_users.Rd index 40edade6..a2b8090e 100644 --- a/man/get_users.Rd +++ b/man/get_users.Rd @@ -9,7 +9,6 @@ get_users( page_size = 500, prefix = NULL, limit = Inf, - page_number = NULL, user_role = NULL, account_status = NULL ) diff --git a/tests/integrated/test-get.R b/tests/integrated/test-get.R index ef2c2a5b..278aba1f 100644 --- a/tests/integrated/test-get.R +++ b/tests/integrated/test-get.R @@ -15,11 +15,13 @@ test_that("get_users works", { purrr::map_chr(vctrs::vec_ptype(connectapi_ptypes$users), typeof) ) - viewers <- get_users(test_conn_1, user_role = "viewer") - expect_equal(nrow(viewers), 0, label = capture.output(print(viewers))) + # Other tests create users, so specifying the exact number here is conditional + # on the contents of other tests and the order that tests run in. + admins <- get_users(test_conn_1, user_role = "administrator") + expect_true(nrow(admins) > 1, label = capture.output(print(admins))) - locked <- get_users(test_conn_1, account_status = "inactive") - expect_equal(nrow(locked), 0, label = capture.output(print(locked))) + licensed <- get_users(test_conn_1, account_status = "licensed") + expect_true(nrow(licensed) > 1, label = capture.output(print(licensed))) }) test_that("get_groups works", { From 5e27e9eeeb4a28e23b4026a0890fd4156a6c08a4 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Tue, 19 Nov 2024 17:43:22 -0500 Subject: [PATCH 09/15] tests --- tests/integrated/test-get.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integrated/test-get.R b/tests/integrated/test-get.R index 278aba1f..d3d44bea 100644 --- a/tests/integrated/test-get.R +++ b/tests/integrated/test-get.R @@ -18,10 +18,10 @@ test_that("get_users works", { # Other tests create users, so specifying the exact number here is conditional # on the contents of other tests and the order that tests run in. admins <- get_users(test_conn_1, user_role = "administrator") - expect_true(nrow(admins) > 1, label = capture.output(print(admins))) + expect_true(nrow(admins) > 1, label = glue::glue("nrow(admins) is {nrow(admins)}")) licensed <- get_users(test_conn_1, account_status = "licensed") - expect_true(nrow(licensed) > 1, label = capture.output(print(licensed))) + expect_true(nrow(licensed) > 1, label = glue::glue("nrow(licensed) is {nrow(licensed)}")) }) test_that("get_groups works", { From 959c5e6ec3d97ea1da61c14f4f4f01d3568c89ab Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Tue, 19 Nov 2024 17:50:41 -0500 Subject: [PATCH 10/15] =?UTF-8?q?ok=20this=E2=80=99ll=20fix=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/integrated/test-get.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integrated/test-get.R b/tests/integrated/test-get.R index d3d44bea..2d56ecc2 100644 --- a/tests/integrated/test-get.R +++ b/tests/integrated/test-get.R @@ -18,10 +18,10 @@ test_that("get_users works", { # Other tests create users, so specifying the exact number here is conditional # on the contents of other tests and the order that tests run in. admins <- get_users(test_conn_1, user_role = "administrator") - expect_true(nrow(admins) > 1, label = glue::glue("nrow(admins) is {nrow(admins)}")) + expect_true(nrow(admins) > 0) licensed <- get_users(test_conn_1, account_status = "licensed") - expect_true(nrow(licensed) > 1, label = glue::glue("nrow(licensed) is {nrow(licensed)}")) + expect_true(nrow(licensed) > 0) }) test_that("get_groups works", { From 8c03fad57f52e9e7cf6fe3780784b0ec690dc13d Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Wed, 20 Nov 2024 14:59:46 -0500 Subject: [PATCH 11/15] add unit tests --- tests/testthat/test-get.R | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/testthat/test-get.R b/tests/testthat/test-get.R index 8d4416f4..a53bac3b 100644 --- a/tests/testthat/test-get.R +++ b/tests/testthat/test-get.R @@ -140,3 +140,30 @@ with_mock_api({ expect_identical(result$name, c("connect_dev", "cool_kids_of_the_dmv")) }) }) + +without_internet({ + client <- Connect$new(server = "https://connect.example", api_key = "fake") + test_that("get_users() works with user_role and account_status", { + # No filter parameters specified + expect_GET( + get_users(client), + "https://connect.example/__api__/v1/users?page_number=1&page_size=500" + ) + + # Filter just on one parameter + expect_GET( + get_users(client, user_role = "administrator"), + "https://connect.example/__api__/v1/users?page_number=1&page_size=500&user_role=administrator" + ) + + # Filter on two parameters, one requiring concatenation + expect_GET( + get_users( + client, + user_role = c("administrator", "publisher"), + account_status = "licensed" + ), + "https://connect.example/__api__/v1/users?page_number=1&page_size=500&user_role=administrator%7Cpublisher&account_status=licensed" + ) + }) +}) From 0a3c04f985b8aa2d0af5cebed0d9e7c10a363841 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Wed, 20 Nov 2024 15:00:32 -0500 Subject: [PATCH 12/15] revert small changes --- .Rbuildignore | 3 --- .gitignore | 1 - DESCRIPTION | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.Rbuildignore b/.Rbuildignore index c686e51e..770351c1 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -2,9 +2,6 @@ ^\.Rproj\.user$ ^\.Renviron$ ^\.github$ -^\.envrc$ -^\.vscode$ -^.*\.lic$ ^renv$ ^renv\.lock$ ^README\.Rmd$ diff --git a/.gitignore b/.gitignore index 9d44b7d8..1a265198 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,3 @@ tests/integrated/integrated-results-check.txt tests/integrated/testthat-problems.rds CRAN-SUBMISSION tests/testthat/Rplots.pdf -*.lic diff --git a/DESCRIPTION b/DESCRIPTION index d1512cb4..6b6220fa 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: connectapi Title: Utilities for Interacting with the 'Posit Connect' Server API -Version: 0.4.0.9000 +Version: 0.4.0 Authors@R: c( person(given = "Toph", family = "Allen", From 6e1db520de40dcd4009332ea3a999f104b279ae7 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Wed, 20 Nov 2024 15:07:37 -0500 Subject: [PATCH 13/15] update documentation --- R/get.R | 11 +++++++---- man/get_users.Rd | 10 ++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/R/get.R b/R/get.R index f732c939..2ee545f0 100644 --- a/R/get.R +++ b/R/get.R @@ -5,10 +5,13 @@ #' @param prefix Filters users by prefix (username, first name, or last name). #' The filter is case insensitive. #' @param limit The max number of records to return -#' @param user_role Filter by user role ("administrator", "publisher", -#' "viewer"). Pass in a vector of multiple roles to match any value. -#' @param account_status Filter by account status ("locked", "licensed", -#' "inactive"). Pass a vector of multiple statuses to match any value. +#' @param user_role Optionally filter by user role ("administrator", +#' "publisher", "viewer"). Pass in a vector of multiple roles to match any value +#' (boolean OR). When `NULL` (the default), results are not filtered. +#' @param account_status Optionally filter by account status ("locked", +#' "licensed", "inactive"). Pass a vector of multiple statuses to match any +#' value (boolean OR). When `NULL` (the default), results are not filtered. + #' #' @return #' A tibble with the following columns: diff --git a/man/get_users.Rd b/man/get_users.Rd index a2b8090e..456b8afa 100644 --- a/man/get_users.Rd +++ b/man/get_users.Rd @@ -23,11 +23,13 @@ The filter is case insensitive.} \item{limit}{The max number of records to return} -\item{user_role}{Filter by user role ("administrator", "publisher", -"viewer"). Pass in a vector of multiple roles to match any value.} +\item{user_role}{Optionally filter by user role ("administrator", +"publisher", "viewer"). Pass in a vector of multiple roles to match any value +(boolean OR). When \code{NULL} (the default), results are not filtered.} -\item{account_status}{Filter by account status ("locked", "licensed", -"inactive"). Pass a vector of multiple statuses to match any value.} +\item{account_status}{Optionally filter by account status ("locked", +"licensed", "inactive"). Pass a vector of multiple statuses to match any +value (boolean OR). When \code{NULL} (the default), results are not filtered.} } \value{ A tibble with the following columns: From 93ea4b306bdacf517c8857ae57066a0afe6071a2 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Wed, 20 Nov 2024 17:07:22 -0500 Subject: [PATCH 14/15] fix lint failure --- tests/testthat/test-get.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/testthat/test-get.R b/tests/testthat/test-get.R index a53bac3b..eaa42b43 100644 --- a/tests/testthat/test-get.R +++ b/tests/testthat/test-get.R @@ -163,7 +163,10 @@ without_internet({ user_role = c("administrator", "publisher"), account_status = "licensed" ), - "https://connect.example/__api__/v1/users?page_number=1&page_size=500&user_role=administrator%7Cpublisher&account_status=licensed" + paste0( + "https://connect.example/__api__/v1/users?page_number=1&page_size=500&", + "user_role=administrator%7Cpublisher&account_status=licensed" + ) ) }) }) From 923494e1c2ebd2c3eaab707f17e3ff976d81d2ad Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Thu, 21 Nov 2024 10:37:34 -0500 Subject: [PATCH 15/15] Update NEWS.md Co-authored-by: Jonathan Keane --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 24a161aa..cac194a7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,7 +3,7 @@ ## New features - `get_users()` now supports filtering users with the `account_status` and - `user_role` parameters. This allows you to, for example, find all licensed + `user_role` parameters. For example, this allows you to find all licensed users on a Connect server. (#311) # connectapi 0.4.0