Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export(get_aws_content_credentials)
export(get_aws_credentials)
export(get_bundles)
export(get_content)
export(get_content_list)
export(get_content_packages)
export(get_content_permissions)
export(get_content_tags)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- New functions allow you to manage the OAuth integrations on your Connect
server: `create_integration()`, `update_integration()` and
`delete_integration()`. (#434)
- New `get_content_list()` function retrieves a list of content items associated with
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing something specific to an oauth integration is not what I would expect a function called get_content_list() to do.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i would agree here. i think naming could be an issue. I almost always prefer explicit naming conventions like: get_associated_content_for_integration() or something like that. get_content_list is too similar to what I would expected to get a list of content objects.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this point of view — I would definitely be open to a more verbose name. If I did change it to something like get_content_for_integration(), then I should also change the get_integration.Content() method added here to something more descriptive.

For context: The way #437 PR works, get_integrations() returns a list of integration objects but uses a different method based on what it's called with: get_integrations.Connect() gets all integration on the server, and get_integrations.Content() gets all integrations used by the piece of content you provide. Following that naming scheme exactly, get_content.integration() (so called with get_content(integration)) would be how this is implemented, but there's an existing get_content() function that returns a data frame, and we want to keep return types consistent within a given function. The idea, though, is that (in a future PR) you'd call get_content_list(client) to get a list of Content objects — kinda like how get_job_list() works. So the idea is that it is a more general name, but uses method dispatch to get you the list of content objects that are determined by the object you pass in.

Yesterday @jonkeane and I talked about a few different approaches, and this is the one I decided to move forward with. But having a more descriptive name and not using method dispatch (I'd probably go with get_content_for_integration() and the converse get_integrations_for_content()) is another option we looked at, and I'd be happy to go with that if folks think it's a better option.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if you implement the search keyword for this like we discussed in the other comment, maybe you don't even need a function for it, it's just search_content(client, integration=) or however you call search. So maybe we don't need to worry what to name this.

a specific OAuth integration. (#433)

# connectapi 0.8.0

Expand Down
51 changes: 51 additions & 0 deletions R/integrations.R
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,57 @@ set_integrations <- function(content, integrations) {
invisible(NULL)
}


#' Get content items using an OAuth integration
#'
#' @description
#' Retrieves a list of content items that are associated with a specific OAuth
#' integration.
#'
#' You must have administrator privileges to use this function.
#'
#' @param x A `connect_integration` object (as returned by [get_integrations()],
#' [get_integration()], [create_integration()], or [update_integration()]).
#'
#' @return A list of `Content` R6 objects representing all pieces of content
#' that use the specified OAuth integration. See [content_item()] for details
#' on the object.
#'
#' @seealso
#' [get_integrations()], [get_integration()], [get_associations()],
#' [content_item()]
#'
#' @examples
#' \dontrun{
#' client <- connect()
#'
#' # Get an integration
#' integration <- get_integration(client, "12345678-90ab-cdef-1234-567890abcdef")
#'
#' # Find all content using this integration
#' content_using_integration <- get_content_list(integration)
#' }
#'
#' @family oauth integration functions
#' @export
get_content_list <- function(x) {
if (!inherits(x, "connect_integration")) {
stop("'x' must be a 'connect_integration' object")
}
client <- attr(x, "client")
error_if_less_than(client$version, "2024.12.0")
assoc <- client$GET(v1_url(
"oauth",
"integrations",
x$guid,
"associations"
))
purrr::map(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a potentially large list, so I'm not sure how wise this.

I wonder if a better way to expose this would be through the content search, adding an integrations:guid keyword. There would be other benefits there too, but if nothing else, that would give you a list of content items already, so you wouldn't have to iterate over guids.

Alternatively, we could expose a different endpoint off of oauth/integrations/guid/ that would return a list of content records. But IMO running this through search would be better than adding a new endpoint.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'll give that a go.

assoc,
~ content_item(client, .x$content_guid)
)
}

#' Get OAuth associations for a piece of content
#'
#' @description
Expand Down
1 change: 1 addition & 0 deletions man/create_integration.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/delete_integration.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/get_associations.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions man/get_content_list.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/get_integration.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/get_integrations.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/set_integrations.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/update_integration.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"app_guid": "12345678",
"content_guid": "12345678",
"oauth_integration_guid": "0000001",
"oauth_integration_name": "Integration 1",
"oauth_integration_description": "This is the first description",
"oauth_integration_template": "template1",
"oauth_integration_auth_type": "Viewer",
"created_time": "2025-08-05T20:02:39Z"
}
]
20 changes: 20 additions & 0 deletions tests/testthat/test-integrations.R
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,23 @@ with_mock_dir("2025.07.0", {
expect_null(delete_integration(created))
})
})

with_mock_dir("2025.07.0", {
test_that("get_content_list() gets content items for integration", {
client <- Connect$new(server = "https://connect.example", api_key = "fake")
integration <- get_integration(client, "0000001")
result <- get_content_list(integration)
expect_equal(length(result), 1)
expect_true(validate_R6_class(result[[1]], "Content"))
expect_equal(result[[1]]$content$name, "fake-app-2000")
})
})

test_that("get_content_list() errs with too-old Connect", {
client <- MockConnect$new("2024.11.1")
client$version
expect_error(
get_integrations(client),
"This feature requires Posit Connect version 2024.12.0 but you are using 2024.11.1"
)
})
Loading