Skip to content

Commit

Permalink
Introduce drive_scopes()
Browse files Browse the repository at this point in the history
Closes #430
  • Loading branch information
jennybc committed Jun 7, 2023
1 parent 4ed4ead commit a1216a5
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 13 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export(drive_read_string)
export(drive_rename)
export(drive_reveal)
export(drive_rm)
export(drive_scopes)
export(drive_share)
export(drive_share_anyone)
export(drive_token)
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# googledrive (development version)

* `drive_scopes()` is a new function to access scopes used with the Drive API.
When called without arguments, `drive_scopes()` returns a named vector scopes,
where the names are the associated short aliases. `drive_scopes()` can also
be called with a character vector; any element that's recognized as a short
alias is replaced with the associated full scope (#430).

# googledrive 2.1.0

## Syncing up with gargle
Expand Down
62 changes: 61 additions & 1 deletion R/drive_auth.R
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ gargle_lookup_table <- list(
#' @eval gargle:::PREFIX_auth_details(gargle_lookup_table)
#' @eval gargle:::PREFIX_auth_params()
#'
#' @param scopes One or more API scopes. Each scope can be specified in full or,
#' for Drive API-specific scopes, in an abbreviated form that is recognized by
#' [drive_scopes()]:
#' * "full" = "https://www.googleapis.com/auth/drive" (the default)
#' * "drive.readonly" = "https://www.googleapis.com/auth/drive.readonly"
#' * "drive.file" = "https://www.googleapis.com/auth/drive.file"
#' * "drive.appdata" = "https://www.googleapis.com/auth/drive.appdata"
#' * "drive.metadata" = "https://www.googleapis.com/auth/drive.metadata"
#' * "drive.metadata.readonly" = "https://www.googleapis.com/auth/drive.metadata.readonly"
#' * "drive.photos.readonly" = "https://www.googleapis.com/auth/drive.photos.readonly"
#' * "drive.scripts" = "https://www.googleapis.com/auth/drive.scripts
#'
#' See <https://developers.google.com/drive/api/guides/api-specific-auth> for
#' details on the permissions for each scope.
#'
#' @family auth functions
#' @export
#'
Expand Down Expand Up @@ -47,11 +62,12 @@ gargle_lookup_table <- list(
#' drive_auth(path = "foofy-83ee9e7c9c48.json")
drive_auth <- function(email = gargle::gargle_oauth_email(),
path = NULL,
scopes = "https://www.googleapis.com/auth/drive",
scopes = "full",
cache = gargle::gargle_oauth_cache(),
use_oob = gargle::gargle_oob_default(),
token = NULL) {
gargle::check_is_service_account(path, hint = "drive_auth_configure")
scopes <- drive_scopes(scopes)
env_unbind(.googledrive, "root_folder")

# If `token` is not `NULL`, it's much better to error noisily now, before
Expand Down Expand Up @@ -222,6 +238,50 @@ drive_oauth_client <- function() {
.auth$client
}

#' Produce scopes specific to the Drive API
#'
#' When called with no arguments, `drive_scopes()` returns a named character vector
#' of scopes associated with the Drive API. If `drive_scopes(scopes =)` is given,
#' an abbreviated entry such as `"drive.readonly"` is expanded to a full scope
#' (`"https://www.googleapis.com/auth/drive.readonly"` in this case).
#' Unrecognized scopes are passed through unchanged.
#'
#' @inheritParams drive_auth
#'
#' @seealso <https://developers.google.com/drive/api/guides/api-specific-auth> for details on
#' the permissions for each scope.
#' @returns A character vector of scopes.
#' @family auth functions
#' @export
#' @examples
#' drive_scopes("full")
#' drive_scopes("drive.readonly")
#' drive_scopes()
drive_scopes <- function(scopes = NULL) {
if (is.null(scopes)) {
drive_api_scopes
} else {
resolve_scopes(user_scopes = scopes, package_scopes = drive_api_scopes)
}
}

drive_api_scopes <- c(
full = "https://www.googleapis.com/auth/drive",
drive = "https://www.googleapis.com/auth/drive",
drive.readonly = "https://www.googleapis.com/auth/drive.readonly",
drive.file = "https://www.googleapis.com/auth/drive.file",
drive.appdata = "https://www.googleapis.com/auth/drive.appdata",
drive.metadata = "https://www.googleapis.com/auth/drive.metadata",
drive.metadata.readonly = "https://www.googleapis.com/auth/drive.metadata.readonly",
drive.photos.readonly = "https://www.googleapis.com/auth/drive.photos.readonly",
drive.scripts = "https://www.googleapis.com/auth/drive.scripts"
)

resolve_scopes <- function(user_scopes, package_scopes) {
m <- match(user_scopes, names(package_scopes))
ifelse(is.na(m), user_scopes, package_scopes[m])
}

# unexported helpers that are nice for internal use ----
drive_auth_internal <- function(account = c("docs", "testing"),
scopes = NULL) {
Expand Down
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ reference:
- drive_auth
- drive_deauth
- drive_auth_configure
- drive_scopes
- title: "Drive API spec"
desc: >
Summon info about or check input against the Drive API spec
Expand Down
28 changes: 18 additions & 10 deletions man/drive_auth.Rd

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

3 changes: 2 additions & 1 deletion man/drive_auth_configure.Rd

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

3 changes: 2 additions & 1 deletion man/drive_deauth.Rd

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

51 changes: 51 additions & 0 deletions man/drive_scopes.Rd

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

24 changes: 24 additions & 0 deletions tests/testthat/_snaps/drive_auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,27 @@
Error in `drive_auth_configure()`:
! Must supply exactly one of `client` or `path`, not both

# drive_scopes() reveals Drive scopes

Code
drive_scopes()
Output
full
"https://www.googleapis.com/auth/drive"
drive
"https://www.googleapis.com/auth/drive"
drive.readonly
"https://www.googleapis.com/auth/drive.readonly"
drive.file
"https://www.googleapis.com/auth/drive.file"
drive.appdata
"https://www.googleapis.com/auth/drive.appdata"
drive.metadata
"https://www.googleapis.com/auth/drive.metadata"
drive.metadata.readonly
"https://www.googleapis.com/auth/drive.metadata.readonly"
drive.photos.readonly
"https://www.googleapis.com/auth/drive.photos.readonly"
drive.scripts
"https://www.googleapis.com/auth/drive.scripts"

35 changes: 35 additions & 0 deletions tests/testthat/test-drive_auth.R
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,38 @@ test_that("drive_auth_configure works", {
drive_auth_configure(api_key = NULL)
expect_null(drive_api_key())
})

# drive_scopes() ----
test_that("drive_scopes() reveals Drive scopes", {
expect_snapshot(drive_scopes())
})

test_that("drive_scopes() substitutes actual scope for short form", {
expect_equal(
drive_scopes(c(
"full",
"drive",
"drive.readonly"
)),
c(
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/drive.readonly"
)
)
})

test_that("drive_scopes() passes unrecognized scopes through", {
expect_equal(
drive_scopes(c(
"email",
"drive.metadata.readonly",
"https://www.googleapis.com/auth/cloud-platform"
)),
c(
"email",
"https://www.googleapis.com/auth/drive.metadata.readonly",
"https://www.googleapis.com/auth/cloud-platform"
)
)
})

0 comments on commit a1216a5

Please sign in to comment.