From e4df3fa73b18bdab23e7352c393d9fc57e930565 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Thu, 21 Mar 2024 14:01:46 -0500 Subject: [PATCH 1/4] Update key when needed. --- R/cookies.R | 13 +++++++++++++ R/user.R | 20 +++++++++++++++----- R/{ui_wrapper.R => wrapper.R} | 0 app.R | 6 ++---- 4 files changed, 30 insertions(+), 9 deletions(-) rename R/{ui_wrapper.R => wrapper.R} (100%) diff --git a/R/cookies.R b/R/cookies.R index 71827e4..f1ac4b5 100644 --- a/R/cookies.R +++ b/R/cookies.R @@ -67,3 +67,16 @@ check_login <- function(team_id, ) return(.shinyslack_decrypt(cookie_token, shinyslack_key)) } + +.update_shinyslack_api_key <- function(slack_api_key, + team_id, + session, + shinyslack_key) { + if (is.null(slack_api_key)) { + slack_token <- .get_slack_cookie_token(team_id, shinyslack_key, session) + if (.validate_slack_token(slack_token, team_id)) { + session$userData$shinyslack_api_key <- slack_token + } + } + return(session$userData$shinyslack_api_key) +} diff --git a/R/user.R b/R/user.R index 91f0b67..e7b52ea 100644 --- a/R/user.R +++ b/R/user.R @@ -20,16 +20,26 @@ user_info <- function(components = c("user_id", "user_name"), ..., session = shiny::getDefaultReactiveDomain(), - slack_api_key = session$userData$shinyslack_api_key) { + slack_api_key = session$userData$shinyslack_api_key, + shinyslack_key = Sys.getenv("SHINYSLACK_KEY")) { components <- match.arg(components, several.ok = TRUE) rlang::check_dots_empty() return( shiny::reactive({ - basics <- slackcalls::post_slack("auth.test", token = slack_api_key) - if (all(components %in% c("user_id", "user_name"))) { - return(c(user_id = basics$user_id, user_name = basics$user)[components]) + slack_api_key <- .update_shinyslack_api_key( + slack_api_key, + team_id, + session, + shinyslack_key + ) + if (!is.null(slack_api_key)) { + basics <- slackcalls::post_slack("auth.test", token = slack_api_key) + if (all(components %in% c("user_id", "user_name"))) { + return(c(user_id = basics$user_id, user_name = basics$user)[components]) + } + return(.get_more_user_info(basics$user_id, slack_api_key, components)) } - return(.get_more_user_info(basics$user_id, slack_api_key, components)) + return(NULL) }) ) } diff --git a/R/ui_wrapper.R b/R/wrapper.R similarity index 100% rename from R/ui_wrapper.R rename to R/wrapper.R diff --git a/app.R b/app.R index afd4c21..847915b 100644 --- a/app.R +++ b/app.R @@ -12,11 +12,9 @@ Sys.unsetenv("SLACK_API_TOKEN") ui <- shiny::fluidPage(shiny::textOutput("user_name")) server <- function(input, output, session) { - username <- user_info(components = "display_name") - + user_name <- user_info(components = "display_name") output$user_name <- shiny::renderText({ - shiny::req(check_login(team_id)()) - username() + user_name() }) } From 839e5fa085bf2ab491ccc7d7996687e65805bc73 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Thu, 21 Mar 2024 14:02:17 -0500 Subject: [PATCH 2/4] Bump vnum. --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index e19ee27..4adb780 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: shinyslack Title: Integrate Slack and Shiny -Version: 0.0.0.9009 +Version: 0.0.0.9010 Authors@R: person("Jon", "Harmon", , "jonthegeek@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0003-4781-4346")) From 06e082a883cfedca542b3ed5129cc800417393df Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Thu, 21 Mar 2024 14:20:50 -0500 Subject: [PATCH 3/4] Pass team_id through to server. --- .Rbuildignore | 1 + R/server.R | 9 +++++++++ R/user.R | 3 +-- R/wrapper.R | 2 +- app.R | 4 ++-- man/dot-parse_app_args.Rd | 2 +- man/dot-shinyslack_decrypt.Rd | 5 +++-- man/shinyslack_app.Rd | 2 +- man/slack_shiny_ui.Rd | 2 +- man/user_info.Rd | 14 ++++++++++++-- 10 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 R/server.R diff --git a/.Rbuildignore b/.Rbuildignore index ad3023f..91613be 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -4,6 +4,7 @@ ^LICENSE\.md$ ^CODE_OF_CONDUCT\.md$ ^app\.R +^apptest\.R ^bad_token\.R ^\.secret ^data-raw$ diff --git a/R/server.R b/R/server.R new file mode 100644 index 0000000..15d1027 --- /dev/null +++ b/R/server.R @@ -0,0 +1,9 @@ +.shinyslack_server <- function(server, team_id) { + force(team_id) + return( + function(input, output, session) { + session$userData$shinyslack_team_id = team_id + server(input, output, session) + } + ) +} diff --git a/R/user.R b/R/user.R index e7b52ea..806b99a 100644 --- a/R/user.R +++ b/R/user.R @@ -18,12 +18,11 @@ user_info <- function(components = c("user_id", "display_name", "pronouns", "user_name"), - ..., session = shiny::getDefaultReactiveDomain(), slack_api_key = session$userData$shinyslack_api_key, + team_id = session$userData$shinyslack_team_id, shinyslack_key = Sys.getenv("SHINYSLACK_KEY")) { components <- match.arg(components, several.ok = TRUE) - rlang::check_dots_empty() return( shiny::reactive({ slack_api_key <- .update_shinyslack_api_key( diff --git a/R/wrapper.R b/R/wrapper.R index 10163bf..aa30181 100644 --- a/R/wrapper.R +++ b/R/wrapper.R @@ -21,7 +21,7 @@ shinyslack_app <- function(ui, rlang::exec( shiny::shinyApp, ui = slack_shiny_ui(ui, team_id, expiration, shinyslack_key), - server = server, + server = .shinyslack_server(server, team_id), !!!dots ) ) diff --git a/app.R b/app.R index 847915b..d08731c 100644 --- a/app.R +++ b/app.R @@ -6,7 +6,7 @@ pkgload::load_all( quiet = TRUE ) -team_id <- "T6UC1DKJQ" +team_id_errorproof <- "T6UC1DKJQ" Sys.unsetenv("SLACK_API_TOKEN") ui <- shiny::fluidPage(shiny::textOutput("user_name")) @@ -21,5 +21,5 @@ server <- function(input, output, session) { shinyslack_app( ui = ui, server = server, - team_id = team_id + team_id = team_id_errorproof ) diff --git a/man/dot-parse_app_args.Rd b/man/dot-parse_app_args.Rd index 8e080d9..8b07b03 100644 --- a/man/dot-parse_app_args.Rd +++ b/man/dot-parse_app_args.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ui_wrapper.R +% Please edit documentation in R/wrapper.R \name{.parse_app_args} \alias{.parse_app_args} \title{Figure Out Args for shinyslack_app} diff --git a/man/dot-shinyslack_decrypt.Rd b/man/dot-shinyslack_decrypt.Rd index 410e20a..bb9eced 100644 --- a/man/dot-shinyslack_decrypt.Rd +++ b/man/dot-shinyslack_decrypt.Rd @@ -11,8 +11,9 @@ set, the string is returned unencrypted.} } \value{ -If \code{shinyslack_key} non-empty, the decrypted string (or "bad_string" -if decryption fails). Otherwise the original string is returned. +If \code{shinyslack_key} is non-empty, the decrypted string (or +"bad_string" if decryption fails). Otherwise the original string is +returned. } \description{ Decrypt a String If Possible diff --git a/man/shinyslack_app.Rd b/man/shinyslack_app.Rd index e0c2de2..3e686de 100644 --- a/man/shinyslack_app.Rd +++ b/man/shinyslack_app.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ui_wrapper.R +% Please edit documentation in R/wrapper.R \name{shinyslack_app} \alias{shinyslack_app} \title{Launch a Shiny App with a Slack Login} diff --git a/man/slack_shiny_ui.Rd b/man/slack_shiny_ui.Rd index 3315806..d153491 100644 --- a/man/slack_shiny_ui.Rd +++ b/man/slack_shiny_ui.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/ui_wrapper.R +% Please edit documentation in R/wrapper.R \name{slack_shiny_ui} \alias{slack_shiny_ui} \title{Require Slack login to a Shiny app} diff --git a/man/user_info.Rd b/man/user_info.Rd index 6118f63..5e3df33 100644 --- a/man/user_info.Rd +++ b/man/user_info.Rd @@ -5,13 +5,20 @@ \title{Fetch Slack User Info} \usage{ user_info( - components = c("user_id", "real_name", "display_name", "pronouns", "user_name"), + team_id, ..., + components = c("user_id", "real_name", "display_name", "pronouns", "user_name"), session = shiny::getDefaultReactiveDomain(), - slack_api_key = session$userData$shinyslack_api_key + slack_api_key = session$userData$shinyslack_api_key, + shinyslack_key = Sys.getenv("SHINYSLACK_KEY") ) } \arguments{ +\item{team_id}{The Slack team ID through which the user is being +authenticated.} + +\item{...}{These dots are for future extensions and must be empty.} + \item{components}{A character vector of user components to include. Current options are: \itemize{ @@ -28,6 +35,9 @@ tests.} \item{slack_api_key}{The Slack API key to use. The default value should likely always be used outside of tests.} + +\item{shinyslack_key}{(optional) A key to use to encrypt the string. If not +set, the string is returned unencrypted.} } \value{ A \code{\link[shiny:reactive]{shiny::reactive()}} with a named character vector. From b002602eac4d489c31fb40fd0a935a58ad086d71 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Fri, 22 Mar 2024 09:13:34 -0500 Subject: [PATCH 4/4] Use package environment Specifically for team_id. --- NAMESPACE | 2 +- NEWS.md | 4 +++- R/cookies.R | 2 +- R/encrypt.R | 10 ++++++++-- R/server.R | 9 --------- R/shinyslack-package.R | 9 +++++++++ R/uis.R | 2 +- R/user.R | 2 +- R/wrapper.R | 30 ++++++++++++++++++++++++++++-- man/shinyslack-package.Rd | 23 +++++++++++++++++++++++ man/shinyslack_team_id.Rd | 29 +++++++++++++++++++++++++++++ man/slack_shiny_ui.Rd | 1 + man/user_info.Rd | 11 ++++------- 13 files changed, 109 insertions(+), 25 deletions(-) delete mode 100644 R/server.R create mode 100644 R/shinyslack-package.R create mode 100644 man/shinyslack-package.Rd create mode 100644 man/shinyslack_team_id.Rd diff --git a/NAMESPACE b/NAMESPACE index 4b2c436..0c7e9ba 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,7 +1,7 @@ # Generated by roxygen2: do not edit by hand export(check_login) +export(get_shinyslack_team_id) export(shinyslack_app) -export(slack_shiny_ui) export(user_info) importFrom(rlang,"%||%") diff --git a/NEWS.md b/NEWS.md index f8c1ee4..e35eb75 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ -# shinyslack 0.0.0.9006 +# shinyslack 0.0.0.9010 +* New exported function get_shinyslack_team_id() provides the team id for the Slack workspace used for login. +* Breaking: Removed slack_shiny_ui() from exports. * Breaking: The site url is now automatically determined, so site_url is no longer an argument to any function. * Breaking: The "real" ui is no longer automatically wrapped in a cookie handler. Many UIs that use shinyslack won't need to deal with cookies in the normal app, so we shouldn't add extra, unnecessary javascript. * Abstracted UI switcher into [{scenes}](https://github.com/shinyworks/scenes) package. diff --git a/R/cookies.R b/R/cookies.R index f1ac4b5..b1a91f9 100644 --- a/R/cookies.R +++ b/R/cookies.R @@ -44,7 +44,7 @@ #' @return A [shiny::reactive()] which returns a logical indicating whether the #' user is logged in with proper API access. #' @export -check_login <- function(team_id, +check_login <- function(team_id = get_shinyslack_team_id(), session = shiny::getDefaultReactiveDomain(), shinyslack_key = Sys.getenv("SHINYSLACK_KEY")) { return( diff --git a/R/encrypt.R b/R/encrypt.R index 69157b5..ed6c112 100644 --- a/R/encrypt.R +++ b/R/encrypt.R @@ -9,10 +9,16 @@ .shinyslack_encrypt <- function(string, shinyslack_key = Sys.getenv("SHINYSLACK_KEY")) { if (isTRUE(as.logical(nchar(shinyslack_key)))) { - cli::cli_inform(c("shinyslack_key set.", v = "Encrypting string.")) + cli::cli_inform(c( + "shinyslack: shinyslack_key set.", + v = "Encrypting string." + )) string <- .sodium_encrypt(string, shinyslack_key) } else { - cli::cli_warn(c("shinyslack_key not found.", x = "String not encoded.")) + cli::cli_warn(c( + "shinyslack: shinyslack_key not found.", + x = "String not encoded." + )) } return(string) diff --git a/R/server.R b/R/server.R deleted file mode 100644 index 15d1027..0000000 --- a/R/server.R +++ /dev/null @@ -1,9 +0,0 @@ -.shinyslack_server <- function(server, team_id) { - force(team_id) - return( - function(input, output, session) { - session$userData$shinyslack_team_id = team_id - server(input, output, session) - } - ) -} diff --git a/R/shinyslack-package.R b/R/shinyslack-package.R new file mode 100644 index 0000000..edf5032 --- /dev/null +++ b/R/shinyslack-package.R @@ -0,0 +1,9 @@ +#' @keywords internal +"_PACKAGE" + +## usethis namespace: start +## usethis namespace: end +NULL + +the <- rlang::new_environment() +the$team_id <- character() diff --git a/R/uis.R b/R/uis.R index bc6de59..52b0840 100644 --- a/R/uis.R +++ b/R/uis.R @@ -122,7 +122,7 @@ if (is.null(url)) { cli::cli_abort( - message = c(x = "Could not determine url.") + message = c(x = "shinyslack: Could not determine url.") ) } diff --git a/R/user.R b/R/user.R index 806b99a..e6dc6c4 100644 --- a/R/user.R +++ b/R/user.R @@ -20,7 +20,7 @@ user_info <- function(components = c("user_id", "user_name"), session = shiny::getDefaultReactiveDomain(), slack_api_key = session$userData$shinyslack_api_key, - team_id = session$userData$shinyslack_team_id, + team_id = get_shinyslack_team_id(), shinyslack_key = Sys.getenv("SHINYSLACK_KEY")) { components <- match.arg(components, several.ok = TRUE) return( diff --git a/R/wrapper.R b/R/wrapper.R index aa30181..eacf641 100644 --- a/R/wrapper.R +++ b/R/wrapper.R @@ -16,12 +16,13 @@ shinyslack_app <- function(ui, shinyslack_key = Sys.getenv("SHINYSLACK_KEY")) { dots <- rlang::list2(...) dots$options <- .parse_app_args(dots$options) + set_shinyslack_team_id(team_id) return( rlang::exec( shiny::shinyApp, ui = slack_shiny_ui(ui, team_id, expiration, shinyslack_key), - server = .shinyslack_server(server, team_id), + server = server, !!!dots ) ) @@ -56,7 +57,7 @@ shinyslack_app <- function(ui, #' #' @return A function defining the UI of a Shiny app (either with login or #' without). -#' @export +#' @keywords internal slack_shiny_ui <- function(ui, team_id, expiration = 90, @@ -90,3 +91,28 @@ slack_shiny_ui <- function(ui, ) ) } + +#' Get the current team_id +#' +#' The `team_id` is set when an app is launched. In almost all cases, that value +#' is the one you will want for any instances of `team_id`. +#' +#' @return A string representing the team_id. +#' @export +#' +#' @examples +#' # If no app is active, the team_id will be a zero-length character vector. +#' get_shinyslack_team_id() +#' +#' set_shinyslack_team_id("T123456") +#' get_shinyslack_team_id() +get_shinyslack_team_id <- function() { + return(the$team_id) +} + +#' @inheritParams .shared-parameters +#' @rdname get_shinyslack_team_id +set_shinyslack_team_id <- function(team_id) { + the$team_id <- team_id + return(the$team_id) +} diff --git a/man/shinyslack-package.Rd b/man/shinyslack-package.Rd new file mode 100644 index 0000000..8815c96 --- /dev/null +++ b/man/shinyslack-package.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/shinyslack-package.R +\docType{package} +\name{shinyslack-package} +\alias{shinyslack} +\alias{shinyslack-package} +\title{shinyslack: Integrate Slack and Shiny} +\description{ +Login to Shiny apps using Slack, and use Slack information in those apps. +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://github.com/r4ds/shinyslack} + \item Report bugs at \url{https://github.com/r4ds/shinyslack/issues} +} + +} +\author{ +\strong{Maintainer}: Jon Harmon \email{jonthegeek@gmail.com} (\href{https://orcid.org/0000-0003-4781-4346}{ORCID}) + +} +\keyword{internal} diff --git a/man/shinyslack_team_id.Rd b/man/shinyslack_team_id.Rd new file mode 100644 index 0000000..20c5751 --- /dev/null +++ b/man/shinyslack_team_id.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/wrapper.R +\name{get_shinyslack_team_id} +\alias{get_shinyslack_team_id} +\alias{set_shinyslack_team_id} +\title{Get the current team_id} +\usage{ +get_shinyslack_team_id() + +set_shinyslack_team_id(team_id) +} +\arguments{ +\item{team_id}{The Slack team ID through which the user is being +authenticated.} +} +\value{ +A string representing the team_id. +} +\description{ +The \code{team_id} is set when an app is launched. In almost all cases, that value +is the one you will want for any instances of \code{team_id}. +} +\examples{ +# If no app is active, the team_id will be a zero-length character vector. +get_shinyslack_team_id() + +set_shinyslack_team_id("T123456") +get_shinyslack_team_id() +} diff --git a/man/slack_shiny_ui.Rd b/man/slack_shiny_ui.Rd index d153491..0367242 100644 --- a/man/slack_shiny_ui.Rd +++ b/man/slack_shiny_ui.Rd @@ -33,3 +33,4 @@ This is a function factory that wraps a Shiny ui. If the user does not have a cookie for that site, they are prompted to login. Once they have a cookie, the UI displays as normal. #5 } +\keyword{internal} diff --git a/man/user_info.Rd b/man/user_info.Rd index 5e3df33..3bb8e7b 100644 --- a/man/user_info.Rd +++ b/man/user_info.Rd @@ -5,20 +5,14 @@ \title{Fetch Slack User Info} \usage{ user_info( - team_id, - ..., components = c("user_id", "real_name", "display_name", "pronouns", "user_name"), session = shiny::getDefaultReactiveDomain(), slack_api_key = session$userData$shinyslack_api_key, + team_id = get_shinyslack_team_id(), shinyslack_key = Sys.getenv("SHINYSLACK_KEY") ) } \arguments{ -\item{team_id}{The Slack team ID through which the user is being -authenticated.} - -\item{...}{These dots are for future extensions and must be empty.} - \item{components}{A character vector of user components to include. Current options are: \itemize{ @@ -36,6 +30,9 @@ tests.} \item{slack_api_key}{The Slack API key to use. The default value should likely always be used outside of tests.} +\item{team_id}{The Slack team ID through which the user is being +authenticated.} + \item{shinyslack_key}{(optional) A key to use to encrypt the string. If not set, the string is returned unencrypted.} }