Skip to content

Commit

Permalink
More cleanup (spillover from prev commit)
Browse files Browse the repository at this point in the history
  • Loading branch information
amoeba committed Oct 4, 2018
1 parent cf4a2a0 commit 1189897
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 34 deletions.
60 changes: 60 additions & 0 deletions R/parse_rt_response.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

#' Parse an RT response in its parts as a list
#'
#' The RT API uses overrides default HTTP behavior with their own set of status
#' codes, messages, and response formats. This function parses that custom
#' implementation and presents it into something that's easier to build a
#' package with.
#'
#' For example, a response like:
#'
#' "RT/4.4.3 200 Ok
#'
#' # Ticket 2 created.
#'
#' "
#'
#' is turned into the list:
#'
#' $status
#' [1] 200
#'
#' $message
#' [1] "Ok"
#'
#' $body
#' [1] "# Ticket 2 created."
#'
#' @param response (character) Parsed response from \code{\link[httr]{content}}
#' @param verbose (logical) Optional, defaults to \code{TRUE}. Prints more information during parsing.
#'
#' @return (list) List with named elements status, message, and body
parse_rt_response <- function(response, verbose = FALSE) {
split_response <- stringr::str_split(response, "[\\n]+", n = 2)

# Response should be a single result, with parts for the first line, and rest
stopifnot(length(split_response) == 1 &&
length(split_response[[1]] != 2))

first <- split_response[[1]][1]
rest <- stringr::str_replace_all(split_response[[1]][2], "[\\n]+$", "")

# Parse the first line (RT version + HTTP status + custom status messsage)
match <- stringr::str_match(first, "\\ART/[\\d\\.]+ (\\d+) (.+)\\Z")

# Stop, helpfully, if we failed to get what we expected from the regex
if (!all(dim(match) == c(1, 3))) {
if (verbose) {
message(content)
}

message("Failed to parse RT response. Returning response directly from httr.")
return(response)
}

return(list(
status = as.numeric(match[1,2]),
message = match[1,3],
body = rest
))
}
10 changes: 6 additions & 4 deletions R/rt.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
#'
#' Create an RT API URL based on the server URL and any arguments provided
#'
#' @param base (character) The base URL that hosts RT for your organization
#' @param rt_base_url (character) The base URL that hosts RT for your organization
#' @param ... Other parameters
#'

rt_url <- function(base, ...) {
paste(gsub("\\/$", "", base), # Removes trailing slash from base URL just in case
"REST", "1.0",
rt_url <- function(rt_base_url, ...) {
stopifnot(nchar(rt_base_url) > 0)
paste(gsub("\\/$", "", rt_base_url), # Removes trailing slash from base URL just in case
"REST",
"1.0",
paste(c(...), collapse = "/"),
sep = "/")
}
Expand Down
84 changes: 54 additions & 30 deletions R/rt_login.R
Original file line number Diff line number Diff line change
@@ -1,61 +1,85 @@
#' Log in to RT
#'
#' Use this to log into RT at the start of your session.
#' Use this to log into RT at the start of your session. Once you call this
#' function and successfully log in, calls of other functions within this
#' package will re-use your login information automatically.
#'
#' @param user (character) Your username
#' @param pass (character) Your password
#' @param rt_base (character) The base URL that hosts RT for your organization. Set the base URL in your R session using \code{options(rt_base = "https://server.name/rt/")}
#' The value of \code{rt_base_url} should be the same address you use in your
#' web browser to log into RT (i.e., the address of the log in page).
#'
#' @param user (character) Your username.
#' @param password (character) Your password.
#' @param rt_base_url (character) The address of your RT installation.
#' See details.
#' @export
#'
#' @import stringr
#'
#' @examples
#' \dontrun{
#' options(rt_base = "https://demo.bestpractical.com")
#' rt_login(user = "guest", pass = "guest")
#' }

rt_login <- function(user, pass, rt_base = getOption("rt_base")) {
if(!is.character(rt_base)){
stop('Check your base URL. Set it in your R session using option(rt_base = "https://server.name/rt/")', call. = FALSE)
#' # You can setup the location of your RT installation and the values for
#' # your credentials as environmental variables
#' Sys.setenv("RT_USER" = "user",
#' "RT_PASSWORD" = "password",
#' "RT_BASE_URL" = "https://demo.bestpractical.com")
#'
#' # And then log in directly like
#' rt_login()
#'
#' # Or if you prefer, you can pass the values directly, like
#' rt_login("user", "password", "https://demo.bestpractical.com")
#'}
rt_login <- function(user = Sys.getenv("RT_USER"),
password = Sys.getenv("RT_PASSWORD"),
rt_base_url = Sys.getenv("RT_BASE_URL")) {
if (!is.character(rt_base_url) && nchar(rt_base_url) > 0) {
stop("Check your base URL. ",
"Set it in your R session using ",
"Sys.setenv(RT_BASE_URL = \"https://server.name/rt/\")",
call. = FALSE)
}

base_api <- paste(stringr::str_replace(rt_base, "\\/$", ""), # removes trailing slash from base URL just in case
"REST", "1.0", sep = "/")
req <- httr::POST(base_api, body = list('user' = user, 'pass' = pass))

# Check that login worked
response <- httr::POST(rt_url(rt_base_url),
body = list(
'user' = utils::URLencode(user,
reserved = TRUE),
'pass' = utils::URLencode(password,
reserved = TRUE)))
check_login(response)
}

if(stringr::str_detect(httr::content(req), "Credentials required")){
invisible(FALSE)
stop("Your log-in was unsuccessful. Check your username, password, and base URL and try again.",
call. = FALSE)
} else {
message("Successfully logged in.")
invisible(TRUE)
#' Check that the login request was successful or not
#'
#' @param response (httr::response) RT API login response
#'
#' @return (logical) TRUE if login was succesful, errors out otherwise
check_login <- function(response) {
parsed <- parse_rt_response(httr::content(response))
if (parsed$body != "# Invalid object specification: ''\n\nid: ") {
stop(parsed$message, ": ", parsed$body, call. = FALSE)
}

message("Successfully logged in.")
invisible(TRUE)
}

#' Log in to RT interactively
#'
#' Wrapper for \code{\link{rt_login}} to interactively log into RT at the start of your
#' session. Keeps your log-in information private.
#'
#' @param rt_base (character) The base URL that hosts RT for your organization. Set the base URL in your R session using \code{options(rt_base = "https://server.name/rt/")}
#' @param rt_base_url (character) The base URL that hosts RT for your organization. Set the base URL in your R session using \code{Sys.getenv("RT_BASE_URL" = "https://server.name/rt/")}
#'
#' @importFrom getPass getPass
#'
#' @export
#'
#' @examples
#' \dontrun{
#' options(rt_base = "https://demo.bestpractical.com")
#' Sys.setenv(RT_BASE_URL = "https://demo.bestpractical.com")
#' rt_login_interactive()
#' }

rt_login_interactive <- function(rt_base = getOption("rt_base")) {
rt_login_interactive <- function(rt_base_url = Sys.getenv("RT_BASE")) {
rt_login(user = readline("Enter username: "),
pass = getPass::getPass(),
rt_base = rt_base)
password = getPass::getPass(),
rt_base_url = rt_base_url)
}

0 comments on commit 1189897

Please sign in to comment.