Skip to content

Commit

Permalink
Add a whoami function (user, token, scopes); closes #39 (#51)
Browse files Browse the repository at this point in the history
* Add a whoami function (user, token, scopes)

* documentation

* unrelated typos

* roxygenize

* better pointer to help

* link to gh_whoami() help from gh help

* test gh_whoami()

* Announce mocking status when recording

* Add additional classes to returned error objects (#50)

This allows you to catch a github_error or the exact status code
directly.

* Record tests (sort of, see below) in test-error.R

Note: there's only one new recording because the second test matches against first recording. It just happens to pass in a happy coincidence.

* Improve and record tests of gh_whoami()

* Make GITHUB_TOKEN be non-NULL on travis and appveyor

* Don't run any example re: gh_whoami()

* Use if() {} else {} when forming Authorization header

* Use gh() instead of low-level fxns in gh_whoami()
  • Loading branch information
Jennifer (Jenny) Bryan authored and gaborcsardi committed Dec 10, 2016
1 parent 23308b6 commit 951031e
Show file tree
Hide file tree
Showing 18 changed files with 226 additions and 32 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Expand Up @@ -7,5 +7,9 @@ r:
- devel
- oldrel

env:
- global
- GITHUB_TOKEN=ANYTHING_BUT_NULL

after_success:
- test $TRAVIS_R_VERSION_STRING = "release" && Rscript -e 'covr::codecov()'
1 change: 1 addition & 0 deletions NAMESPACE
Expand Up @@ -7,6 +7,7 @@ export(gh_last)
export(gh_next)
export(gh_prev)
export(gh_tree_remote)
export(gh_whoami)
importFrom(httr,DELETE)
importFrom(httr,GET)
importFrom(httr,PATCH)
Expand Down
8 changes: 5 additions & 3 deletions R/gh_request.R
Expand Up @@ -118,9 +118,11 @@ gh_token <- function() {
}

gh_auth <- function(token) {
auth <- character()
if (token != "") auth <- c("Authorization" = paste("token", token))
auth
if (isTRUE(token != "")) {
c("Authorization" = paste("token", token))
} else {
character()
}
}

gh_send_headers <- function(headers = NULL) {
Expand Down
73 changes: 73 additions & 0 deletions R/gh_whoami.R
@@ -0,0 +1,73 @@
#' Info on current GitHub user and token
#'
#' Reports wallet name, GitHub login, and GitHub URL for the current
#' authenticated user, the first few and last characters of the token, and the
#' associated scopes.
#'
#' Get a personal access token for the GitHub API from
#' \url{https://github.com/settings/tokens} and select the scopes necessary for
#' your planned tasks. The \code{repo} scope, for example, is one many are
#' likely to need. The token itself is a string of 40 letters and digits. You
#' can store it any way you like and provide explicitly via the \code{.token}
#' argument to \code{\link{gh}()}.
#'
#' However, many prefer to define an environment variable \code{GITHUB_PAT} (or
#' \code{GITHUB_TOKEN}) with this value in their \code{.Renviron} file. Add a
#' line that looks like this, substituting your PAT:
#'
#' \preformatted{
#' GITHUB_PAT=8c70fd8419398999c9ac5bacf3192882193cadf2
#' }
#'
#' Put a line break at the end! If you’re using an editor that shows line
#' numbers, there should be (at least) two lines, where the second one is empty.
#' Restart R for this to take effect. Call \code{gh_whoami()} to confirm
#' success.
#'
#' To get complete information on the authenticated user, call
#' \code{gh("/user")}.
#'
#' For token management via API (versus the browser), use the
#' \href{https://developer.github.com/v3/oauth_authorizations/}{OAuth
#' Authorizations API}. This API requires Basic Authentication using your
#' username and password, not tokens, and is outside the scope of the \code{gh}
#' package.
#'
#' @inheritParams gh
#'
#' @return A \code{gh_response} object, which is also a \code{list}.
#' @export
#'
#' @examples
#' \dontrun{
#' gh_whoami()
#'
#' ## explicit token + use with GitHub Enterprise
#' gh_whoami(.token = "8c70fd8419398999c9ac5bacf3192882193cadf2",
#' .api_url = "https://github.foobar.edu/api/v3")
#' }
gh_whoami <- function(.token = NULL, .api_url = NULL, .send_headers = NULL) {
.token <- .token %||% gh_token()
if (isTRUE(.token == "")) {
message("No personal access token (PAT) available.\n",
"Obtain a PAT from here:\n",
"https://github.com/settings/tokens\n",
"For more on what to do with the PAT, see ?gh_whoami.")
return(invisible(NULL))
}
res <- gh(endpoint = "/user", .token = .token,
.api_url = .api_url, .send_headers = .send_headers)
scopes <- attr(res, "response")[["x-oauth-scopes"]]
res <- res[c("name", "login", "html_url")]
res$scopes <- scopes
res$token <- hide_middle(.token)
## 'gh_response' class has to be restored
class(res) <- c("gh_response", "list")
res
}

hide_middle <- function(x, n = 4) {
paste0(substr(x, start = 1, stop = n),
"...",
substr(x, start = nchar(x) - n + 1, stop = nchar(x)))
}
4 changes: 3 additions & 1 deletion R/package.R
Expand Up @@ -34,7 +34,7 @@ NULL
#' If the method is not supplied, will use \code{.method}, which defaults
#' to \code{GET}.
#' @param ... Name-value pairs giving API parameters. Will be matched
#' into \code{url} placeholders, send as query parameters in \code{GET}
#' into \code{url} placeholders, sent as query parameters in \code{GET}
#' requests, and in the JSON body of \code{POST} requests.
#' @param .token Authentication token.
#' @param .api_url Github API url (default: \url{https://api.github.com}).
Expand Down Expand Up @@ -67,6 +67,8 @@ NULL
#' @importFrom jsonlite fromJSON toJSON
#' @importFrom utils URLencode
#' @export
#' @seealso \code{\link{gh_whoami}()} for details on GitHub API token
#' management.
#' @examples
#' \dontrun{
#' ## Repositories of a user, these are equivalent
Expand Down
2 changes: 1 addition & 1 deletion R/pagination.R
Expand Up @@ -57,7 +57,7 @@ gh_link <- function(gh_response, link) {
#' @details
#' Note that these are not always defined. E.g. if the first
#' page was queried (the default), then there are no first and previous
#' pages defined. If there is no next page, then there is nog
#' pages defined. If there is no next page, then there is no
#' next page defined, etc.
#'
#' If the requested page does not exist, an error is thrown.
Expand Down
4 changes: 4 additions & 0 deletions appveyor.yml
Expand Up @@ -12,6 +12,10 @@ install:

# Adapt as necessary starting from here

environment:
global:
GITHUB_TOKEN: ANYTHING_BUT_NULL

build_script:
- travis-tool.sh install_deps

Expand Down
42 changes: 23 additions & 19 deletions man/gh.Rd

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

10 changes: 6 additions & 4 deletions man/gh_next.Rd

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

3 changes: 1 addition & 2 deletions man/gh_tree_remote.Rd

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

71 changes: 71 additions & 0 deletions man/gh_whoami.Rd

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

1 change: 0 additions & 1 deletion man/print.gh_response.Rd

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

4 changes: 3 additions & 1 deletion tests/testthat/helper.R
Expand Up @@ -12,7 +12,9 @@ if (file.exists("github-token.txt")) {
Sys.setenv(DEBUGME = "httrmock")
httrmock::start_recording()
httrmock::start_replaying()

ms <- httrmock::mocking_status()
message("mocking status:\n",
paste(names(ms), ms, sep = ": ", collapse = "\n"))
} else {
httrmock::stop_recording()
httrmock::start_replaying()
Expand Down
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
b9a62b3016e67bbab29cda2f3ec3457b
@@ -0,0 +1 @@
99a06080989bb5e278f39cf8c9035a26
29 changes: 29 additions & 0 deletions tests/testthat/test-whoami.R
@@ -0,0 +1,29 @@
context("whoami")

test_that("whoami works in presence of PAT", {
## being explicit re token because GITHUB_PAT > GITHUB_TOKEN in gh_token()
## don't want developer's GITHUB_PAT to override gh-testing's GITHUB_TOKEN
res <- gh_whoami(Sys.getenv("GITHUB_TOKEN"))
expect_s3_class(res, "gh_response")
expect_identical(res[["login"]], "gh-testing")
expect_match(res[["scopes"]], "\\brepo\\b")
expect_match(res[["scopes"]], "\\buser\\b")
})

test_that("whoami works in absence of PAT", {
expect_message(res <- gh_whoami(.token = ""),
"No personal access token \\(PAT\\) available.")
expect_null(res)
})

test_that("whoami errors with bad PAT", {
skip("re-activate when request matching sorted out (gaborcsardi/httrmock#3)")

e <- tryCatch(gh_whoami(.token = NA), error = identity)
expect_s3_class(e, "github_error")
expect_s3_class(e, "http_error_401")

e <- tryCatch(gh_whoami(.token = "blah"), error = identity)
expect_s3_class(e, "github_error")
expect_s3_class(e, "http_error_401")
})

0 comments on commit 951031e

Please sign in to comment.