diff --git a/NAMESPACE b/NAMESPACE
index c04ae9b2..9f14a7b9 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -7,9 +7,11 @@ S3method(api_build,op_base_connect)
S3method(api_build,op_head)
S3method(as.data.frame,connect_integration_list)
S3method(as.data.frame,connect_list_hits)
+S3method(as.data.frame,connect_users)
S3method(as.data.frame,tbl_connect)
S3method(as_tibble,connect_integration_list)
S3method(as_tibble,connect_list_hits)
+S3method(as_tibble,connect_users)
S3method(connect_vars,op_base)
S3method(connect_vars,op_single)
S3method(connect_vars,tbl_connect)
@@ -115,6 +117,7 @@ export(get_usage_shiny)
export(get_usage_static)
export(get_user_permission)
export(get_users)
+export(get_users_list)
export(get_vanity_url)
export(get_vanity_urls)
export(get_variant)
diff --git a/R/connect.R b/R/connect.R
index 0631c895..aefb1b43 100644
--- a/R/connect.R
+++ b/R/connect.R
@@ -495,9 +495,10 @@ Connect <- R6::R6Class(
# users -----------------------------------------------
#' @description Get user details.
- #' @param guid The user GUID.
+ #' @param guid The user GUID or a `connect_user` object.
user = function(guid) {
- self$GET(v1_url("users", guid))
+ guid <- get_user_guid(guid)
+ prepend_class(self$GET(v1_url("users", guid)), "connect_user")
},
#' @description Get users.
@@ -527,7 +528,11 @@ Connect <- R6::R6Class(
user_role = user_role,
account_status = account_status
)
- self$GET(path, query = query)
+ res <- self$GET(path, query = query)
+ if (!is.null(res$results)) {
+ res$results <- lapply(res$results, prepend_class, "connect_user")
+ }
+ res
},
#' @description Get remote users.
@@ -585,8 +590,9 @@ Connect <- R6::R6Class(
},
#' @description Lock a user.
- #' @param user_guid User GUID.
- users_lock = function(user_guid) {
+ #' @param user User GUID or a `connect_user` object.
+ users_lock = function(user) {
+ user_guid <- get_user_guid(user)
path <- v1_url("users", user_guid, "lock")
message(path)
self$POST(
@@ -596,8 +602,9 @@ Connect <- R6::R6Class(
},
#' @description Unlock a user.
- #' @param user_guid User GUID.
- users_unlock = function(user_guid) {
+ #' @param user User GUID or a `connect_user` object.
+ users_unlock = function(user) {
+ user_guid <- get_user_guid(user)
path <- v1_url("users", user_guid, "lock")
self$POST(
path = path,
@@ -606,9 +613,10 @@ Connect <- R6::R6Class(
},
#' @description Update a user.
- #' @param user_guid User GUID.
+ #' @param user User GUID or a `connect_user` object.
#' @param ... User fields.
- users_update = function(user_guid, ...) {
+ users_update = function(user, ...) {
+ user_guid <- get_user_guid(user)
path <- v1_url("users", user_guid)
self$PUT(
path = path,
@@ -641,16 +649,18 @@ Connect <- R6::R6Class(
#' @description Add a group member.
#' @param group_guid The group GUID.
- #' @param user_guid The user GUID.
- group_member_add = function(group_guid, user_guid) {
+ #' @param user The user GUID or a `connect_user` object.
+ group_member_add = function(group_guid, user) {
+ user_guid <- get_user_guid(user)
path <- v1_url("groups", group_guid, "members")
self$POST(path, body = list(user_guid = user_guid))
},
#' @description Remove a group member.
#' @param group_guid The group GUID.
- #' @param user_guid The user GUID.
- group_member_remove = function(group_guid, user_guid) {
+ #' @param user The user GUID or a `connect_user` object.
+ group_member_remove = function(group_guid, user) {
+ user_guid <- get_user_guid(user)
path <- v1_url("groups", group_guid, "members", user_guid)
self$DELETE(path)
},
diff --git a/R/content.R b/R/content.R
index 355e21d0..e0ddb6d0 100644
--- a/R/content.R
+++ b/R/content.R
@@ -219,10 +219,14 @@ Content <- R6::R6Class(
self$connect$GET(url)
},
#' @description Add a principal to the ACL for this content.
- #' @param principal_guid GUID for the target user or group.
+ #' @param principal_guid GUID for the target user or group. When
+ #' `principal_type = "user"`, can also be a `connect_user` object.
#' @param principal_type Acting on user or group.
#' @param role The kind of content access.
permissions_add = function(principal_guid, principal_type, role) {
+ if (principal_type == "user") {
+ principal_guid <- get_user_guid(principal_guid)
+ }
url <- v1_url("content", self$content$guid, "permissions")
self$connect$POST(
url,
@@ -235,10 +239,14 @@ Content <- R6::R6Class(
},
#' @description Alter a principal in the ACL for this content.
#' @param id The target identifier.
- #' @param principal_guid GUID for the target user or group.
+ #' @param principal_guid GUID for the target user or group. When
+ #' `principal_type = "user"`, can also be a `connect_user` object.
#' @param principal_type Acting on user or group.
#' @param role The kind of content access.
permissions_update = function(id, principal_guid, principal_type, role) {
+ if (principal_type == "user") {
+ principal_guid <- get_user_guid(principal_guid)
+ }
url <- v1_url("content", self$content$guid, "permissions", id)
self$connect$PUT(
url,
@@ -954,7 +962,6 @@ set_run_as <- function(content, run_as, run_as_current_user = FALSE) {
return(content)
}
-
#' Delete Content
#'
#' Delete a content item. WARNING: This action deletes all history, configuration,
@@ -1007,8 +1014,8 @@ content_delete <- function(content, force = FALSE) {
#' @param content An R6 content item
#' @param ... Settings up update that are passed along to Posit Connect
#' @param access_type One of "all", "logged_in", or "acl"
-#' @param owner_guid The GUID of a user who is a publisher, so that they can
-#' become the new owner of the content
+#' @param owner The GUID of a user who is a publisher, so that they can
+#' become the new owner of the content. Can also be a `connect_user` object.
#'
#' @return An R6 content item
#'
@@ -1040,7 +1047,8 @@ content_update_access_type <- function(
#' @rdname content_update
#' @export
-content_update_owner <- function(content, owner_guid) {
+content_update_owner <- function(content, owner) {
+ owner_guid <- get_user_guid(owner)
content_update(content = content, owner_guid = owner_guid)
}
@@ -1103,7 +1111,6 @@ unlock_content <- function(content) {
return(content)
}
-
#' Verify Content Name
#'
#' Ensures that a content name fits the specifications / requirements of Posit
@@ -1178,7 +1185,6 @@ delete_bundle <- function(content, bundle_id) {
return(content)
}
-
#' Content permissions
#'
#' Get or set content permissions for a content item
@@ -1199,8 +1205,12 @@ delete_bundle <- function(content, bundle_id) {
#' This makes it easier to find / isolate this record.
#'
#' @param content An R6 content object
-#' @param guid The guid associated with either a user (for `content_add_user`) or group (for `content_add_group`)
-#' @param role The role to assign to a user. Either "viewer" or "owner." Defaults to "viewer"
+#' @param user The guid associated with either a user (for `content_add_user`)
+#' or group (for `content_add_group`). Can also be a list of `connect_user`
+#' objects.
+#' @param guid The guid associated with a group.
+#' @param role The role to assign to a user. Either "viewer" or "owner."
+#' Defaults to "viewer"
#' @param add_owner Optional. Whether to include the owner in returned
#' permission sets. Default is TRUE. The owner will have an NA_character_
#' permission "id"
@@ -1209,10 +1219,11 @@ delete_bundle <- function(content, bundle_id) {
#' @rdname permissions
#' @family content functions
#' @export
-content_add_user <- function(content, guid, role = c("viewer", "owner")) {
+content_add_user <- function(content, user, role = c("viewer", "owner")) {
validate_R6_class(content, "Content")
role <- .define_role(role)
+ guid <- purrr::map_chr(user, get_user_guid)
purrr::map(guid, ~ .content_add_permission_impl(content, "user", .x, role))
return(content)
@@ -1278,8 +1289,9 @@ content_add_group <- function(content, guid, role = c("viewer", "owner")) {
#' @rdname permissions
#' @export
-content_delete_user <- function(content, guid) {
+content_delete_user <- function(content, user) {
validate_R6_class(content, "Content")
+ guid <- purrr::map_chr(user, get_user_guid)
purrr::map(
guid,
~ .content_delete_permission_impl(
@@ -1331,8 +1343,9 @@ content_delete_group <- function(content, guid) {
#' @rdname permissions
#' @export
-get_user_permission <- function(content, guid, add_owner = TRUE) {
+get_user_permission <- function(content, user, add_owner = TRUE) {
validate_R6_class(content, "Content")
+ guid <- get_user_guid(user)
res <- .get_permission(content, "user", guid, add_owner = add_owner)
if (length(res) > 0) {
return(res[[1]])
@@ -1361,7 +1374,6 @@ get_group_permission <- function(content, guid) {
}
}
-
#' @rdname permissions
#' @export
get_content_permissions <- function(content, add_owner = TRUE) {
diff --git a/R/get.R b/R/get.R
index 01e41f06..cd0affb9 100644
--- a/R/get.R
+++ b/R/get.R
@@ -13,8 +13,8 @@
#' value (boolean OR). When `NULL` (the default), results are not filtered.
#'
-#' @return
-#' A tibble with the following columns:
+#' @return For `get_users_list`, a list of objects of type `"connect_user"` which
+#' contain the following information:
#'
#' * `email`: The user's email
#' * `username`: The user's username
@@ -33,6 +33,10 @@
#' * `locked`: Whether or not the user is locked
#' * `guid`: The user's GUID, or unique identifier, in UUID RFC4122 format
#'
+#' For `get_users`, a data frame with the same fields as `get_users_list`.
+#'
+#' For `get_user`, a single `"connect_user"` object.
+#'
#' @details
#' Please see https://docs.posit.co/connect/api/#get-/v1/users for more information.
#'
@@ -41,7 +45,7 @@
#' library(connectapi)
#' client <- connect()
#'
-#' # Get all users
+#' # Get all users as a data frame
#' get_users(client)
#'
#' # Get all licensed users
@@ -49,10 +53,33 @@
#'
#' # Get all users who are administrators or publishers
#' get_users(client, user_role = c("administrator", "publisher"))
+#'
+#' # Get users as a list
+#' users_list <- get_users_list(client)
#' }
#'
#' @export
-get_users <- function(
+get_users <- function(src,
+ page_size = 500,
+ prefix = NULL,
+ limit = Inf,
+ user_role = NULL,
+ account_status = NULL) {
+ as.data.frame(
+ get_users_list(
+ src,
+ page_size,
+ prefix,
+ limit,
+ user_role,
+ account_status
+ )
+ )
+}
+
+#' @rdname get_users
+#' @export
+get_users_list <- function(
src,
page_size = 500,
prefix = NULL,
@@ -72,10 +99,23 @@ get_users <- function(
),
limit = limit
)
+ return(prepend_class(res, "connect_users"))
+}
- out <- parse_connectapi_typed(res, connectapi_ptypes$users)
+#' @param guid user GUID
+#' @rdname get_users
+get_user <- function(src, guid) {
+ src$user(guid)
+}
- return(out)
+#' @export
+as.data.frame.connect_users <- function(x, ...) {
+ parse_connectapi_typed(x, connectapi_ptypes$users)
+}
+
+#' @export
+as_tibble.connect_users <- function(x, ...) {
+ parse_connectapi_typed(x, connectapi_ptypes$users)
}
#' Get information about content on the Posit Connect server
diff --git a/R/user.R b/R/user.R
index 90b2122b..a4d415b6 100644
--- a/R/user.R
+++ b/R/user.R
@@ -32,3 +32,28 @@ user_guid_from_username <- function(client, username) {
return(res[[1]]$guid)
}
}
+
+#' Extract User GUID
+#'
+#' Helper function to extract a user GUID from either a character string or a
+#' `connect_user` object.
+#'
+#' @param user Either a character string containing a user GUID or a
+#' `connect_user` object (as returned by `Connect$user()` or `Connect$users()`)
+#'
+#' @return A character string containing the user GUID
+#'
+#' @keywords internal
+get_user_guid <- function(user) {
+ if (is.character(user)) {
+ return(user)
+ } else if (inherits(user, "connect_user")) {
+ if (!is.null(user$guid)) {
+ return(user$guid)
+ } else {
+ stop("connect_user object does not contain a guid field")
+ }
+ } else {
+ stop("user must be either a character string (GUID) or a connect_user object")
+ }
+}
diff --git a/R/utils.R b/R/utils.R
index 85e89b4e..be60518d 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -256,3 +256,9 @@ message_if_not_testing <- function(...) {
message(...)
}
}
+
+# Prepends a new class to a given objec
+prepend_class <- function(x, class) {
+ class(x) <- c(class, class(x))
+ x
+}
diff --git a/man/Content.Rd b/man/Content.Rd
index e8b45a1b..8e222bce 100644
--- a/man/Content.Rd
+++ b/man/Content.Rd
@@ -334,7 +334,8 @@ Add a principal to the ACL for this content.
\subsection{Arguments}{
\if{html}{\out{
}}
\describe{
-\item{\code{principal_guid}}{GUID for the target user or group.}
+\item{\code{principal_guid}}{GUID for the target user or group. When
+\code{principal_type = "user"}, can also be a \code{connect_user} object.}
\item{\code{principal_type}}{Acting on user or group.}
@@ -357,7 +358,8 @@ Alter a principal in the ACL for this content.
\describe{
\item{\code{id}}{The target identifier.}
-\item{\code{principal_guid}}{GUID for the target user or group.}
+\item{\code{principal_guid}}{GUID for the target user or group. When
+\code{principal_type = "user"}, can also be a \code{connect_user} object.}
\item{\code{principal_type}}{Acting on user or group.}
diff --git a/man/PositConnect.Rd b/man/PositConnect.Rd
index 241b0c9a..47c2267f 100644
--- a/man/PositConnect.Rd
+++ b/man/PositConnect.Rd
@@ -767,7 +767,7 @@ Get user details.
\subsection{Arguments}{
\if{html}{\out{
}}
\describe{
-\item{\code{guid}}{The user GUID.}
+\item{\code{guid}}{The user GUID or a \code{connect_user} object.}
}
\if{html}{\out{
}}
}
@@ -883,13 +883,13 @@ Create a remote user.
\subsection{Method \code{users_lock()}}{
Lock a user.
\subsection{Usage}{
-\if{html}{\out{
}}\preformatted{Connect$users_lock(user_guid)}\if{html}{\out{
}}
+\if{html}{\out{
}}\preformatted{Connect$users_lock(user)}\if{html}{\out{
}}
}
\subsection{Arguments}{
\if{html}{\out{
}}
\describe{
-\item{\code{user_guid}}{User GUID.}
+\item{\code{user}}{User GUID or a \code{connect_user} object.}
}
\if{html}{\out{
}}
}
@@ -900,13 +900,13 @@ Lock a user.
\subsection{Method \code{users_unlock()}}{
Unlock a user.
\subsection{Usage}{
-\if{html}{\out{
}}\preformatted{Connect$users_unlock(user_guid)}\if{html}{\out{
}}
+\if{html}{\out{
}}\preformatted{Connect$users_unlock(user)}\if{html}{\out{
}}
}
\subsection{Arguments}{
\if{html}{\out{
}}
\describe{
-\item{\code{user_guid}}{User GUID.}
+\item{\code{user}}{User GUID or a \code{connect_user} object.}
}
\if{html}{\out{
}}
}
@@ -917,13 +917,13 @@ Unlock a user.
\subsection{Method \code{users_update()}}{
Update a user.
\subsection{Usage}{
-\if{html}{\out{
}}\preformatted{Connect$users_update(user_guid, ...)}\if{html}{\out{
}}
+\if{html}{\out{
}}\preformatted{Connect$users_update(user, ...)}\if{html}{\out{
}}
}
\subsection{Arguments}{
\if{html}{\out{
}}
\describe{
-\item{\code{user_guid}}{User GUID.}
+\item{\code{user}}{User GUID or a \code{connect_user} object.}
\item{\code{...}}{User fields.}
}
@@ -974,7 +974,7 @@ Get group members.
\subsection{Method \code{group_member_add()}}{
Add a group member.
\subsection{Usage}{
-\if{html}{\out{
}}\preformatted{Connect$group_member_add(group_guid, user_guid)}\if{html}{\out{
}}
+\if{html}{\out{
}}\preformatted{Connect$group_member_add(group_guid, user)}\if{html}{\out{
}}
}
\subsection{Arguments}{
@@ -982,7 +982,7 @@ Add a group member.
\describe{
\item{\code{group_guid}}{The group GUID.}
-\item{\code{user_guid}}{The user GUID.}
+\item{\code{user}}{The user GUID or a \code{connect_user} object.}
}
\if{html}{\out{
}}
}
@@ -993,7 +993,7 @@ Add a group member.
\subsection{Method \code{group_member_remove()}}{
Remove a group member.
\subsection{Usage}{
-\if{html}{\out{
}}\preformatted{Connect$group_member_remove(group_guid, user_guid)}\if{html}{\out{
}}
+\if{html}{\out{
}}\preformatted{Connect$group_member_remove(group_guid, user)}\if{html}{\out{
}}
}
\subsection{Arguments}{
@@ -1001,7 +1001,7 @@ Remove a group member.
\describe{
\item{\code{group_guid}}{The group GUID.}
-\item{\code{user_guid}}{The user GUID.}
+\item{\code{user}}{The user GUID or a \code{connect_user} object.}
}
\if{html}{\out{
}}
}
diff --git a/man/VariantR6.Rd b/man/VariantR6.Rd
index be0cc4a6..cdf922d6 100644
--- a/man/VariantR6.Rd
+++ b/man/VariantR6.Rd
@@ -39,7 +39,6 @@ Other R6 classes:
\section{Methods}{
\subsection{Public methods}{
\itemize{
-\item \href{#method-Variant-get_variant}{\code{Variant$get_variant()}}
\item \href{#method-Variant-get_variant_remote}{\code{Variant$get_variant_remote()}}
\item \href{#method-Variant-new}{\code{Variant$new()}}
\item \href{#method-Variant-send_mail}{\code{Variant$send_mail()}}
@@ -90,19 +89,12 @@ Other R6 classes:
}}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Variant-get_variant}{}}}
-\subsection{Method \code{get_variant()}}{
-Get the underlying variant data.
-\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Variant$get_variant()}\if{html}{\out{
}}
-}
-
-}
-\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-Variant-get_variant_remote}{}}}
\subsection{Method \code{get_variant_remote()}}{
+Get the underlying variant data.
+
+
Get and store the (remote) variant data.
\subsection{Usage}{
\if{html}{\out{}}\preformatted{Variant$get_variant_remote()}\if{html}{\out{
}}
diff --git a/man/VariantSchedule.Rd b/man/VariantSchedule.Rd
index ee961e1a..d7c23e29 100644
--- a/man/VariantSchedule.Rd
+++ b/man/VariantSchedule.Rd
@@ -81,7 +81,6 @@ Other R6 classes:
connectapi::Variant$get_subscribers()
connectapi::Variant$get_url()
connectapi::Variant$get_url_rev()
-connectapi::Variant$get_variant()
connectapi::Variant$get_variant_remote()
connectapi::Variant$job()
connectapi::Variant$jobs()
diff --git a/man/VariantTask.Rd b/man/VariantTask.Rd
index 4f23bd9e..316a53a1 100644
--- a/man/VariantTask.Rd
+++ b/man/VariantTask.Rd
@@ -80,7 +80,6 @@ Other R6 classes:
connectapi::Variant$get_subscribers()
connectapi::Variant$get_url()
connectapi::Variant$get_url_rev()
-connectapi::Variant$get_variant()
connectapi::Variant$get_variant_remote()
connectapi::Variant$job()
connectapi::Variant$jobs()
diff --git a/man/content_update.Rd b/man/content_update.Rd
index ef1254e8..a5e49522 100644
--- a/man/content_update.Rd
+++ b/man/content_update.Rd
@@ -10,7 +10,7 @@ content_update(content, ...)
content_update_access_type(content, access_type = c("all", "logged_in", "acl"))
-content_update_owner(content, owner_guid)
+content_update_owner(content, owner)
}
\arguments{
\item{content}{An R6 content item}
@@ -19,8 +19,8 @@ content_update_owner(content, owner_guid)
\item{access_type}{One of "all", "logged_in", or "acl"}
-\item{owner_guid}{The GUID of a user who is a publisher, so that they can
-become the new owner of the content}
+\item{owner}{The GUID of a user who is a publisher, so that they can
+become the new owner of the content. Can also be a \code{connect_user} object.}
}
\value{
An R6 content item
diff --git a/man/get_user_guid.Rd b/man/get_user_guid.Rd
new file mode 100644
index 00000000..b69472f0
--- /dev/null
+++ b/man/get_user_guid.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/user.R
+\name{get_user_guid}
+\alias{get_user_guid}
+\title{Extract User GUID}
+\usage{
+get_user_guid(user)
+}
+\arguments{
+\item{user}{Either a character string containing a user GUID or a
+\code{connect_user} object (as returned by \code{Connect$user()} or \code{Connect$users()})}
+}
+\value{
+A character string containing the user GUID
+}
+\description{
+Helper function to extract a user GUID from either a character string or a
+\code{connect_user} object.
+}
+\keyword{internal}
diff --git a/man/get_users.Rd b/man/get_users.Rd
index 456b8afa..abb497bb 100644
--- a/man/get_users.Rd
+++ b/man/get_users.Rd
@@ -2,6 +2,8 @@
% Please edit documentation in R/get.R
\name{get_users}
\alias{get_users}
+\alias{get_users_list}
+\alias{get_user}
\title{Get user information from the Posit Connect server}
\usage{
get_users(
@@ -12,6 +14,17 @@ get_users(
user_role = NULL,
account_status = NULL
)
+
+get_users_list(
+ src,
+ page_size = 500,
+ prefix = NULL,
+ limit = Inf,
+ user_role = NULL,
+ account_status = NULL
+)
+
+get_user(src, guid)
}
\arguments{
\item{src}{The source object}
@@ -30,9 +43,12 @@ The filter is case insensitive.}
\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.}
+
+\item{guid}{user GUID}
}
\value{
-A tibble with the following columns:
+For \code{get_users_list}, a list of objects of type \code{"connect_user"} which
+contain the following information:
\itemize{
\item \code{email}: The user's email
\item \code{username}: The user's username
@@ -51,6 +67,10 @@ through an email. This feature is unique to password authentication.
\item \code{locked}: Whether or not the user is locked
\item \code{guid}: The user's GUID, or unique identifier, in UUID RFC4122 format
}
+
+For \code{get_users}, a data frame with the same fields as \code{get_users_list}.
+
+For \code{get_user}, a single \code{"connect_user"} object.
}
\description{
Get user information from the Posit Connect server
@@ -63,7 +83,7 @@ Please see https://docs.posit.co/connect/api/#get-/v1/users for more information
library(connectapi)
client <- connect()
-# Get all users
+# Get all users as a data frame
get_users(client)
# Get all licensed users
@@ -71,6 +91,9 @@ get_users(client, account_status = "licensed")
# Get all users who are administrators or publishers
get_users(client, user_role = c("administrator", "publisher"))
+
+# Get users as a list
+users_list <- get_users_list(client)
}
}
diff --git a/man/permissions.Rd b/man/permissions.Rd
index 1af0aff9..0db517a4 100644
--- a/man/permissions.Rd
+++ b/man/permissions.Rd
@@ -12,15 +12,15 @@
\alias{get_content_permissions}
\title{Content permissions}
\usage{
-content_add_user(content, guid, role = c("viewer", "owner"))
+content_add_user(content, user, role = c("viewer", "owner"))
content_add_group(content, guid, role = c("viewer", "owner"))
-content_delete_user(content, guid)
+content_delete_user(content, user)
content_delete_group(content, guid)
-get_user_permission(content, guid, add_owner = TRUE)
+get_user_permission(content, user, add_owner = TRUE)
get_my_permission(content, add_owner = TRUE)
@@ -31,9 +31,14 @@ get_content_permissions(content, add_owner = TRUE)
\arguments{
\item{content}{An R6 content object}
-\item{guid}{The guid associated with either a user (for \code{content_add_user}) or group (for \code{content_add_group})}
+\item{user}{The guid associated with either a user (for \code{content_add_user})
+or group (for \code{content_add_group}). Can also be a list of \code{connect_user}
+objects.}
-\item{role}{The role to assign to a user. Either "viewer" or "owner." Defaults to "viewer"}
+\item{role}{The role to assign to a user. Either "viewer" or "owner."
+Defaults to "viewer"}
+
+\item{guid}{The guid associated with a group.}
\item{add_owner}{Optional. Whether to include the owner in returned
permission sets. Default is TRUE. The owner will have an NA_character_
diff --git a/tests/integrated/test-get.R b/tests/integrated/test-get.R
index 0e41106c..860bf909 100644
--- a/tests/integrated/test-get.R
+++ b/tests/integrated/test-get.R
@@ -8,7 +8,6 @@ cont1_content <- NULL
test_that("get_users works", {
users <- get_users(test_conn_1)
-
expect_s3_class(users, c("tbl_df", "tbl", "data.frame"))
expect_equal(
purrr::map_chr(vctrs::vec_ptype(users), typeof),
diff --git a/tests/testthat/test-content.R b/tests/testthat/test-content.R
index f7e8e434..466a91d6 100644
--- a/tests/testthat/test-content.R
+++ b/tests/testthat/test-content.R
@@ -144,6 +144,32 @@ with_mock_api({
"https://connect.example/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions"
)
})
+
+ test_that("Content$permissions_add() accepts connect_user object for users", {
+ con <- Connect$new(server = "https://connect.example", api_key = "fake")
+ item <- content_item(con, "f2f37341-e21d-3d80-c698-a935ad614066")
+ user_guid <- "a01792e3-2e67-402e-99af-be04a48da074"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ expect_POST(
+ item$permissions_add(user, "user", "viewer"),
+ "https://connect.example/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions",
+ '{"principal_guid":"a01792e3-2e67-402e-99af-be04a48da074","principal_type":"user","role":"viewer"}'
+ )
+ })
+
+ test_that("Content$permissions_update() accepts connect_user object for users", {
+ con <- Connect$new(server = "https://connect.example", api_key = "fake")
+ item <- content_item(con, "f2f37341-e21d-3d80-c698-a935ad614066")
+ user_guid <- "a01792e3-2e67-402e-99af-be04a48da074"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ expect_PUT(
+ item$permissions_update(94, user, "user", "editor"),
+ "https://connect.example/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions/94",
+ '{"principal_guid":"a01792e3-2e67-402e-99af-be04a48da074","principal_type":"user","role":"editor"}'
+ )
+ })
})
without_internet({
@@ -467,6 +493,55 @@ test_that("get_content_packages() gets packages", {
})
})
+with_mock_api({
+ test_that("content_add_user() accepts connect_user object", {
+ con <- Connect$new(server = "https://connect.example", api_key = "fake")
+ item <- content_item(con, "f2f37341-e21d-3d80-c698-a935ad614066")
+ user_guid <- "a01792e3-2e67-402e-99af-be04a48da074"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ expect_POST(
+ suppressMessages(content_add_user(item, user)),
+ "https://connect.example/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions"
+ )
+
+ # Test with list of connect_user objects
+ user2 <- structure(list(guid = "b02892e3-2e67-402e-99af-be04a48da075"), class = "connect_user")
+ expect_POST(
+ suppressMessages(content_add_user(item, list(user, user2))),
+ "https://connect.example/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions"
+ )
+ })
+
+ test_that("get_user_permission() accepts connect_user object", {
+ con <- Connect$new(server = "https://connect.example", api_key = "fake")
+ item <- content_item(con, "f2f37341-e21d-3d80-c698-a935ad614066")
+ user_guid <- "a01792e3-2e67-402e-99af-be04a48da074"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ # Test with GUID string - should work
+ perm1 <- suppressMessages(get_user_permission(item, user_guid))
+
+ # Test with connect_user object - should also work and return same result
+ perm2 <- suppressMessages(get_user_permission(item, user))
+
+ # Both should return the same permission (or NULL if not found)
+ expect_equal(perm1, perm2)
+ })
+
+ test_that("content_update_owner() accepts connect_user object", {
+ con <- Connect$new(server = "https://connect.example", api_key = "fake")
+ item <- content_item(con, "f2f37341-e21d-3d80-c698-a935ad614066")
+ user_guid <- "a01792e3-2e67-402e-99af-be04a48da074"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ expect_PATCH(
+ suppressMessages(content_update_owner(item, user)),
+ "https://connect.example/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066"
+ )
+ })
+})
+
# content search ----
with_mock_dir("2025.09.0", {
diff --git a/tests/testthat/test-groups.R b/tests/testthat/test-groups.R
index 957cd3b3..6fa5319c 100644
--- a/tests/testthat/test-groups.R
+++ b/tests/testthat/test-groups.R
@@ -34,6 +34,30 @@ without_internet({
"https://connect.example/__api__/v1/groups/remote?limit=500"
)
})
+
+ test_that("group_member_add() accepts connect_user object", {
+ client <- Connect$new(server = "https://connect.example", api_key = "fake")
+ group_guid <- "a6fb5cea-0123-4567-89ab-cdef01234567"
+ user_guid <- "20a79ce3-6e87-4522-9faf-be24228800a4"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ expect_POST(
+ client$group_member_add(group_guid, user),
+ "https://connect.example/__api__/v1/groups/a6fb5cea-0123-4567-89ab-cdef01234567/members"
+ )
+ })
+
+ test_that("group_member_remove() accepts connect_user object", {
+ client <- Connect$new(server = "https://connect.example", api_key = "fake")
+ group_guid <- "a6fb5cea-0123-4567-89ab-cdef01234567"
+ user_guid <- "20a79ce3-6e87-4522-9faf-be24228800a4"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ expect_DELETE(
+ client$group_member_remove(group_guid, user),
+ "https://connect.example/__api__/v1/groups/a6fb5cea-0123-4567-89ab-cdef01234567/members/20a79ce3-6e87-4522-9faf-be24228800a4" # nolint
+ )
+ })
})
with_mock_dir("2024.08.0", {
diff --git a/tests/testthat/test-users.R b/tests/testthat/test-users.R
index 23f45c5b..60f6f455 100644
--- a/tests/testthat/test-users.R
+++ b/tests/testthat/test-users.R
@@ -1,3 +1,47 @@
+# Tests for get_user_guid() helper function ----
+
+test_that("get_user_guid() works with character GUID", {
+ guid <- "20a79ce3-6e87-4522-9faf-be24228800a4"
+ expect_equal(get_user_guid(guid), guid)
+})
+
+test_that("get_user_guid() works with connect_user object", {
+ user <- structure(
+ list(
+ guid = "20a79ce3-6e87-4522-9faf-be24228800a4",
+ username = "carlos12",
+ first_name = "Carlos",
+ last_name = "User"
+ ),
+ class = "connect_user"
+ )
+ expect_equal(get_user_guid(user), "20a79ce3-6e87-4522-9faf-be24228800a4")
+})
+
+test_that("get_user_guid() errors with invalid input", {
+ expect_error(
+ get_user_guid(123),
+ "user must be either a character string \\(GUID\\) or a connect_user object"
+ )
+ expect_error(
+ get_user_guid(list(name = "test")),
+ "user must be either a character string \\(GUID\\) or a connect_user object"
+ )
+})
+
+test_that("get_user_guid() errors when connect_user has no guid field", {
+ user <- structure(
+ list(username = "carlos12"),
+ class = "connect_user"
+ )
+ expect_error(
+ get_user_guid(user),
+ "connect_user object does not contain a guid field"
+ )
+})
+
+# Tests for Connect client methods accepting connect_user objects ----
+
with_mock_api({
test_that("we can retrieve the users list", {
con <- Connect$new(server = "https://connect.example", api_key = "fake")
@@ -6,6 +50,13 @@ with_mock_api({
expect_equal(nrow(users), 3)
})
+ test_that("we can retrieve the users list as list", {
+ con <- Connect$new(server = "https://connect.example", api_key = "fake")
+ users <- get_users_list(con)
+ expect_s3_class(users, "connect_users")
+ expect_equal(length(users), 3)
+ })
+
test_that("we can retrieve a user by id", {
con <- Connect$new(server = "https://connect.example", api_key = "fake")
a_user <- con$user("20a79ce3-6e87-4522-9faf-be24228800a4")
@@ -13,6 +64,14 @@ with_mock_api({
expect_type(a_user, "list")
expect_equal(a_user$first_name, "Carlos")
})
+
+ test_that("Connect$user() accepts connect_user object", {
+ con <- Connect$new(server = "https://connect.example", api_key = "fake")
+ user <- con$user("20a79ce3-6e87-4522-9faf-be24228800a4")
+ same_user <- con$user(user)
+ expect_equal(same_user$guid, "20a79ce3-6e87-4522-9faf-be24228800a4")
+ expect_equal(same_user$first_name, "Carlos")
+ })
})
without_internet({
@@ -43,4 +102,52 @@ without_internet({
"https://connect.example/__api__/v1/users/remote?prefix=A%20user%20name"
)
})
+
+ test_that("users_lock() accepts connect_user object or GUID string", {
+ client <- Connect$new(server = "https://connect.example", api_key = "fake")
+ user_guid <- "20a79ce3-6e87-4522-9faf-be24228800a4"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ expect_POST(
+ client$users_lock(user_guid),
+ "https://connect.example/__api__/v1/users/20a79ce3-6e87-4522-9faf-be24228800a4/lock"
+ )
+
+ expect_POST(
+ client$users_lock(user),
+ "https://connect.example/__api__/v1/users/20a79ce3-6e87-4522-9faf-be24228800a4/lock"
+ )
+ })
+
+ test_that("users_unlock() accepts connect_user object or GUID string", {
+ client <- Connect$new(server = "https://connect.example", api_key = "fake")
+ user_guid <- "20a79ce3-6e87-4522-9faf-be24228800a4"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ expect_POST(
+ client$users_unlock(user_guid),
+ "https://connect.example/__api__/v1/users/20a79ce3-6e87-4522-9faf-be24228800a4/lock"
+ )
+
+ expect_POST(
+ client$users_unlock(user),
+ "https://connect.example/__api__/v1/users/20a79ce3-6e87-4522-9faf-be24228800a4/lock"
+ )
+ })
+
+ test_that("users_update() accepts connect_user object or GUID string", {
+ client <- Connect$new(server = "https://connect.example", api_key = "fake")
+ user_guid <- "20a79ce3-6e87-4522-9faf-be24228800a4"
+ user <- structure(list(guid = user_guid), class = "connect_user")
+
+ expect_PUT(
+ client$users_update(user_guid, first_name = "New Name"),
+ "https://connect.example/__api__/v1/users/20a79ce3-6e87-4522-9faf-be24228800a4"
+ )
+
+ expect_PUT(
+ client$users_update(user, first_name = "New Name"),
+ "https://connect.example/__api__/v1/users/20a79ce3-6e87-4522-9faf-be24228800a4"
+ )
+ })
})