From 3419593cc13e829dd843ce34af3e05b5af3f6548 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Fri, 25 Jul 2025 18:55:02 -0400 Subject: [PATCH 1/6] Add function to get integrations --- DESCRIPTION | 1 + NAMESPACE | 3 + R/get.R | 4 + R/integrations.R | 97 +++ R/ptype.R | 11 + _pkgdown.yml | 2 + ...as.data.frame.connect_list_integrations.Rd | 24 + man/as_tibble.connect_list_integrations.Rd | 20 + man/get_integrations.Rd | 56 ++ man/get_oauth_content_credentials.Rd | 3 + man/get_oauth_credentials.Rd | 3 + .../2024.08.0/__api__/server_settings.json | 137 +++++ .../__api__/v1/oauth/integrations.json | 579 ++++++++++++++++++ .../2024.12.0/__api__/server_settings.json | 3 + .../__api__/v1/oauth/integrations.json | 38 ++ tests/testthat/2024.12.0/__ping__.json | 3 + tests/testthat/test-integrations.R | 48 ++ 17 files changed, 1032 insertions(+) create mode 100644 R/integrations.R create mode 100644 man/as.data.frame.connect_list_integrations.Rd create mode 100644 man/as_tibble.connect_list_integrations.Rd create mode 100644 man/get_integrations.Rd create mode 100644 tests/testthat/2024.08.0/__api__/v1/oauth/integrations.json create mode 100644 tests/testthat/2024.12.0/__api__/server_settings.json create mode 100644 tests/testthat/2024.12.0/__api__/v1/oauth/integrations.json create mode 100644 tests/testthat/2024.12.0/__ping__.json create mode 100644 tests/testthat/test-integrations.R diff --git a/DESCRIPTION b/DESCRIPTION index ba602beed..3bc58308c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -78,6 +78,7 @@ Collate: 'get.R' 'git.R' 'groups.R' + 'integrations.R' 'lazy.R' 'page.R' 'parse.R' diff --git a/NAMESPACE b/NAMESPACE index 30118f21e..340e5a151 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,8 +6,10 @@ S3method("[[",connect_tag_tree) S3method(api_build,op_base_connect) S3method(api_build,op_head) S3method(as.data.frame,connect_list_hits) +S3method(as.data.frame,connect_list_integrations) S3method(as.data.frame,tbl_connect) S3method(as_tibble,connect_list_hits) +S3method(as_tibble,connect_list_integrations) S3method(connect_vars,op_base) S3method(connect_vars,op_single) S3method(connect_vars,tbl_connect) @@ -84,6 +86,7 @@ export(get_group_members) export(get_group_permission) export(get_groups) export(get_image) +export(get_integrations) export(get_job) export(get_job_list) export(get_jobs) diff --git a/R/get.R b/R/get.R index fded2a3b6..01e41f060 100644 --- a/R/get.R +++ b/R/get.R @@ -820,6 +820,8 @@ get_procs <- function(src) { #' Please see https://docs.posit.co/connect/user/oauth-integrations/#obtaining-a-viewer-oauth-access-token #' for more information. #' +#' @seealso [get_integrations()], [get_oauth_content_credentials()] +#' #' @export get_oauth_credentials <- function( connect, @@ -886,6 +888,8 @@ get_oauth_credentials <- function( #' Please see https://docs.posit.co/connect/user/oauth-integrations/#obtaining-a-service-account-oauth-access-token #' for more information. #' +#' @seealso [get_integrations()], [get_oauth_credentials()] +#' #' @export get_oauth_content_credentials <- function( connect, diff --git a/R/integrations.R b/R/integrations.R new file mode 100644 index 000000000..635df5327 --- /dev/null +++ b/R/integrations.R @@ -0,0 +1,97 @@ +#' List all OAuth integrations on the Connect server +#' +#' @description +#' Retrieve information about all OAuth integrations available to Posit Connect. +#' You must have administrator or publisher privileges to perform this action. +#' +#' @param client A `Connect` R6 client object. +#' +#' @return A list of OAuth integrations. Each integration is a list with the +#' following elements (all character strings unless indicated otherwise): +#' +#' * `id`: The internal identifier of this OAuth integration. +#' * `guid`: The GUID of this OAuth integration. +#' * `created_time`: The timestamp (RFC3339) indicating when this integration +#' was created. +#' * `updated_time`: The timestamp (RFC3339) indicating when this integration +#' was last updated +#' * `name`: A descriptive name to identify the OAuth integration. +#' * `description`: A brief text to describe the OAuth integration. +#' * `template`: The template used to configure this OAuth integration. +#' * `auth_type`: The authentication type indicates which OAuth flow is used by +#' this integration. +#' * `config`: A sub-list list with the OAuth integration configuration. Fields +#' differ between integrations. +#' +#' Use [as.data.frame()] or [tibble::as_tibble()] to convert to a data frame with +#' parsed types. In the resulting data frame: +#' +#' * `created_time` and `updated_time` are parsed to `POSIXct`. +#' * `config` remains as a list-column. +#' +#' @seealso [get_oauth_credentials()], [get_oauth_content_credentials()] +#' +#' @examples +#' \dontrun{ +#' client <- connect() +#' +#' # Fetch all OAuth integrations +#' integrations <- get_integrations(client) +#' +#' # Convert to tibble or data frame +#' integrations_df <- tibble::as_tibble(integrations) +#' } +#' +#' @export +get_integrations <- function(client) { + error_if_less_than(client$version, "2024.12.0") + integrations <- client$GET(v1_url("oauth", "integrations")) + class(integrations) <- c("connect_list_integrations", class(integrations)) + integrations +} + +#' Convert integrations data to a data frame +#' +#' @description +#' Converts an object returned by [get_integrations()] into a data frame with parsed +#' column types. +#' +#' @param x A `connect_list_integrations` object (from [get_integrations()]). +#' @param row.names Passed to [base::as.data.frame()]. +#' @param optional Passed to [base::as.data.frame()]. +#' @param ... Passed to [base::as.data.frame()]. +#' +#' @return A `data.frame` with one row per integration. +#' @export +#' @method as.data.frame connect_list_integrations +as.data.frame.connect_list_integrations <- function( + x, + row.names = NULL, + optional = FALSE, + ... +) { + integrations_df <- parse_connectapi_typed(x, connectapi_ptypes$integrations) + as.data.frame( + integrations_df, + row.names = row.names, + optional = optional, + ... + ) +} + +#' Convert integrations data to a tibble +#' +#' @description +#' Converts an object returned by [get_integrations()] to a tibble via +#' [as.data.frame.connect_list_integrations()]. +#' +#' @param x A `connect_list_integrations` object. +#' @param ... Passed to [as.data.frame()]. +#' +#' @return A tibble with one row per integration. +#' @export +#' @importFrom tibble as_tibble +#' @method as_tibble connect_list_integrations +as_tibble.connect_list_integrations <- function(x, ...) { + tibble::as_tibble(as.data.frame(x, ...)) +} diff --git a/R/ptype.R b/R/ptype.R index 7febeba8d..3600266a4 100644 --- a/R/ptype.R +++ b/R/ptype.R @@ -271,5 +271,16 @@ connectapi_ptypes <- list( name = NA_character_, version = NA_character_, hash = NA_character_ + ), + integrations = tibble::tibble( + id = NA_character_, + guid = NA_character_, + created_time = NA_datetime_, + updated_time = NA_datetime_, + name = NA_character_, + description = NA_character_, + template = NA_character_, + auth_type = NA_character_, + config = NA_list_ ) ) diff --git a/_pkgdown.yml b/_pkgdown.yml index d8f943c84..ae9f6d7d1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -69,6 +69,8 @@ reference: - starts_with("get") - as.data.frame.connect_list_hits - as_tibble.connect_list_hits + - as.data.frame.connect_list_integrations + - as_tibble.connect_list_integrations - title: "Other" desc: > diff --git a/man/as.data.frame.connect_list_integrations.Rd b/man/as.data.frame.connect_list_integrations.Rd new file mode 100644 index 000000000..1b255c3dd --- /dev/null +++ b/man/as.data.frame.connect_list_integrations.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/integrations.R +\name{as.data.frame.connect_list_integrations} +\alias{as.data.frame.connect_list_integrations} +\title{Convert integrations data to a data frame} +\usage{ +\method{as.data.frame}{connect_list_integrations}(x, row.names = NULL, optional = FALSE, ...) +} +\arguments{ +\item{x}{A \code{connect_list_integrations} object (from \code{\link[=get_integrations]{get_integrations()}}).} + +\item{row.names}{Passed to \code{\link[base:as.data.frame]{base::as.data.frame()}}.} + +\item{optional}{Passed to \code{\link[base:as.data.frame]{base::as.data.frame()}}.} + +\item{...}{Passed to \code{\link[base:as.data.frame]{base::as.data.frame()}}.} +} +\value{ +A \code{data.frame} with one row per integration. +} +\description{ +Converts an object returned by \code{\link[=get_integrations]{get_integrations()}} into a data frame with parsed +column types. +} diff --git a/man/as_tibble.connect_list_integrations.Rd b/man/as_tibble.connect_list_integrations.Rd new file mode 100644 index 000000000..51e6840ec --- /dev/null +++ b/man/as_tibble.connect_list_integrations.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/integrations.R +\name{as_tibble.connect_list_integrations} +\alias{as_tibble.connect_list_integrations} +\title{Convert integrations data to a tibble} +\usage{ +\method{as_tibble}{connect_list_integrations}(x, ...) +} +\arguments{ +\item{x}{A \code{connect_list_integrations} object.} + +\item{...}{Passed to \code{\link[=as.data.frame]{as.data.frame()}}.} +} +\value{ +A tibble with one row per integration. +} +\description{ +Converts an object returned by \code{\link[=get_integrations]{get_integrations()}} to a tibble via +\code{\link[=as.data.frame.connect_list_integrations]{as.data.frame.connect_list_integrations()}}. +} diff --git a/man/get_integrations.Rd b/man/get_integrations.Rd new file mode 100644 index 000000000..bbe7c8515 --- /dev/null +++ b/man/get_integrations.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/integrations.R +\name{get_integrations} +\alias{get_integrations} +\title{List all OAuth integrations on the Connect server} +\usage{ +get_integrations(client) +} +\arguments{ +\item{client}{A \code{Connect} R6 client object.} +} +\value{ +A list of OAuth integrations. Each integration is a list with the +following elements (all character strings unless indicated otherwise): +\itemize{ +\item \code{id}: The internal identifier of this OAuth integration. +\item \code{guid}: The GUID of this OAuth integration. +\item \code{created_time}: The timestamp (RFC3339) indicating when this integration +was created. +\item \code{updated_time}: The timestamp (RFC3339) indicating when this integration +was last updated +\item \code{name}: A descriptive name to identify the OAuth integration. +\item \code{description}: A brief text to describe the OAuth integration. +\item \code{template}: The template used to configure this OAuth integration. +\item \code{auth_type}: The authentication type indicates which OAuth flow is used by +this integration. +\item \code{config}: A sub-list list with the OAuth integration configuration. Fields +differ between integrations. +} + +Use \code{\link[=as.data.frame]{as.data.frame()}} or \code{\link[tibble:as_tibble]{tibble::as_tibble()}} to convert to a data frame with +parsed types. In the resulting data frame: +\itemize{ +\item \code{created_time} and \code{updated_time} are parsed to \code{POSIXct}. +\item \code{config} remains as a list-column. +} +} +\description{ +Retrieve information about all OAuth integrations available to Posit Connect. +You must have administrator or publisher privileges to perform this action. +} +\examples{ +\dontrun{ +client <- connect() + +# Fetch all OAuth integrations +integrations <- get_integrations(client) + +# Convert to tibble or data frame +integrations_df <- tibble::as_tibble(integrations) +} + +} +\seealso{ +\code{\link[=get_oauth_credentials]{get_oauth_credentials()}}, \code{\link[=get_oauth_content_credentials]{get_oauth_content_credentials()}} +} diff --git a/man/get_oauth_content_credentials.Rd b/man/get_oauth_content_credentials.Rd index fda3c2e29..812fe4644 100644 --- a/man/get_oauth_content_credentials.Rd +++ b/man/get_oauth_content_credentials.Rd @@ -57,3 +57,6 @@ function(req) { } } +\seealso{ +\code{\link[=get_integrations]{get_integrations()}}, \code{\link[=get_oauth_credentials]{get_oauth_credentials()}} +} diff --git a/man/get_oauth_credentials.Rd b/man/get_oauth_credentials.Rd index 05b54efb9..6fd71c5e9 100644 --- a/man/get_oauth_credentials.Rd +++ b/man/get_oauth_credentials.Rd @@ -55,3 +55,6 @@ function(req) { } } +\seealso{ +\code{\link[=get_integrations]{get_integrations()}}, \code{\link[=get_oauth_content_credentials]{get_oauth_content_credentials()}} +} diff --git a/tests/testthat/2024.08.0/__api__/server_settings.json b/tests/testthat/2024.08.0/__api__/server_settings.json index 2c63c0851..fa299e6b7 100644 --- a/tests/testthat/2024.08.0/__api__/server_settings.json +++ b/tests/testthat/2024.08.0/__api__/server_settings.json @@ -1,2 +1,139 @@ { + "hostname": "ip-10-0-35-196", + "version": "2025.07.0-dev+206-g8646c704d2", + "build": "v2025.06.0-206-g8646c704d2", + "about": "Posit Connect v2025.07.0-dev+206-g8646c704d2", + "authentication": { + "handles_credentials": false, + "handles_login": true, + "challenge_response_enabled": false, + "external_user_data": true, + "external_user_search": true, + "external_user_id": true, + "groups_enabled": true, + "external_group_search": false, + "external_group_members": false, + "external_group_id": false, + "external_group_owner": false, + "unique_usernames": true, + "name_editable_by": "provider", + "email_editable_by": "provider", + "username_editable_by": "adminandself", + "role_editable_by": "adminandself", + "name": "OAuth2", + "notice": "", + "warning_delay": 10, + "API_key_auth": true + }, + "license": { + "ts": 1753480753654, + "status": "activated", + "expiration": 1772064000000, + "days-left": 216, + "has-key": true, + "has-trial": false, + "tier": "advanced", + "sku-year": "2024", + "edition": "", + "cores": 0, + "connections": 0, + "type": "local", + "users": 0, + "user-activity-days": 365, + "users-grace": "true", + "shiny-users": 0, + "allow-apis": true, + "custom-branding": true, + "current-user-execution": true, + "anonymous-servers": true, + "unrestricted-servers": true, + "anonymous-branding": false, + "oauth-integrations": true, + "enable-launcher": true + }, + "license_expiration_ui_warning": true, + "users_limit_ui_warning": true, + "end_of_support_ui_warning": true, + "deprecated_settings": false, + "deprecated_settings_ui_warning": true, + "viewers_can_request_privileges": true, + "mail_all": false, + "mail_configured": true, + "public_warning": "", + "logged_in_warning": "", + "logout_url": "__logout__", + "metrics_rrd_enabled": true, + "metrics_instrumentation": true, + "customized_landing": false, + "self_registration": false, + "prohibited_usernames": [ + "connect", + "apps", + "users", + "groups", + "setpassword", + "user-completion", + "confirm", + "recent", + "reports", + "plots", + "unpublished", + "settings", + "metrics", + "tokens", + "help", + "login", + "welcome", + "register", + "resetpassword", + "content" + ], + "username_validator": "default", + "viewers_can_only_see_themselves": false, + "http_warning": false, + "runtimes": [ + "R", + "Python", + "Quarto", + "TensorFlow" + ], + "default_content_list_view": "compact", + "maximum_app_image_size": 10000000, + "server_settings_toggler": true, + "git_enabled": true, + "git_available": true, + "dashboard_path": "/connect", + "system_display_name": "Posit Connect", + "hide_viewer_documentation": false, + "jump_start_enabled": true, + "permission_request": true, + "tableau_integration_enabled": true, + "execution_type": "native", + "enable_runtime_cache_management": true, + "default_environment_selection_enabled": true, + "default_environment_management_selection": true, + "default_r_environment_management": true, + "default_py_environment_management": true, + "new_parameterization_enabled": false, + "use_window_location": false, + "oauth_integrations_enabled": true, + "gallery_enabled": true, + "gallery_feeds": [ + { + "id": "default", + "name": "Posit Connect Gallery", + "description": "Official extensions for Connect", + "url": "https://raw.githubusercontent.com/posit-dev/connect-extensions/refs/heads/main/extensions.json" + } + ], + "whats_new_enabled": true, + "early_access": { + "content_preview": true, + "execution_environments_access_control": true, + "execution_environments_volume_mounts": true, + "freeze_bundles": false, + "share_link": true + }, + "content_list_updates": true, + "deploy_external_content": false } diff --git a/tests/testthat/2024.08.0/__api__/v1/oauth/integrations.json b/tests/testthat/2024.08.0/__api__/v1/oauth/integrations.json new file mode 100644 index 000000000..e14e46737 --- /dev/null +++ b/tests/testthat/2024.08.0/__api__/v1/oauth/integrations.json @@ -0,0 +1,579 @@ +[ + { + "id": "4", + "guid": "f8688548", + "created_time": "2024-08-01T20:14:31Z", + "updated_time": "2025-03-25T19:08:26Z", + "name": "gh app 1", + "description": "with refresh support ", + "template": "custom", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://github.com/login/oauth/authorize", + "client_id": "Iv23liKagsdSr7lkx7M5", + "scopes": "offline_access openid profile email repo read:user", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://github.com/login/oauth/access_token", + "use_pkce": true + } + }, + { + "id": "5", + "guid": "cacc0cf1", + "created_time": "2024-09-09T15:01:29Z", + "updated_time": "2025-03-25T19:07:01Z", + "name": "PBL - Snowflake test integration", + "description": "TESTING CHANGING A DESCRIPTION", + "template": "snowflake", + "auth_type": "Viewer", + "config": { + "account_url": "https://duloftf-posit-software-pbc-dev.snowflakecomputing.com", + "auth_mode": "Confidential", + "client_id": "6VHVy2TdBh0f/3PR4mT9p+0d9bA=", + "scopes": "refresh_token" + } + }, + { + "id": "7", + "guid": "d70da2a9", + "created_time": "2024-09-23T14:21:49Z", + "updated_time": "2024-09-23T14:43:57Z", + "name": "Toni Snowflake", + "description": "Testing out Snowflake R example.", + "template": "snowflake", + "auth_type": "Viewer", + "config": { + "account_url": "https://grb51556.us-east-1.snowflakecomputing.com", + "auth_mode": "Confidential", + "client_id": "urag20JXr3sR96Spk5fYQJN8pXc=", + "scopes": "refresh_token" + } + }, + { + "id": "10", + "guid": "c27739a9", + "created_time": "2024-10-04T18:50:43Z", + "updated_time": "2025-03-25T19:08:06Z", + "name": "Chaita snowflake ", + "description": "test ", + "template": "snowflake", + "auth_type": "Viewer", + "config": { + "account_url": "https://duloftf-posit-software-pbc-dev.snowflakecomputing.com", + "auth_mode": "Confidential", + "client_id": "f9JbfFCtGn5bku4bjA2SVapYVok=", + "scopes": "refresh_token" + } + }, + { + "id": "12", + "guid": "ba8de30f", + "created_time": "2024-11-01T20:32:20Z", + "updated_time": "2024-11-01T20:44:11Z", + "name": "Salesforce", + "description": "or is it agentforce? the important thing is that force is being applied.", + "template": "custom", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://posit2-dev-ed.develop.my.salesforce.com/services/oauth2/authorize", + "client_id": "3MVG9XgkMlifdwVAgRhENDBlg6io7Da5H6OOstcXNz9GiZ.crjZumgulm_eV0VTfRnoCrmoTjQhNRCVLoftgt", + "scopes": "offline_access openid full api refresh_token", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://posit2-dev-ed.develop.my.salesforce.com/services/oauth2/token", + "use_pkce": true + } + }, + { + "id": "18", + "guid": "806756a1", + "created_time": "2024-11-07T16:15:47Z", + "updated_time": "2024-11-08T17:17:34Z", + "name": "Chaita-Github-custom", + "description": "custom github integration ( wiki-test-github app name)", + "template": "custom", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://github.com/login/oauth/authorize", + "client_id": "Iv23liFk3dHL5pNvxg6O", + "scopes": "offline_access openid profile email repo read:user", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://github.com/login/oauth/access_token", + "use_pkce": true + } + }, + { + "id": "21", + "guid": "04ede72d", + "created_time": "2024-11-08T15:22:31Z", + "updated_time": "2025-03-25T19:07:13Z", + "name": "PBL - Azure", + "description": "TESTING CHANGING THIS DESCRIPTION", + "template": "azure", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "client_id": "539b4ade", + "scopes": "offline_access 2ff814a6/.default", + "tenant_id": "b0b52785" + } + }, + { + "id": "22", + "guid": "b794e18e", + "created_time": "2024-11-08T17:24:57Z", + "updated_time": "2024-11-08T17:24:57Z", + "name": "Chaita-Snowflake-custom", + "description": "custom integration", + "template": "custom", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://duloftf-posit-software-pbc-dev.snowflakecomputing.com/oauth/authorize", + "client_id": "MMzb08JZP1DHIevEUQIdyzan8jU=", + "scopes": "refresh_token", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://duloftf-posit-software-pbc-dev.snowflakecomputing.com/oauth/token-request", + "use_pkce": true + } + }, + { + "id": "25", + "guid": "0f1c1315", + "created_time": "2024-11-12T23:59:54Z", + "updated_time": "2025-03-27T15:20:00Z", + "name": "Fake Integration – Now with an Impressively Long Title That May or May Not Break the UI (Let’s Find Out)", + "description": "A totally real fake-integration with an unnecessarily long description—just to see how badly we can break the UI (for science, of course). Testing how descriptions behave when they get a little out of hand.", + "template": "github", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "client_id": "test1234123123312312" + } + }, + { + "id": "28", + "guid": "8013035f", + "created_time": "2024-11-21T03:20:58Z", + "updated_time": "2025-03-25T19:07:30Z", + "name": "Chaita-Google1 ", + "description": "BigQuery Google1 test", + "template": "bigquery", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "client_id": "854262705359-ng1hudhn3ec37a2dj8ich48ic6aipqfb.apps.googleusercontent.com", + "scopes": "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.insertdata" + } + }, + { + "id": "29", + "guid": "f2130379", + "created_time": "2024-11-26T18:46:10Z", + "updated_time": "2024-12-04T16:55:15Z", + "name": "Azure - Chaita", + "description": "Azure", + "template": "azure", + "auth_type": "Viewer", + "config": { + "auth_mode": "Public", + "auth_type": "Viewer", + "client_id": "f517375e", + "scopes": "offline_access 2ff814a6/.default", + "tenant_id": "b0b52785" + } + }, + { + "id": "30", + "guid": "c854004b", + "created_time": "2024-12-02T21:49:47Z", + "updated_time": "2024-12-11T14:28:56Z", + "name": "Azure Databricks Service Account", + "description": "Azure Databricks integration that uses a client credentials flow.", + "template": "azure", + "auth_type": "Service Account", + "config": { + "auth_type": "Service Account", + "client_id": "5f3e3af1", + "scopes": "offline_access 2ff814a6/.default", + "tenant_id": "b0b52785" + } + }, + { + "id": "33", + "guid": "d211793f", + "created_time": "2024-12-13T16:42:15Z", + "updated_time": "2025-03-25T18:54:13Z", + "name": "positpbc-dev-ed Salesforce ", + "description": "Login to Salesforce dashboard via https://positpbc-dev-ed.develop.lightning.force.com/lightning/page/home \nSalesforce domain / login URL used in code: https://positpbc-dev-ed.develop.my.salesforce.com", + "template": "salesforce", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "client_id": "3MVG91oqviqJKoEF_.qUhZ.ZDv34yklQvJ20VqZ_ai_6O3pqPAFN2pr7Nk6PVpfGHRWhdRCxCOX3Zho.ciJ0D", + "scopes": "refresh_token offline_access api web", + "sf_host_domain": "https://positpbc-dev-ed.develop.my.salesforce.com" + } + }, + { + "id": "34", + "guid": "85c0f968", + "created_time": "2024-12-13T18:21:42Z", + "updated_time": "2024-12-18T20:15:35Z", + "name": "Azure-Service-Account-Chaita", + "description": "won't work it will only work for localhost:3939", + "template": "azure", + "auth_type": "Service Account", + "config": { + "auth_type": "Service Account", + "client_id": "48d61bca", + "scopes": "2ff814a6/.default offline_access", + "tenant_id": "b0b52785" + } + }, + { + "id": "37", + "guid": "14719ca2", + "created_time": "2024-12-17T16:11:31Z", + "updated_time": "2025-01-22T22:16:01Z", + "name": "Toni Azure Public Auth", + "description": "", + "template": "azure", + "auth_type": "Viewer", + "config": { + "auth_mode": "Public", + "auth_type": "Viewer", + "client_id": "d898af1e", + "scopes": "offline_access .default", + "tenant_id": "b0b52785" + } + }, + { + "id": "39", + "guid": "cfd4d4ab", + "created_time": "2025-01-16T02:45:13Z", + "updated_time": "2025-01-16T02:45:13Z", + "name": "Connect API - Viewer Role", + "description": "", + "template": "connect", + "auth_type": "Visitor API Key", + "config": { + "max_role": "Viewer" + } + }, + { + "id": "40", + "guid": "48617a66", + "created_time": "2025-01-16T02:45:35Z", + "updated_time": "2025-01-16T02:45:35Z", + "name": "Connect API - Publisher Role", + "description": "", + "template": "connect", + "auth_type": "Visitor API Key", + "config": { + "max_role": "Publisher" + } + }, + { + "id": "41", + "guid": "f42342d6", + "created_time": "2025-01-16T02:45:49Z", + "updated_time": "2025-01-16T02:45:49Z", + "name": "Connect API - Admin Role", + "description": "", + "template": "connect", + "auth_type": "Visitor API Key", + "config": { + "max_role": "Admin" + } + }, + { + "id": "45", + "guid": "9c2e4bde", + "created_time": "2025-01-28T21:27:57Z", + "updated_time": "2025-01-28T21:27:57Z", + "name": "Sharepoint", + "description": "testing with Toni's creds", + "template": "sharepoint", + "auth_type": "Viewer", + "config": { + "auth_mode": "Public", + "auth_type": "Viewer", + "client_id": "f8bbe4ff", + "scopes": "00000003/.default offline_access", + "tenant_id": "eacb8f40" + } + }, + { + "id": "46", + "guid": "56928e28", + "created_time": "2025-02-11T12:40:28Z", + "updated_time": "2025-02-11T12:40:28Z", + "name": "Chaita-Salesforce", + "description": "should work", + "template": "salesforce", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "client_id": "3MVG91oqviqJKoEF_.qUhZ.ZDv34yklQvJ20VqZ_ai_6O3pqPAFN2pr7Nk6PVpfGHRWhdRCxCOX3Zho.ciJ0D", + "scopes": "refresh_token offline_access api web", + "sf_host_domain": "https://positpbc-dev-ed.develop.my.salesforce.com" + } + }, + { + "id": "48", + "guid": "2e0a6448", + "created_time": "2025-03-17T16:06:17Z", + "updated_time": "2025-03-27T15:20:44Z", + "name": "AWS-Azure-Chaita", + "description": "Should work with localhost:3939", + "template": "aws", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/authorize", + "client_id": "e5652005", + "role_arn": "arn:aws:iam::440849847947:role/PositConnect-OIDC-Role", + "scopes": "offline_access openid profile email", + "session_duration": "3600", + "sts_region": "us-east-1", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/token", + "use_pkce": true + } + }, + { + "id": "49", + "guid": "82031f9f", + "created_time": "2025-03-17T16:09:00Z", + "updated_time": "2025-03-17T16:09:00Z", + "name": "AWS-Okta-Chaita", + "description": "", + "template": "aws", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://dev-45652974.okta.com/oauth2/v1/authorize", + "client_id": "0oantnx4gsTh7zmG35d7", + "role_arn": "arn:aws:iam::440849847947:role/OktaAccessRole", + "scopes": "offline_access openid profile email", + "session_duration": "3600", + "sts_region": "us-east-1", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://dev-45652974.okta.com/oauth2/v1/token", + "use_pkce": true + } + }, + { + "id": "50", + "guid": "cfaef603", + "created_time": "2025-03-24T19:56:34Z", + "updated_time": "2025-03-24T19:56:34Z", + "name": "AWS-Azure-S3", + "description": "", + "template": "aws", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/authorize", + "client_id": "a3ca211e", + "role_arn": "arn:aws:iam::440849847947:role/mattconflitti-s3-FullAccess", + "scopes": "offline_access openid profile email", + "session_duration": "3600", + "sts_region": "us-east-1", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/token", + "use_pkce": true + } + }, + { + "id": "52", + "guid": "e57705ac", + "created_time": "2025-03-25T19:05:55Z", + "updated_time": "2025-03-25T19:06:33Z", + "name": "Zack test", + "description": "123 abc", + "template": "github", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "client_id": "arstarstarstarstarst" + } + }, + { + "id": "53", + "guid": "3c6d320e", + "created_time": "2025-03-25T19:36:03Z", + "updated_time": "2025-03-25T19:36:03Z", + "name": "AWS Bedrock Full Access", + "description": "", + "template": "aws", + "auth_type": "Service Account", + "config": { + "auth_type": "Service Account", + "role_arn": "arn:aws:iam::440849847947:role/mattconflitti-bedrock-FullAccess", + "session_duration": "3600", + "sts_region": "us-east-1" + } + }, + { + "id": "54", + "guid": "0ce09de1", + "created_time": "2025-03-25T19:54:01Z", + "updated_time": "2025-03-25T19:54:01Z", + "name": "AWS S3 Full Access", + "description": "", + "template": "aws", + "auth_type": "Service Account", + "config": { + "auth_type": "Service Account", + "role_arn": "arn:aws:iam::440849847947:role/mattconflitti-s3-FullAccess", + "session_duration": "3600", + "sts_region": "us-east-1" + } + }, + { + "id": "55", + "guid": "d84381b3", + "created_time": "2025-03-27T03:00:30Z", + "updated_time": "2025-03-27T03:00:30Z", + "name": "AWS Bedrock + S3", + "description": "AWS Bedrock plus S3 Full Access", + "template": "aws", + "auth_type": "Service Account", + "config": { + "auth_type": "Service Account", + "role_arn": "arn:aws:iam::440849847947:role/mattc-bedrock-s3", + "session_duration": "3600", + "sts_region": "us-east-1" + } + }, + { + "id": "57", + "guid": "7513cdea", + "created_time": "2025-05-15T19:12:11Z", + "updated_time": "2025-05-15T19:12:11Z", + "name": "DCM - test visitor key", + "description": "", + "template": "connect", + "auth_type": "Visitor API Key", + "config": { + "max_role": "Viewer" + } + }, + { + "id": "58", + "guid": "e14305d5", + "created_time": "2025-05-27T18:22:40Z", + "updated_time": "2025-05-27T18:22:40Z", + "name": "Connect Visitor API Key", + "description": "Provide a short-lived Connect API key to visitors. This is useful for apps that interact with Connect programmatically.", + "template": "connect", + "auth_type": "Visitor API Key", + "config": { + "max_role": "Admin" + } + }, + { + "id": "59", + "guid": "4d49f399", + "created_time": "2025-06-10T17:13:49Z", + "updated_time": "2025-06-17T20:23:15Z", + "name": "Azure OpenAI - Viewer", + "description": "Integration with Azure OpenAI services using OAuth authentication", + "template": "custom", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/authorize", + "client_id": "52818ef8", + "scopes": "https://cognitiveservices.azure.com/.default offline_access", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/token", + "use_pkce": true + } + }, + { + "id": "60", + "guid": "c0dad604", + "created_time": "2025-06-17T20:12:31Z", + "updated_time": "2025-06-17T20:23:28Z", + "name": "Azure OpenAI - service account", + "description": "Integration with Azure OpenAI services using service account authentication", + "template": "custom", + "auth_type": "Service Account", + "config": { + "auth_type": "Service Account", + "authorization_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/authorize", + "client_id": "1381d669", + "scopes": "https://cognitiveservices.azure.com/.default offline_access", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/token" + } + }, + { + "id": "61", + "guid": "1fcd4f88", + "created_time": "2025-06-24T17:15:08Z", + "updated_time": "2025-06-24T17:15:58Z", + "name": "Azure OpenAI ", + "description": "", + "template": "azure-openai", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "client_id": "faf78b60", + "scopes": "https://cognitiveservices.azure.com/.default offline_access", + "tenant_id": "b0b52785" + } + }, + { + "id": "62", + "guid": "f6716d3f", + "created_time": "2025-07-07T20:48:06Z", + "updated_time": "2025-07-07T22:07:34Z", + "name": "Vertex AI", + "description": "", + "template": "custom", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://accounts.google.com/o/oauth2/v2/auth", + "client_id": "196021606923-kk10h9movm1vf3b55jho3cd2tvbhrpl5.apps.googleusercontent.com", + "scopes": "https://www.googleapis.com/auth/cloud-platform", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://oauth2.googleapis.com/token", + "use_pkce": true + } + }, + { + "id": "64", + "guid": "5a57f4cf", + "created_time": "2025-07-22T17:50:21Z", + "updated_time": "2025-07-22T17:50:21Z", + "name": "Vertex AI", + "description": "", + "template": "vertex-ai", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "client_id": "196021606923-0daalcru5ah7k51e49f1i1oneeajuft6.apps.googleusercontent.com", + "scopes": "https://www.googleapis.com/auth/cloud-platform" + } + } +] diff --git a/tests/testthat/2024.12.0/__api__/server_settings.json b/tests/testthat/2024.12.0/__api__/server_settings.json new file mode 100644 index 000000000..716ea04ef --- /dev/null +++ b/tests/testthat/2024.12.0/__api__/server_settings.json @@ -0,0 +1,3 @@ +{ + "version": "2024.12.0" +} diff --git a/tests/testthat/2024.12.0/__api__/v1/oauth/integrations.json b/tests/testthat/2024.12.0/__api__/v1/oauth/integrations.json new file mode 100644 index 000000000..926a60fc7 --- /dev/null +++ b/tests/testthat/2024.12.0/__api__/v1/oauth/integrations.json @@ -0,0 +1,38 @@ +[ + { + "id": "4", + "guid": "f8688548", + "created_time": "2024-08-01T20:14:31Z", + "updated_time": "2025-03-25T19:08:26Z", + "name": "GitHub Integration", + "description": "with refresh support ", + "template": "custom", + "auth_type": "Viewer", + "config": { + "auth_mode": "Confidential", + "auth_type": "Viewer", + "authorization_uri": "https://github.com/login/oauth/authorize", + "client_id": "client_id_123", + "scopes": "offline_access openid profile email repo read:user", + "token_endpoint_auth_method": "client_secret_post", + "token_uri": "https://github.com/login/oauth/access_token", + "use_pkce": true + } + }, + { + "id": "5", + "guid": "cacc0cf1", + "created_time": "2024-09-09T15:01:29Z", + "updated_time": "2025-03-25T19:07:01Z", + "name": "Example Service", + "description": "The service provides utility to your company", + "template": "generic_service", + "auth_type": "Viewer", + "config": { + "account_url": "https://service.example.com", + "auth_mode": "Confidential", + "client_id": "client_id_456", + "scopes": "refresh_token" + } + } +] diff --git a/tests/testthat/2024.12.0/__ping__.json b/tests/testthat/2024.12.0/__ping__.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/tests/testthat/2024.12.0/__ping__.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/testthat/test-integrations.R b/tests/testthat/test-integrations.R new file mode 100644 index 000000000..30127a2dd --- /dev/null +++ b/tests/testthat/test-integrations.R @@ -0,0 +1,48 @@ +with_mock_dir("2024.12.0", { + test_that("get_integrations() gets integrations", { + client <- Connect$new(server = "https://connect.example", api_key = "fake") + integrations <- get_integrations(client) + expect_true(inherits(integrations, "connect_list_integrations")) + + # Check a few fields + expect_equal(integrations[[1]]$name, "GitHub Integration") + expect_equal(integrations[[2]]$updated_time, "2025-03-25T19:07:01Z") + expect_equal(integrations[[1]]$config$client_id, "client_id_123") + }) + + test_that("get_integrations() can be converted to a data frame correctly", { + client <- Connect$new(server = "https://connect.example", api_key = "fake") + integrations_df <- get_integrations(client) |> + as_tibble() + expect_named( + integrations_df, + c( + "id", + "guid", + "created_time", + "updated_time", + "name", + "description", + "template", + "auth_type", + "config" + ) + ) + expect_equal( + integrations_df$description, + c( + "with refresh support ", + "The service provides utility to your company" + ) + ) + }) +}) + +test_that("get_integrations() errs on older Connect versions", { + 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" + ) +}) From 47b5138b4d158ffff78faa71a9e0b14877459dc2 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Fri, 25 Jul 2025 18:58:18 -0400 Subject: [PATCH 2/6] update NEWS --- NEWS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.md b/NEWS.md index b31cd8fae..2bc8f4020 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,9 @@ - New `get_usage()` function returns content usage data from Connect's `GET v1/instrumentation/content/hits` endpoint on Connect v2025.04.0 and higher. (#390) +- New `get_integrations()` function lists all OAuth integrations available on the + Connect server from the `GET v1/oauth/integrations` endpoint on Connect v2024.12.0 + and higher. ## Enhancements and fixes From 2f393c704ff0d848ebc7d4ba260018c4621d4218 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Mon, 28 Jul 2025 13:56:56 -0400 Subject: [PATCH 3/6] revert accidental change to mock --- NEWS.md | 2 +- .../2024.08.0/__api__/server_settings.json | 137 ------------------ 2 files changed, 1 insertion(+), 138 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2bc8f4020..66b7dd157 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,7 +7,7 @@ (#390) - New `get_integrations()` function lists all OAuth integrations available on the Connect server from the `GET v1/oauth/integrations` endpoint on Connect v2024.12.0 - and higher. + and higher. (#413) ## Enhancements and fixes diff --git a/tests/testthat/2024.08.0/__api__/server_settings.json b/tests/testthat/2024.08.0/__api__/server_settings.json index fa299e6b7..2c63c0851 100644 --- a/tests/testthat/2024.08.0/__api__/server_settings.json +++ b/tests/testthat/2024.08.0/__api__/server_settings.json @@ -1,139 +1,2 @@ { - "hostname": "ip-10-0-35-196", - "version": "2025.07.0-dev+206-g8646c704d2", - "build": "v2025.06.0-206-g8646c704d2", - "about": "Posit Connect v2025.07.0-dev+206-g8646c704d2", - "authentication": { - "handles_credentials": false, - "handles_login": true, - "challenge_response_enabled": false, - "external_user_data": true, - "external_user_search": true, - "external_user_id": true, - "groups_enabled": true, - "external_group_search": false, - "external_group_members": false, - "external_group_id": false, - "external_group_owner": false, - "unique_usernames": true, - "name_editable_by": "provider", - "email_editable_by": "provider", - "username_editable_by": "adminandself", - "role_editable_by": "adminandself", - "name": "OAuth2", - "notice": "", - "warning_delay": 10, - "API_key_auth": true - }, - "license": { - "ts": 1753480753654, - "status": "activated", - "expiration": 1772064000000, - "days-left": 216, - "has-key": true, - "has-trial": false, - "tier": "advanced", - "sku-year": "2024", - "edition": "", - "cores": 0, - "connections": 0, - "type": "local", - "users": 0, - "user-activity-days": 365, - "users-grace": "true", - "shiny-users": 0, - "allow-apis": true, - "custom-branding": true, - "current-user-execution": true, - "anonymous-servers": true, - "unrestricted-servers": true, - "anonymous-branding": false, - "oauth-integrations": true, - "enable-launcher": true - }, - "license_expiration_ui_warning": true, - "users_limit_ui_warning": true, - "end_of_support_ui_warning": true, - "deprecated_settings": false, - "deprecated_settings_ui_warning": true, - "viewers_can_request_privileges": true, - "mail_all": false, - "mail_configured": true, - "public_warning": "", - "logged_in_warning": "", - "logout_url": "__logout__", - "metrics_rrd_enabled": true, - "metrics_instrumentation": true, - "customized_landing": false, - "self_registration": false, - "prohibited_usernames": [ - "connect", - "apps", - "users", - "groups", - "setpassword", - "user-completion", - "confirm", - "recent", - "reports", - "plots", - "unpublished", - "settings", - "metrics", - "tokens", - "help", - "login", - "welcome", - "register", - "resetpassword", - "content" - ], - "username_validator": "default", - "viewers_can_only_see_themselves": false, - "http_warning": false, - "runtimes": [ - "R", - "Python", - "Quarto", - "TensorFlow" - ], - "default_content_list_view": "compact", - "maximum_app_image_size": 10000000, - "server_settings_toggler": true, - "git_enabled": true, - "git_available": true, - "dashboard_path": "/connect", - "system_display_name": "Posit Connect", - "hide_viewer_documentation": false, - "jump_start_enabled": true, - "permission_request": true, - "tableau_integration_enabled": true, - "execution_type": "native", - "enable_runtime_cache_management": true, - "default_environment_selection_enabled": true, - "default_environment_management_selection": true, - "default_r_environment_management": true, - "default_py_environment_management": true, - "new_parameterization_enabled": false, - "use_window_location": false, - "oauth_integrations_enabled": true, - "gallery_enabled": true, - "gallery_feeds": [ - { - "id": "default", - "name": "Posit Connect Gallery", - "description": "Official extensions for Connect", - "url": "https://raw.githubusercontent.com/posit-dev/connect-extensions/refs/heads/main/extensions.json" - } - ], - "whats_new_enabled": true, - "early_access": { - "content_preview": true, - "execution_environments_access_control": true, - "execution_environments_volume_mounts": true, - "freeze_bundles": false, - "share_link": true - }, - "content_list_updates": true, - "deploy_external_content": false } From 0c1edd0e8daf4c042c6d36b838683e923d8f8250 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Mon, 28 Jul 2025 13:58:48 -0400 Subject: [PATCH 4/6] remove unused mock --- .../__api__/v1/oauth/integrations.json | 579 ------------------ 1 file changed, 579 deletions(-) delete mode 100644 tests/testthat/2024.08.0/__api__/v1/oauth/integrations.json diff --git a/tests/testthat/2024.08.0/__api__/v1/oauth/integrations.json b/tests/testthat/2024.08.0/__api__/v1/oauth/integrations.json deleted file mode 100644 index e14e46737..000000000 --- a/tests/testthat/2024.08.0/__api__/v1/oauth/integrations.json +++ /dev/null @@ -1,579 +0,0 @@ -[ - { - "id": "4", - "guid": "f8688548", - "created_time": "2024-08-01T20:14:31Z", - "updated_time": "2025-03-25T19:08:26Z", - "name": "gh app 1", - "description": "with refresh support ", - "template": "custom", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "authorization_uri": "https://github.com/login/oauth/authorize", - "client_id": "Iv23liKagsdSr7lkx7M5", - "scopes": "offline_access openid profile email repo read:user", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://github.com/login/oauth/access_token", - "use_pkce": true - } - }, - { - "id": "5", - "guid": "cacc0cf1", - "created_time": "2024-09-09T15:01:29Z", - "updated_time": "2025-03-25T19:07:01Z", - "name": "PBL - Snowflake test integration", - "description": "TESTING CHANGING A DESCRIPTION", - "template": "snowflake", - "auth_type": "Viewer", - "config": { - "account_url": "https://duloftf-posit-software-pbc-dev.snowflakecomputing.com", - "auth_mode": "Confidential", - "client_id": "6VHVy2TdBh0f/3PR4mT9p+0d9bA=", - "scopes": "refresh_token" - } - }, - { - "id": "7", - "guid": "d70da2a9", - "created_time": "2024-09-23T14:21:49Z", - "updated_time": "2024-09-23T14:43:57Z", - "name": "Toni Snowflake", - "description": "Testing out Snowflake R example.", - "template": "snowflake", - "auth_type": "Viewer", - "config": { - "account_url": "https://grb51556.us-east-1.snowflakecomputing.com", - "auth_mode": "Confidential", - "client_id": "urag20JXr3sR96Spk5fYQJN8pXc=", - "scopes": "refresh_token" - } - }, - { - "id": "10", - "guid": "c27739a9", - "created_time": "2024-10-04T18:50:43Z", - "updated_time": "2025-03-25T19:08:06Z", - "name": "Chaita snowflake ", - "description": "test ", - "template": "snowflake", - "auth_type": "Viewer", - "config": { - "account_url": "https://duloftf-posit-software-pbc-dev.snowflakecomputing.com", - "auth_mode": "Confidential", - "client_id": "f9JbfFCtGn5bku4bjA2SVapYVok=", - "scopes": "refresh_token" - } - }, - { - "id": "12", - "guid": "ba8de30f", - "created_time": "2024-11-01T20:32:20Z", - "updated_time": "2024-11-01T20:44:11Z", - "name": "Salesforce", - "description": "or is it agentforce? the important thing is that force is being applied.", - "template": "custom", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "authorization_uri": "https://posit2-dev-ed.develop.my.salesforce.com/services/oauth2/authorize", - "client_id": "3MVG9XgkMlifdwVAgRhENDBlg6io7Da5H6OOstcXNz9GiZ.crjZumgulm_eV0VTfRnoCrmoTjQhNRCVLoftgt", - "scopes": "offline_access openid full api refresh_token", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://posit2-dev-ed.develop.my.salesforce.com/services/oauth2/token", - "use_pkce": true - } - }, - { - "id": "18", - "guid": "806756a1", - "created_time": "2024-11-07T16:15:47Z", - "updated_time": "2024-11-08T17:17:34Z", - "name": "Chaita-Github-custom", - "description": "custom github integration ( wiki-test-github app name)", - "template": "custom", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "authorization_uri": "https://github.com/login/oauth/authorize", - "client_id": "Iv23liFk3dHL5pNvxg6O", - "scopes": "offline_access openid profile email repo read:user", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://github.com/login/oauth/access_token", - "use_pkce": true - } - }, - { - "id": "21", - "guid": "04ede72d", - "created_time": "2024-11-08T15:22:31Z", - "updated_time": "2025-03-25T19:07:13Z", - "name": "PBL - Azure", - "description": "TESTING CHANGING THIS DESCRIPTION", - "template": "azure", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "client_id": "539b4ade", - "scopes": "offline_access 2ff814a6/.default", - "tenant_id": "b0b52785" - } - }, - { - "id": "22", - "guid": "b794e18e", - "created_time": "2024-11-08T17:24:57Z", - "updated_time": "2024-11-08T17:24:57Z", - "name": "Chaita-Snowflake-custom", - "description": "custom integration", - "template": "custom", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "authorization_uri": "https://duloftf-posit-software-pbc-dev.snowflakecomputing.com/oauth/authorize", - "client_id": "MMzb08JZP1DHIevEUQIdyzan8jU=", - "scopes": "refresh_token", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://duloftf-posit-software-pbc-dev.snowflakecomputing.com/oauth/token-request", - "use_pkce": true - } - }, - { - "id": "25", - "guid": "0f1c1315", - "created_time": "2024-11-12T23:59:54Z", - "updated_time": "2025-03-27T15:20:00Z", - "name": "Fake Integration – Now with an Impressively Long Title That May or May Not Break the UI (Let’s Find Out)", - "description": "A totally real fake-integration with an unnecessarily long description—just to see how badly we can break the UI (for science, of course). Testing how descriptions behave when they get a little out of hand.", - "template": "github", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "client_id": "test1234123123312312" - } - }, - { - "id": "28", - "guid": "8013035f", - "created_time": "2024-11-21T03:20:58Z", - "updated_time": "2025-03-25T19:07:30Z", - "name": "Chaita-Google1 ", - "description": "BigQuery Google1 test", - "template": "bigquery", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "client_id": "854262705359-ng1hudhn3ec37a2dj8ich48ic6aipqfb.apps.googleusercontent.com", - "scopes": "https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/bigquery.insertdata" - } - }, - { - "id": "29", - "guid": "f2130379", - "created_time": "2024-11-26T18:46:10Z", - "updated_time": "2024-12-04T16:55:15Z", - "name": "Azure - Chaita", - "description": "Azure", - "template": "azure", - "auth_type": "Viewer", - "config": { - "auth_mode": "Public", - "auth_type": "Viewer", - "client_id": "f517375e", - "scopes": "offline_access 2ff814a6/.default", - "tenant_id": "b0b52785" - } - }, - { - "id": "30", - "guid": "c854004b", - "created_time": "2024-12-02T21:49:47Z", - "updated_time": "2024-12-11T14:28:56Z", - "name": "Azure Databricks Service Account", - "description": "Azure Databricks integration that uses a client credentials flow.", - "template": "azure", - "auth_type": "Service Account", - "config": { - "auth_type": "Service Account", - "client_id": "5f3e3af1", - "scopes": "offline_access 2ff814a6/.default", - "tenant_id": "b0b52785" - } - }, - { - "id": "33", - "guid": "d211793f", - "created_time": "2024-12-13T16:42:15Z", - "updated_time": "2025-03-25T18:54:13Z", - "name": "positpbc-dev-ed Salesforce ", - "description": "Login to Salesforce dashboard via https://positpbc-dev-ed.develop.lightning.force.com/lightning/page/home \nSalesforce domain / login URL used in code: https://positpbc-dev-ed.develop.my.salesforce.com", - "template": "salesforce", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "client_id": "3MVG91oqviqJKoEF_.qUhZ.ZDv34yklQvJ20VqZ_ai_6O3pqPAFN2pr7Nk6PVpfGHRWhdRCxCOX3Zho.ciJ0D", - "scopes": "refresh_token offline_access api web", - "sf_host_domain": "https://positpbc-dev-ed.develop.my.salesforce.com" - } - }, - { - "id": "34", - "guid": "85c0f968", - "created_time": "2024-12-13T18:21:42Z", - "updated_time": "2024-12-18T20:15:35Z", - "name": "Azure-Service-Account-Chaita", - "description": "won't work it will only work for localhost:3939", - "template": "azure", - "auth_type": "Service Account", - "config": { - "auth_type": "Service Account", - "client_id": "48d61bca", - "scopes": "2ff814a6/.default offline_access", - "tenant_id": "b0b52785" - } - }, - { - "id": "37", - "guid": "14719ca2", - "created_time": "2024-12-17T16:11:31Z", - "updated_time": "2025-01-22T22:16:01Z", - "name": "Toni Azure Public Auth", - "description": "", - "template": "azure", - "auth_type": "Viewer", - "config": { - "auth_mode": "Public", - "auth_type": "Viewer", - "client_id": "d898af1e", - "scopes": "offline_access .default", - "tenant_id": "b0b52785" - } - }, - { - "id": "39", - "guid": "cfd4d4ab", - "created_time": "2025-01-16T02:45:13Z", - "updated_time": "2025-01-16T02:45:13Z", - "name": "Connect API - Viewer Role", - "description": "", - "template": "connect", - "auth_type": "Visitor API Key", - "config": { - "max_role": "Viewer" - } - }, - { - "id": "40", - "guid": "48617a66", - "created_time": "2025-01-16T02:45:35Z", - "updated_time": "2025-01-16T02:45:35Z", - "name": "Connect API - Publisher Role", - "description": "", - "template": "connect", - "auth_type": "Visitor API Key", - "config": { - "max_role": "Publisher" - } - }, - { - "id": "41", - "guid": "f42342d6", - "created_time": "2025-01-16T02:45:49Z", - "updated_time": "2025-01-16T02:45:49Z", - "name": "Connect API - Admin Role", - "description": "", - "template": "connect", - "auth_type": "Visitor API Key", - "config": { - "max_role": "Admin" - } - }, - { - "id": "45", - "guid": "9c2e4bde", - "created_time": "2025-01-28T21:27:57Z", - "updated_time": "2025-01-28T21:27:57Z", - "name": "Sharepoint", - "description": "testing with Toni's creds", - "template": "sharepoint", - "auth_type": "Viewer", - "config": { - "auth_mode": "Public", - "auth_type": "Viewer", - "client_id": "f8bbe4ff", - "scopes": "00000003/.default offline_access", - "tenant_id": "eacb8f40" - } - }, - { - "id": "46", - "guid": "56928e28", - "created_time": "2025-02-11T12:40:28Z", - "updated_time": "2025-02-11T12:40:28Z", - "name": "Chaita-Salesforce", - "description": "should work", - "template": "salesforce", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "client_id": "3MVG91oqviqJKoEF_.qUhZ.ZDv34yklQvJ20VqZ_ai_6O3pqPAFN2pr7Nk6PVpfGHRWhdRCxCOX3Zho.ciJ0D", - "scopes": "refresh_token offline_access api web", - "sf_host_domain": "https://positpbc-dev-ed.develop.my.salesforce.com" - } - }, - { - "id": "48", - "guid": "2e0a6448", - "created_time": "2025-03-17T16:06:17Z", - "updated_time": "2025-03-27T15:20:44Z", - "name": "AWS-Azure-Chaita", - "description": "Should work with localhost:3939", - "template": "aws", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "authorization_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/authorize", - "client_id": "e5652005", - "role_arn": "arn:aws:iam::440849847947:role/PositConnect-OIDC-Role", - "scopes": "offline_access openid profile email", - "session_duration": "3600", - "sts_region": "us-east-1", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/token", - "use_pkce": true - } - }, - { - "id": "49", - "guid": "82031f9f", - "created_time": "2025-03-17T16:09:00Z", - "updated_time": "2025-03-17T16:09:00Z", - "name": "AWS-Okta-Chaita", - "description": "", - "template": "aws", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "authorization_uri": "https://dev-45652974.okta.com/oauth2/v1/authorize", - "client_id": "0oantnx4gsTh7zmG35d7", - "role_arn": "arn:aws:iam::440849847947:role/OktaAccessRole", - "scopes": "offline_access openid profile email", - "session_duration": "3600", - "sts_region": "us-east-1", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://dev-45652974.okta.com/oauth2/v1/token", - "use_pkce": true - } - }, - { - "id": "50", - "guid": "cfaef603", - "created_time": "2025-03-24T19:56:34Z", - "updated_time": "2025-03-24T19:56:34Z", - "name": "AWS-Azure-S3", - "description": "", - "template": "aws", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "authorization_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/authorize", - "client_id": "a3ca211e", - "role_arn": "arn:aws:iam::440849847947:role/mattconflitti-s3-FullAccess", - "scopes": "offline_access openid profile email", - "session_duration": "3600", - "sts_region": "us-east-1", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/token", - "use_pkce": true - } - }, - { - "id": "52", - "guid": "e57705ac", - "created_time": "2025-03-25T19:05:55Z", - "updated_time": "2025-03-25T19:06:33Z", - "name": "Zack test", - "description": "123 abc", - "template": "github", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "client_id": "arstarstarstarstarst" - } - }, - { - "id": "53", - "guid": "3c6d320e", - "created_time": "2025-03-25T19:36:03Z", - "updated_time": "2025-03-25T19:36:03Z", - "name": "AWS Bedrock Full Access", - "description": "", - "template": "aws", - "auth_type": "Service Account", - "config": { - "auth_type": "Service Account", - "role_arn": "arn:aws:iam::440849847947:role/mattconflitti-bedrock-FullAccess", - "session_duration": "3600", - "sts_region": "us-east-1" - } - }, - { - "id": "54", - "guid": "0ce09de1", - "created_time": "2025-03-25T19:54:01Z", - "updated_time": "2025-03-25T19:54:01Z", - "name": "AWS S3 Full Access", - "description": "", - "template": "aws", - "auth_type": "Service Account", - "config": { - "auth_type": "Service Account", - "role_arn": "arn:aws:iam::440849847947:role/mattconflitti-s3-FullAccess", - "session_duration": "3600", - "sts_region": "us-east-1" - } - }, - { - "id": "55", - "guid": "d84381b3", - "created_time": "2025-03-27T03:00:30Z", - "updated_time": "2025-03-27T03:00:30Z", - "name": "AWS Bedrock + S3", - "description": "AWS Bedrock plus S3 Full Access", - "template": "aws", - "auth_type": "Service Account", - "config": { - "auth_type": "Service Account", - "role_arn": "arn:aws:iam::440849847947:role/mattc-bedrock-s3", - "session_duration": "3600", - "sts_region": "us-east-1" - } - }, - { - "id": "57", - "guid": "7513cdea", - "created_time": "2025-05-15T19:12:11Z", - "updated_time": "2025-05-15T19:12:11Z", - "name": "DCM - test visitor key", - "description": "", - "template": "connect", - "auth_type": "Visitor API Key", - "config": { - "max_role": "Viewer" - } - }, - { - "id": "58", - "guid": "e14305d5", - "created_time": "2025-05-27T18:22:40Z", - "updated_time": "2025-05-27T18:22:40Z", - "name": "Connect Visitor API Key", - "description": "Provide a short-lived Connect API key to visitors. This is useful for apps that interact with Connect programmatically.", - "template": "connect", - "auth_type": "Visitor API Key", - "config": { - "max_role": "Admin" - } - }, - { - "id": "59", - "guid": "4d49f399", - "created_time": "2025-06-10T17:13:49Z", - "updated_time": "2025-06-17T20:23:15Z", - "name": "Azure OpenAI - Viewer", - "description": "Integration with Azure OpenAI services using OAuth authentication", - "template": "custom", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "authorization_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/authorize", - "client_id": "52818ef8", - "scopes": "https://cognitiveservices.azure.com/.default offline_access", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/token", - "use_pkce": true - } - }, - { - "id": "60", - "guid": "c0dad604", - "created_time": "2025-06-17T20:12:31Z", - "updated_time": "2025-06-17T20:23:28Z", - "name": "Azure OpenAI - service account", - "description": "Integration with Azure OpenAI services using service account authentication", - "template": "custom", - "auth_type": "Service Account", - "config": { - "auth_type": "Service Account", - "authorization_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/authorize", - "client_id": "1381d669", - "scopes": "https://cognitiveservices.azure.com/.default offline_access", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://login.microsoftonline.com/b0b52785/oauth2/v2.0/token" - } - }, - { - "id": "61", - "guid": "1fcd4f88", - "created_time": "2025-06-24T17:15:08Z", - "updated_time": "2025-06-24T17:15:58Z", - "name": "Azure OpenAI ", - "description": "", - "template": "azure-openai", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "client_id": "faf78b60", - "scopes": "https://cognitiveservices.azure.com/.default offline_access", - "tenant_id": "b0b52785" - } - }, - { - "id": "62", - "guid": "f6716d3f", - "created_time": "2025-07-07T20:48:06Z", - "updated_time": "2025-07-07T22:07:34Z", - "name": "Vertex AI", - "description": "", - "template": "custom", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "auth_type": "Viewer", - "authorization_uri": "https://accounts.google.com/o/oauth2/v2/auth", - "client_id": "196021606923-kk10h9movm1vf3b55jho3cd2tvbhrpl5.apps.googleusercontent.com", - "scopes": "https://www.googleapis.com/auth/cloud-platform", - "token_endpoint_auth_method": "client_secret_post", - "token_uri": "https://oauth2.googleapis.com/token", - "use_pkce": true - } - }, - { - "id": "64", - "guid": "5a57f4cf", - "created_time": "2025-07-22T17:50:21Z", - "updated_time": "2025-07-22T17:50:21Z", - "name": "Vertex AI", - "description": "", - "template": "vertex-ai", - "auth_type": "Viewer", - "config": { - "auth_mode": "Confidential", - "client_id": "196021606923-0daalcru5ah7k51e49f1i1oneeajuft6.apps.googleusercontent.com", - "scopes": "https://www.googleapis.com/auth/cloud-platform" - } - } -] From da188bf824b99b45f072b14971861b94f279d425 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Mon, 28 Jul 2025 14:28:42 -0400 Subject: [PATCH 5/6] Update methods and documentation --- R/integrations.R | 34 ++++++++++++------- ...as.data.frame.connect_list_integrations.Rd | 3 +- man/as_tibble.connect_list_integrations.Rd | 7 ++-- man/get_integrations.Rd | 14 ++++++++ 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/R/integrations.R b/R/integrations.R index 635df5327..69f0a2be2 100644 --- a/R/integrations.R +++ b/R/integrations.R @@ -38,6 +38,20 @@ #' # Fetch all OAuth integrations #' integrations <- get_integrations(client) #' +#' +#' # Update the configuration and metadata for a subset of integrations. +#' json_payload <- toJSON(list( +#' description = "New Description", +#' config = list( +#' client_secret = "new-client-secret" +#' ) +#' ), auto_unbox = TRUE) +#' +#' results <- integrations |> +#' purrr::keep(\(x) x$template == "service_to_update") |> +#' purrr::map(\(x) client$PATCH(paste0("v1/oauth/integrations/", x$guid), body = json_payload)) +#' +#' #' # Convert to tibble or data frame #' integrations_df <- tibble::as_tibble(integrations) #' } @@ -53,8 +67,7 @@ get_integrations <- function(client) { #' Convert integrations data to a data frame #' #' @description -#' Converts an object returned by [get_integrations()] into a data frame with parsed -#' column types. +#' Converts an list returned by [get_integrations()] into a data frame. #' #' @param x A `connect_list_integrations` object (from [get_integrations()]). #' @param row.names Passed to [base::as.data.frame()]. @@ -63,16 +76,15 @@ get_integrations <- function(client) { #' #' @return A `data.frame` with one row per integration. #' @export -#' @method as.data.frame connect_list_integrations as.data.frame.connect_list_integrations <- function( x, - row.names = NULL, + row.names = NULL, # nolint optional = FALSE, ... ) { - integrations_df <- parse_connectapi_typed(x, connectapi_ptypes$integrations) + integrations_tbl <- as_tibble(x) as.data.frame( - integrations_df, + integrations_tbl, row.names = row.names, optional = optional, ... @@ -82,16 +94,12 @@ as.data.frame.connect_list_integrations <- function( #' Convert integrations data to a tibble #' #' @description -#' Converts an object returned by [get_integrations()] to a tibble via -#' [as.data.frame.connect_list_integrations()]. +#' Converts a list returned by [get_integrations()] to a tibble. #' #' @param x A `connect_list_integrations` object. -#' @param ... Passed to [as.data.frame()]. #' #' @return A tibble with one row per integration. #' @export -#' @importFrom tibble as_tibble -#' @method as_tibble connect_list_integrations -as_tibble.connect_list_integrations <- function(x, ...) { - tibble::as_tibble(as.data.frame(x, ...)) +as_tibble.connect_list_integrations <- function(x) { + parse_connectapi_typed(x, connectapi_ptypes$integrations) } diff --git a/man/as.data.frame.connect_list_integrations.Rd b/man/as.data.frame.connect_list_integrations.Rd index 1b255c3dd..8f54ff5b7 100644 --- a/man/as.data.frame.connect_list_integrations.Rd +++ b/man/as.data.frame.connect_list_integrations.Rd @@ -19,6 +19,5 @@ A \code{data.frame} with one row per integration. } \description{ -Converts an object returned by \code{\link[=get_integrations]{get_integrations()}} into a data frame with parsed -column types. +Converts an list returned by \code{\link[=get_integrations]{get_integrations()}} into a data frame. } diff --git a/man/as_tibble.connect_list_integrations.Rd b/man/as_tibble.connect_list_integrations.Rd index 51e6840ec..5aa2db355 100644 --- a/man/as_tibble.connect_list_integrations.Rd +++ b/man/as_tibble.connect_list_integrations.Rd @@ -4,17 +4,14 @@ \alias{as_tibble.connect_list_integrations} \title{Convert integrations data to a tibble} \usage{ -\method{as_tibble}{connect_list_integrations}(x, ...) +\method{as_tibble}{connect_list_integrations}(x) } \arguments{ \item{x}{A \code{connect_list_integrations} object.} - -\item{...}{Passed to \code{\link[=as.data.frame]{as.data.frame()}}.} } \value{ A tibble with one row per integration. } \description{ -Converts an object returned by \code{\link[=get_integrations]{get_integrations()}} to a tibble via -\code{\link[=as.data.frame.connect_list_integrations]{as.data.frame.connect_list_integrations()}}. +Converts a list returned by \code{\link[=get_integrations]{get_integrations()}} to a tibble. } diff --git a/man/get_integrations.Rd b/man/get_integrations.Rd index bbe7c8515..26dad24c6 100644 --- a/man/get_integrations.Rd +++ b/man/get_integrations.Rd @@ -46,6 +46,20 @@ client <- connect() # Fetch all OAuth integrations integrations <- get_integrations(client) + +# Update the configuration and metadata for a subset of integrations. +json_payload <- toJSON(list( + description = "New Description", + config = list( + client_secret = "new-client-secret" + ) +), auto_unbox = TRUE) + +results <- integrations |> + purrr::keep(\(x) x$template == "service_to_update") |> + purrr::map(\(x) client$PATCH(paste0("v1/oauth/integrations/", x$guid), body = json_payload)) + + # Convert to tibble or data frame integrations_df <- tibble::as_tibble(integrations) } From b94f7d1e5082eb0877b448b21200003221b55e68 Mon Sep 17 00:00:00 2001 From: Toph Allen Date: Mon, 28 Jul 2025 16:48:36 -0400 Subject: [PATCH 6/6] Attempt to fix CI --- R/integrations.R | 3 ++- man/as_tibble.connect_list_integrations.Rd | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/R/integrations.R b/R/integrations.R index 69f0a2be2..b39122a01 100644 --- a/R/integrations.R +++ b/R/integrations.R @@ -97,9 +97,10 @@ as.data.frame.connect_list_integrations <- function( #' Converts a list returned by [get_integrations()] to a tibble. #' #' @param x A `connect_list_integrations` object. +#' @param ... Unused. #' #' @return A tibble with one row per integration. #' @export -as_tibble.connect_list_integrations <- function(x) { +as_tibble.connect_list_integrations <- function(x, ...) { parse_connectapi_typed(x, connectapi_ptypes$integrations) } diff --git a/man/as_tibble.connect_list_integrations.Rd b/man/as_tibble.connect_list_integrations.Rd index 5aa2db355..a060b4972 100644 --- a/man/as_tibble.connect_list_integrations.Rd +++ b/man/as_tibble.connect_list_integrations.Rd @@ -4,10 +4,12 @@ \alias{as_tibble.connect_list_integrations} \title{Convert integrations data to a tibble} \usage{ -\method{as_tibble}{connect_list_integrations}(x) +\method{as_tibble}{connect_list_integrations}(x, ...) } \arguments{ \item{x}{A \code{connect_list_integrations} object.} + +\item{...}{Unused.} } \value{ A tibble with one row per integration.