From 761ed0783ea6795ad69574bd40ef5fd971aed202 Mon Sep 17 00:00:00 2001 From: Clayton Yochum Date: Wed, 18 Oct 2017 01:37:19 -0400 Subject: [PATCH] Separate API calls to internal functions, excluding data-transfer and auth. --- DESCRIPTION | 3 +- R/drop_acc.R | 16 ++-- R/drop_dir.R | 100 ++++++++++--------------- R/drop_file_ops.R | 165 ++++++++++++++++++++++++++++-------------- R/drop_get_metadata.R | 42 +++++++---- R/drop_history.R | 38 +++++----- R/drop_media.R | 22 ++++-- R/drop_search.R | 47 +++++++----- R/drop_shared.R | 83 ++++++++++++++------- R/drop_utils.R | 23 ++++++ 10 files changed, 337 insertions(+), 202 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 3f3beb4..422c863 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -18,7 +18,8 @@ Imports: httr, jsonlite, magrittr, - purrr + purrr, + tibble Suggests: testthat, uuid RoxygenNote: 6.0.1 diff --git a/R/drop_acc.R b/R/drop_acc.R index d0fd279..a3556f4 100644 --- a/R/drop_acc.R +++ b/R/drop_acc.R @@ -4,7 +4,6 @@ #' #' @template token #' -#' @import httr #' @export #' #' @return @@ -28,9 +27,16 @@ #' } drop_acc <- function(dtoken = get_dropbox_token()) { - url <- "https://api.dropbox.com/2/users/get_current_account" + api_get_current_account(dtoken) +} + + +#' API wrapper for users/get_current_account +#' +#' @noRd +#' +#' @keywords internal +api_get_current_account <- function(dtoken) { - # make request and parse response - req <- httr::POST(url, httr::config(token = dtoken)) - httr::content(req) + post_api("https://api.dropbox.com/2/users/get_current_account", dtoken) } diff --git a/R/drop_dir.R b/R/drop_dir.R index c22d670..60c98fa 100644 --- a/R/drop_dir.R +++ b/R/drop_dir.R @@ -60,12 +60,12 @@ drop_dir <- function( if (is.character(cursor)) { # list changes since cursor - content <- drop_list_folder_continue(cursor, dtoken) + content <- api_list_folder_continue(cursor, dtoken) } else if (cursor) { # get a cursor to track changes against - content <- drop_list_folder_get_latest_cursor( + content <- api_list_folder_get_latest_cursor( path, recursive, include_media_info, @@ -80,7 +80,7 @@ drop_dir <- function( } else { # list files normally - content <- drop_list_folder( + content <- api_list_folder( path, recursive, include_media_info, @@ -101,7 +101,7 @@ drop_dir <- function( while (content$has_more) { # update content, append results - content <- drop_list_folder_continue(content$cursor) + content <- api_list_folder_continue(content$cursor) results <- append(results, content$entries) } } @@ -111,18 +111,17 @@ drop_dir <- function( } -#' List contents of a Dropbox folder. -#' -#' For internal use; drop_dir should generally be used to list files in a folder. +#### API wrappers ##### + +#' API wrapper for files/list_folder #' #' @return a list with three elements: \code{entries}, \code{cursor}, and \code{has_more}. #' #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder}{API reference} #' #' @noRd -#' #' @keywords internal -drop_list_folder <- function( +api_list_folder <- function( path, recursive = FALSE, include_media_info = FALSE, @@ -133,55 +132,43 @@ drop_list_folder <- function( dtoken = get_dropbox_token() ) { - url <- "https://api.dropboxapi.com/2/files/list_folder" - - req <- httr::POST( - url = url, - httr::config(token = dtoken), - body = drop_compact(list( - path = path, - recursive = recursive, - include_media_info = include_media_info, - include_deleted = include_deleted, - include_has_explicit_shared_members = include_has_explicit_shared_members, - include_mounted_folders = include_mounted_folders, - limit = limit - )), - encode = "json" + post_api( + url = "https://api.dropboxapi.com/2/files/list_folder", + token = dtoken, + path, + recursive, + include_media_info, + include_deleted, + include_has_explicit_shared_members, + include_mounted_folders, + limit ) - - httr::stop_for_status(req) - - httr::content(req) } +#' API wrapper for files/list_folder/continue +#' #' Fetch additional results from a cursor #' -#' @return see \code{drop_list_folder} +#' @return see \code{api_list_folder} #' #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue}{Dropbox API} #' #' @noRd #' #' @keywords internal -drop_list_folder_continue <- function(cursor, dtoken = get_dropbox_token()) { +api_list_folder_continue <- function(cursor, dtoken = get_dropbox_token()) { - url <- "https://api.dropboxapi.com/2/files/list_folder/continue" - - req <- httr::POST( - url = url, - httr::config(token = dtoken), - body = list(cursor = cursor), - encode = "json" + post_api( + "https://api.dropboxapi.com/2/files/list_folder/continue", + dtoken, + cursor ) - - httr::stop_for_status(req) - - httr::content(req) } +#' API wrapper for files/list_folder/get_latest_cursor +#' #' Get the current cursor for a set of path + options #' #' @return a cursor, a string uniquely identifying a folder and how much of it has been listed @@ -191,7 +178,7 @@ drop_list_folder_continue <- function(cursor, dtoken = get_dropbox_token()) { #' @noRd #' #' @keywords internal -drop_list_folder_get_latest_cursor <- function( +api_list_folder_get_latest_cursor <- function( path, recursive = FALSE, include_media_info = FALSE, @@ -202,24 +189,15 @@ drop_list_folder_get_latest_cursor <- function( dtoken = get_dropbox_token() ) { - url <- "https://api.dropboxapi.com/2/files/list_folder/get_latest_cursor" - - req <- httr::POST( - url = url, - httr::config(token = dtoken), - body = drop_compact(list( - path = path, - recursive = recursive, - include_media_info = include_media_info, - include_deleted = include_deleted, - include_has_explicit_shared_members = include_has_explicit_shared_members, - include_mounted_folders = include_mounted_folders, - limit = limit - )), - encode = "json" + post_api( + "https://api.dropboxapi.com/2/files/list_folder/get_latest_cursor", + dtoken, + path, + recursive, + include_media_info, + include_deleted, + include_has_explicit_shared_members, + include_mounted_folders, + limit ) - - httr::stop_for_status(req) - - httr::content(req) } diff --git a/R/drop_file_ops.R b/R/drop_file_ops.R index 485e93a..125205e 100644 --- a/R/drop_file_ops.R +++ b/R/drop_file_ops.R @@ -28,7 +28,6 @@ drop_copy <- allow_ownership_transfer = FALSE, verbose = FALSE, dtoken = get_dropbox_token()) { - copy_url <- "https://api.dropboxapi.com/2/files/copy_v2" from_path <- add_slashes(from_path) to_path <- add_slashes(to_path) @@ -51,25 +50,17 @@ drop_copy <- # Nothing to do, since both paths reflect origin and destination # Copying a folder to an existing filename will result in a HTTP 409 (conflict error) - - args <- drop_compact( - list( - from_path = from_path, - to_path = to_path, - allow_shared_folder = allow_shared_folder, - autorename = autorename, - allow_ownership_transfer = allow_ownership_transfer - ) - ) - if (drop_exists(from_path)) { # copy - x <- - httr::POST(copy_url, - httr::config(token = dtoken), - body = args, - encode = "json") - res <- httr::content(x) + res <- api_copy( + from_path, + to_path, + allow_shared_folder, + autorename, + allow_ownership_transfer, + dtoken + ) + if (!verbose) { message(sprintf("%s copied to %s", from_path, res$metadata$path_lower)) invisible(res) @@ -111,7 +102,6 @@ drop_move <- allow_ownership_transfer = FALSE, verbose = FALSE, dtoken = get_dropbox_token()) { - move_url <- "https://api.dropboxapi.com/2/files/move_v2" from_path <- add_slashes(from_path) to_path <- add_slashes(to_path) @@ -135,24 +125,16 @@ drop_move <- # Moving a folder to an existing filename will result in a HTTP 409 (conflict error) - args <- drop_compact( - list( - from_path = from_path, - to_path = to_path, - allow_shared_folder = allow_shared_folder, - autorename = autorename, - allow_ownership_transfer = allow_ownership_transfer - ) - ) - if (drop_exists(from_path)) { # move - x <- - httr::POST(move_url, - httr::config(token = dtoken), - body = args, - encode = "json") - res <- httr::content(x) + res <- api_move( + from_path, + to_path, + allow_shared_folder, + autorename, + allow_ownership_transfer, + dtoken + ) if (!verbose) { message(sprintf("%s moved to %s", from_path, res$metadata$path_lower)) @@ -178,17 +160,11 @@ drop_delete <- function (path = NULL, verbose = FALSE, dtoken = get_dropbox_token()) { - create_url <- "https://api.dropboxapi.com/2/files/delete_v2" + if (drop_exists(path)) { path <- add_slashes(path) - x <- - httr::POST( - create_url, - httr::config(token = dtoken), - body = list(path = path), - encode = "json" - ) - res <- httr::content(x) + + res <- api_delete(path, dtoken) if (verbose) { res @@ -224,17 +200,10 @@ drop_create <- # if a folder exists, but autorename is TRUE, proceed # However, if a folder exists, and autorename if FALSE, fail in the else. if (!drop_exists(path) || autorename) { - create_url <- "https://api.dropboxapi.com/2/files/create_folder_v2" path <- add_slashes(path) - x <- - httr::POST( - create_url, - config(token = dtoken), - body = list(path = path, autorename = autorename), - encode = "json" - ) - results <- httr::content(x) + + results <- api_create_folder(path, autorename, dtoken) if (verbose) { pretty_lists(results) @@ -336,3 +305,93 @@ drop_type <- function(x, dtoken = get_dropbox_token()) { x$result$.tag } } + + + +#### API wrappers #### + +#' API wrapper for files/copy_v2 +#' +#' @noRd +#' +#' @keywords internal +api_copy <- function( + from_path, + to_path, + allow_shared_folder = FALSE, + autorename = FALSE, + allow_ownership_transfer = FALSE, + dtoken +) { + + post_api( + "https://api.dropboxapi.com/2/files/copy_v2", + dtoken, + from_path , + to_path, + allow_shared_folder, + autorename, + allow_ownership_transfer + ) +} + + +#' API wrapper for files/move_v2 +#' +#' @noRd +#' +#' @keywords internal +api_move <- function( + from_path, + to_path, + allow_shared_folder = FALSE, + autorename = FALSE, + allow_ownership_transfer = FALSE, + dtoken +) { + + post_api( + "https://api.dropboxapi.com/2/files/move_v2", + dtoken, + from_path, + to_path, + allow_shared_folder, + autorename, + allow_ownership_transfer + ) +} + + +#' API wrapper for files/delete_v2 +#' +#' @noRd +#' +#' @keywords internal +api_delete <- function(path, dtoken) { + + post_api( + "https://api.dropboxapi.com/2/files/delete_v2", + dtoken, + path + ) +} + + +#' API wrapper for files/create_folder_v2 +#' +#' @noRd +#' +#' @keywords internal +api_create_folder <- function( + path, + autorename = FALSE, + dtoken +) { + + post_api( + "https://api.dropboxapi.com/2/files/create_folder_v2", + dtoken, + path, + autorename + ) +} diff --git a/R/drop_get_metadata.R b/R/drop_get_metadata.R index 23f5a87..26e183a 100644 --- a/R/drop_get_metadata.R +++ b/R/drop_get_metadata.R @@ -21,23 +21,37 @@ drop_get_metadata <- function( dtoken = get_dropbox_token() ) { - url <- "https://api.dropboxapi.com/2/files/get_metadata" - if (!grepl("^(id|rev):", path)) path <- add_slashes(path) - req <- httr::POST( - url = url, - httr::config(token = dtoken), - body = list( - path = path, - include_media_info = include_media_info, - include_deleted = include_deleted, - include_has_explicit_shared_members = include_has_explicit_shared_members - ), - encode = "json" + api_get_metadata( + path, + include_media_info, + include_deleted, + include_has_explicit_shared_members, + dtoken ) +} - httr::stop_for_status(req) - httr::content(req) +#' API wrapper for files/get_metadata +#' +#' @noRd +#' +#' @keywords internal +api_get_metadata <- function( + path, + include_media_info = FALSE, + include_deleted = FALSE, + include_has_explicit_shared_members = FALSE, + dtoken +) { + + post_api( + "https://api.dropboxapi.com/2/files/get_metadata", + dtoken, + path, + include_media_info, + include_deleted, + include_has_explicit_shared_members + ) } diff --git a/R/drop_history.R b/R/drop_history.R index 75e8bbd..7c2e2c4 100644 --- a/R/drop_history.R +++ b/R/drop_history.R @@ -20,17 +20,22 @@ #' @export drop_history <- function(path, limit = 10, dtoken = get_dropbox_token()) { - content <- drop_list_revisions(path, limit, dtoken) + content <- drop_list_revisions( + path = add_slashes(path), + limit = limit, + dtoken = dtoken + ) dplyr::bind_rows(content$entries) } -#' Get revision history of a file +#' API wrapper for files/list_revisions #' -#' Does not include deletions. +#' Get revision history of a file. Does not include deletions. #' #' @param path path to a file in Dropbox. +#' @param mode "path" or "id" (unexposed) #' @param limit maximum number of revisions to return; defaults to 10. #' @template token #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-list_revisions}{API documentation} @@ -44,21 +49,18 @@ drop_history <- function(path, limit = 10, dtoken = get_dropbox_token()) { #' @noRd #' #' @keywords internal -drop_list_revisions <- function(path, limit = 10, dtoken = get_dropbox_token()) { - - url <- "https://api.dropboxapi.com/2/files/list_revisions" +drop_list_revisions <- function( + path, + mode = "path", + limit = 10, + dtoken +) { - req <- httr::POST( - url = url, - httr::config(token = dtoken), - body = list( - path = add_slashes(path), - limit = limit - ), - encode = "json" + post_api( + "https://api.dropboxapi.com/2/files/list_revisions", + dtoken, + path, + mode, + limit ) - - httr::stop_for_status(req) - - httr::content(req) } diff --git a/R/drop_media.R b/R/drop_media.R index 54e9bd2..23ef89a 100644 --- a/R/drop_media.R +++ b/R/drop_media.R @@ -17,13 +17,25 @@ #'} drop_media <- function(path = NULL, dtoken = get_dropbox_token()) { assertive::assert_is_not_null(path) - if(drop_exists(path)) { - media_url <- "https://api.dropbox.com/2/files/get_temporary_link" - path <- add_slashes(path) - res <- POST(media_url, body = list(path = path), httr::config(token = dtoken), encode = "json") - content(res) + if (drop_exists(path)) { + api_get_temporary_link(add_slashes(path), dtoken) } else { stop("File not found \n") FALSE } } + + +#' API wrapper for files/get_temporary_link +#' +#' @noRd +#' +#' @keywords internal +api_get_temporary_link <- function(path, dtoken) { + + post_api( + "https://api.dropbox.com/2/files/get_temporary_link", + dtoken, + path + ) +} diff --git a/R/drop_search.R b/R/drop_search.R index 664df89..aa010e1 100644 --- a/R/drop_search.R +++ b/R/drop_search.R @@ -31,27 +31,40 @@ drop_search <- function(query, assertive::assert_any_are_matching_fixed(available_modes, mode) # A search cannot have a negative start index and a negative max_results assertive::assert_all_are_non_negative(start, max_results) - args <- drop_compact( - list( - query = query, - path = path, - start = as.integer(start), - max_results = as.integer(max_results), - mode = mode - ) - ) - search_url <- "https://api.dropboxapi.com/2/files/search" - res <- - httr::POST(search_url, - body = args, - httr::config(token = dtoken), - encode = "json") - httr::stop_for_status(res) - httr::content(res) + start <- as.integer(start) + max_results <- as.integer(max_results) + + api_search(path, query, start, max_results, mode, dtoken) # TODO # Need to do a verbose return but also print a nice data.frame # One way to do that is with purrr::flatten # e.g. purrr::flatten(results$matches) # But, do we want purrr as another import??? } + + +#' API wrapper for files/search +#' +#' @noRd +#' +#' @keywords internal +api_search <- function( + path, + query, + start = 0L, + max_results = 100L, + mode = "filename", + dtoken +) { + + post_api( + "https://api.dropboxapi.com/2/files/search", + dtoken, + path, + query, + start, + max_results, + mode + ) +} diff --git a/R/drop_shared.R b/R/drop_shared.R index 4850563..d36a676 100644 --- a/R/drop_shared.R +++ b/R/drop_shared.R @@ -41,28 +41,14 @@ drop_share <- function(path = NULL, # exists on Dropbox before proceeding path <- add_slashes(path) - settings <- - drop_compact( - list( - requested_visibility = requested_visibility, - link_password = link_password, - expires = expires - ) - ) - # TODO: Check to see if this is necessary when we have encode to json below - share_url <- - "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings" - - req <- httr::POST( - url = share_url, - httr::config(token = dtoken), - body = list(path = path, settings = settings), - encode = "json" - ) - # stopping for status otherwise content fails - httr::stop_for_status(req) - response <- httr::content(req) - response + + settings <- purrr::compact(tibble::lst( + requested_visibility, + link_password, + expires + )) + + api_create_shared_link_with_settings(path, settings, dtoken) } #' List all shared links @@ -79,12 +65,9 @@ drop_share <- function(path = NULL, #' } drop_list_shared_links <- function(verbose = TRUE, dtoken = get_dropbox_token()) { - shared_links_url <- - "https://api.dropboxapi.com/2/sharing/list_shared_links" - res <- - httr::POST(shared_links_url, httr::config(token = dtoken), encode = "json") - httr::stop_for_status(res) - z <- httr::content(res) + + z <- api_list_shared_links(dtoken = dtoken)$links + if (verbose) { invisible(z) pretty_lists(z) @@ -94,3 +77,47 @@ drop_list_shared_links <- # Clean up the verbose and non-verbose options } } + + +#### API wrappers #### + +#' API wrapper for sharing/create_shared_link_with_settings +#' +#' @noRd +#' +#' @keywords internal +api_create_shared_link_with_settings <- function( + path, + settings = NULL, + dtoken +) { + + post_api( + "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings", + dtoken, + path, + settings + ) +} + + +#' API wrapper for sharing/list_shared_links +#' +#' @noRd +#' +#' @keywords internal +api_list_shared_links <- function( + path = NULL, + cursor = NULL, + direct_only = NULL, + dtoken +) { + + post_api( + "https://api.dropboxapi.com/2/sharing/list_shared_links", + dtoken, + path, + cursor, + direct_only + ) +} diff --git a/R/drop_utils.R b/R/drop_utils.R index 196b5d6..681bc64 100644 --- a/R/drop_utils.R +++ b/R/drop_utils.R @@ -8,6 +8,29 @@ #' @usage lhs \%>\% rhs NULL +#' Common pattern for calling most API endpoints +#' +#' Additional args are passed to API, with names inferred from calling environment. +#' +#' @noRd +#' @keywords internal +post_api <- function(url, token, ...) { + + # if no args, leave NULL + body <- purrr::compact(tibble::lst(...)) + if (purrr::is_empty(body)) body <- NULL + + response <- httr::POST( + url = url, + httr::config(token = token), + body = body, + encode = "json" + ) + + httr::stop_for_status(response) + + httr::content(response) +} #' A local version of list compact from plyr. #' @noRd