├── .Rbuildignore ├── .Rhistory ├── .gitignore ├── .travis.yml ├── CRAN-RELEASE ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── NEWS.md ├── R ├── drop_acc.R ├── drop_auth.R ├── drop_content_hash.R ├── drop_dir.R ├── drop_download.R ├── drop_file_ops.R ├── drop_get_metadata.R ├── drop_history.R ├── drop_media.R ├── drop_read_csv.R ├── drop_search.R ├── drop_shared.R ├── drop_upload.R └── drop_utils.R ├── README.md ├── cran-comments.md ├── docs ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── rdrop2-logo.png ├── man-roxygen ├── file_limit.R ├── from_to.R ├── include_membership.R ├── locale.R ├── path.R ├── token.R └── verbose.R ├── man ├── drop_acc.Rd ├── drop_auth.Rd ├── drop_content_hash.Rd ├── drop_copy.Rd ├── drop_create.Rd ├── drop_delete.Rd ├── drop_dir.Rd ├── drop_download.Rd ├── drop_exists.Rd ├── drop_get.Rd ├── drop_get_metadata.Rd ├── drop_history.Rd ├── drop_list_shared_links.Rd ├── drop_media.Rd ├── drop_move.Rd ├── drop_read_csv.Rd ├── drop_search.Rd ├── drop_share.Rd ├── drop_upload.Rd ├── get_dropbox_token.Rd └── pipe.Rd ├── rDrop2.Rproj └── tests ├── testthat.R └── testthat ├── helper-01.R ├── rdrop2_package_test_image.png ├── test-01_rdrop-auth.R ├── test-02_rdrop2-upload.R ├── test-04-rdrop2-metadata.R ├── test-05-rdrop2-dir.R ├── test-06-rdrop2-download.R ├── test-07-drop_ops.R ├── test-08-rdrop2-content-hash.R ├── test-99-rdrop2.R └── token.rds.enc /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^\.httr-oauth$ 4 | ^\.travis\.yml$ 5 | ^docs$ 6 | Makefile 7 | README.* 8 | .httr-oauth.enc 9 | tests/testthat/.httr-oauth.enc 10 | tests/testthat/.httr-oauth 11 | ^.*\.gif$ 12 | ^.*\.csv$ 13 | zzz.R 14 | man-roxygen 15 | cache/ 16 | cran-comments.md 17 | ^CRAN-RELEASE$ 18 | -------------------------------------------------------------------------------- /.Rhistory: -------------------------------------------------------------------------------- 1 | library(devtools) 2 | load_all() 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | zzz.R 2 | .Rproj.user 3 | .httr-oauth 4 | Makefile 5 | ^.*\.Rproj$ 6 | cache/ 7 | *.csv 8 | *.gif 9 | token.rds 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: r 2 | sudo: false 3 | cache: packages 4 | before_install: 5 | - openssl aes-256-cbc -K $encrypted_ad3262635b9a_key -iv $encrypted_ad3262635b9a_iv -in tests/testthat/token.rds.enc -out tests/testthat/token.rds -d 6 | r_packages: 7 | - uuid 8 | r_github_packages: 9 | - jimhester/covr 10 | env: 11 | - warnings_are_errors: false 12 | after_success: 13 | - Rscript -e 'library(covr); coveralls()' 14 | -------------------------------------------------------------------------------- /CRAN-RELEASE: -------------------------------------------------------------------------------- 1 | This package was submitted to CRAN on 2020-08-03. 2 | Once it is accepted, delete this file and tag the release (commit dc351775be). 3 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rdrop2 2 | Title: Programmatic Interface to the 'Dropbox' API 3 | Version: 0.8.2.2 4 | Authors@R: c(person("Karthik", "Ram", email = "karthik.ram@gmail.com", role = c("aut", "cre")), 5 | person("Clayton", "Yochum", role = "aut"), 6 | person("Caleb", "Scheidel", role = "ctb"), 7 | person("Akhil", "Bhel", role = "cph") 8 | ) 9 | Description: Provides full programmatic access to the 'Dropbox' file hosting platform , including support for all standard file operations. 10 | Depends: R (>= 3.1.1) 11 | License: MIT + file LICENSE 12 | BugReports: https://github.com/karthik/rdrop2/issues 13 | LazyData: true 14 | Imports: digest, 15 | dplyr, 16 | httr, 17 | jsonlite, 18 | magrittr, 19 | purrr, 20 | assertthat 21 | Suggests: testthat, 22 | uuid 23 | RoxygenNote: 7.2.3 24 | Encoding: UTF-8 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2015 2 | COPYRIGHT HOLDER: Karthik Ram -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export("%>%") 4 | export(drop_acc) 5 | export(drop_auth) 6 | export(drop_content_hash) 7 | export(drop_copy) 8 | export(drop_create) 9 | export(drop_delete) 10 | export(drop_dir) 11 | export(drop_download) 12 | export(drop_exists) 13 | export(drop_get) 14 | export(drop_get_metadata) 15 | export(drop_history) 16 | export(drop_list_shared_links) 17 | export(drop_media) 18 | export(drop_move) 19 | export(drop_read_csv) 20 | export(drop_search) 21 | export(drop_share) 22 | export(drop_upload) 23 | import(httr) 24 | importFrom(magrittr,"%>%") 25 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # rdrop2 0.8.2.2 2 | * replaced dependency package `assertive` by `assertthat`. 3 | 4 | # rdrop2 0.8.2 5 | * Minor update to unarchive on CRAN (archived because assertive was archived) 6 | 7 | 8 | # rdrop2 0.8.1 9 | * `drop_dir` now flattens nested responses so return can be coerced to `tbl_df`, e.g. if returned files/folders are shared with other (#135) 10 | * `drop_download` now returns `TRUE` when download was successful; should mitigate errors noted by multiple users (#132) 11 | 12 | # rdrop2 0.8 13 | * This version is a major update from V1 of the Dropbox API (being deprecated on September 28, 2017) to V2. 🎉 14 | * `drop_acc` - no longer returns quota information but still returns all other account related details. Quota retrieval will become available again on `0.9` 15 | * `drop_auth` - now allows for specifying a saved token with `rdstoken` 16 | * `drop_copy` and `drop_move` - no longer allow a `root` argument. These functions also now have 3 additional options. `allow_shared_folder` (allows for copying/moving shared folders), `autorename`, renames objects in cases of conflict, and `allow_ownership_transfer` allows ownership transfer if a copy/move operation results in such a change. 17 | * `drop_create` - no longer includes a `root` argument. Function now allows for `autorename` to rename folders in case of conflicts. 18 | * `drop_delete` - loses the `root` argument but functionally remains unchanged. 19 | * `drop_delta` - no longer exists and functionality has been folded into `drop_dir`. `cursor` can be passed to `drop_dir` (see below). 20 | * `drop_dir` - now allows for `recursive` listing. In addition, one can filter by `include_has_explicit_shared_members` (has been shared explicitly), `include_mounted_folders`. `file_limit` has been changed to `limit`. 21 | * `drop_get` - has been deprecated and replaced with `drop_download`. `drop_get` will be removed in a future version. In `drop_download`, `local_file` has been replaced with `local_path`. 22 | * `drop_history` - now includes a limit argument (for number of versions required) 23 | * `drop_media` - no longer has a `locale` argument 24 | * `drop_read_csv` - remains unchanged. However, in future versions, it will become a generic wrapper with support for custom handlers. 25 | * `drop_search` - `file_limit` is now `max_results`. Additionally one can specify an offset with `start`. `locale` is no longer an argument. `include_deleted` is also not a function argument but returned as part of the result metadata. New option called `mode` allows for `filename`, `filename_and_content`, or for restricting search to deleted files with `deleted_filename`. 26 | * `drop_share` - no longer has `locale` or `short_url` as arguments. Function now allows more granularity in visibility (public, team_only, or password protected), and also allows users to set an expire time. 27 | * `drop_upload` - `dest` is now `path`. Overwrite argument has been removed and replaced with mode, which can take overwrite or add. Additionally if `autorename` is set to TRUE, uploaded objects with conflicts will be renamed. Support for update mode will be implemented in `0.9`. Function also allows for muting upload notifications on clients. 28 | 29 | # rdrop2 0.7 30 | 31 | * Added basic support for progress bars for `drop_get()` 32 | * Added new argument `n` to `drop_dir` to control number of rows printed by `tbl_df` 33 | * Empty folder return a empty `data.frame` instead of `NULL` (thanks @daattali) 34 | * Fixed bug with NULL revision in `drop_get` (thanks @moggces) 35 | * Miscellaneous bug fixes 36 | 37 | # rdrop2 0.6 38 | 39 | * Initial release to CRAN. 40 | -------------------------------------------------------------------------------- /R/drop_acc.R: -------------------------------------------------------------------------------- 1 | #' Get information about current Dropbox account. 2 | #' 3 | #' Fields returned will vary by account; 4 | #' 5 | #' @template token 6 | #' 7 | #' @import httr 8 | #' @export 9 | #' 10 | #' @return 11 | #' Nested list with elements \code{account_id}, 12 | #' \code{name} (list), \code{email}, \code{email_verified}, \code{disabled}, 13 | #' \code{locale}, \code{referral_link}, \code{is_paired}, \code{account_type} 14 | #' (list). 15 | #' 16 | #' If available, may also return \code{profile_photo_url}, 17 | #' \code{country}, \code{team} (list), \code{team_member_id}. 18 | #' 19 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account}{API documentation} 20 | #' 21 | #' @examples 22 | #' \dontrun{ 23 | #' 24 | #' acc_info <- drop_acc() 25 | #' 26 | #' # extract display name 27 | #' acc_info$name$display_name 28 | #' } 29 | drop_acc <- function(dtoken = get_dropbox_token()) { 30 | 31 | url <- "https://api.dropbox.com/2/users/get_current_account" 32 | 33 | # make request and parse response 34 | req <- httr::POST(url, httr::config(token = dtoken)) 35 | httr::content(req) 36 | } 37 | -------------------------------------------------------------------------------- /R/drop_auth.R: -------------------------------------------------------------------------------- 1 | # environment to store credentials 2 | .dstate <- new.env(parent = emptyenv()) 3 | 4 | 5 | #' Authentication for Dropbox 6 | #' 7 | #' This function authenticates you into Dropbox. The documentation for the 8 | #' \href{https://www.dropbox.com/developers/documentation?_tk=pilot_lp&_ad=topbar1&_camp=docs}{core Dropbox API} 9 | #' provides more details including alternate methods if you desire to 10 | #' reimplement your own. 11 | #' 12 | #' @param new_user Set to \code{TRUE} if you need to switch to a new user 13 | #' account or just flush existing token. Default is \code{FALSE}. 14 | #' @param key Your application key. \code{rdrop2} already comes with a key/secret but 15 | #' you are welcome to swap out with our own. Since these keys are shipped with 16 | #' the package, there is a small chance they could be voided if someone abuses 17 | #' the key. If you plan to use this in production, or for an internal tool, 18 | #' the recommended practice is to create a new application on Dropbox and use 19 | #' those keys for your purposes. 20 | #' @param secret Your application secret. Like \code{key}, \code{rdrop2} comes 21 | #' with a secret but you are welcome to swap out with our own. 22 | #' @param cache By default your credentials are locally cached in a file called 23 | #' \code{.httr-oauth}. Set to FALSE if you need to authenticate separately 24 | #' each time. 25 | #' @param rdstoken File path to stored RDS token. In server environments where 26 | #' interactive OAuth is not possible, a token can be created on a desktop 27 | #' client and used in production. See examples. 28 | #' 29 | #' @return A Token2.0 object, invisibly 30 | #' 31 | #' @import httr 32 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#authorization}{API documentation} 33 | #' @export 34 | #' 35 | #' @examples 36 | #' \dontrun{ 37 | #' 38 | #' # To either read token from .httr-oauth in the working directory or open a 39 | #' # web browser to authenticate (and cache a token) 40 | #' drop_auth() 41 | #' 42 | #' # If you want to overwrite an existing local token and switch to a new 43 | #' # user, set new_user to TRUE. 44 | #' drop_auth(new_user = TRUE) 45 | #' 46 | #' # To store a token for re-use (more flexible than .httr-oauth), save the 47 | #' # output of drop_auth and save it to an RDS file 48 | #' token <- drop_auth() 49 | #' saveRDS(token, "/path/to/tokenfile.RDS") 50 | #' 51 | #' # To use a stored token provide token location 52 | #' drop_auth(rdstoken = "/path/to/tokenfile.RDS") 53 | #' } 54 | drop_auth <- function(new_user = FALSE, 55 | key = "mmhfsybffdom42w", 56 | secret = "l8zeqqqgm1ne5z0", 57 | cache = TRUE, 58 | rdstoken = NA) { 59 | 60 | # check if token file exists & use it 61 | if (new_user == FALSE & !is.na(rdstoken)) { 62 | 63 | # read token or error 64 | if (file.exists(rdstoken)) { 65 | .dstate$token <- readRDS(rdstoken) 66 | } else { 67 | stop("token file not found") 68 | } 69 | 70 | # authenticate normally 71 | } else { 72 | 73 | # remove any cached token if new user 74 | if (new_user && file.exists(".httr-oauth")) { 75 | message("Removing old credentials...") 76 | file.remove(".httr-oauth") 77 | } 78 | 79 | # set dropbox oauth2 endpoints 80 | dropbox <- httr::oauth_endpoint( 81 | authorize = "https://www.dropbox.com/oauth2/authorize", 82 | access = "https://api.dropbox.com/oauth2/token" 83 | ) 84 | 85 | # registered dropbox app's key & secret 86 | dropbox_app <- httr::oauth_app("dropbox", key, secret) 87 | 88 | # get the token 89 | dropbox_token <- httr::oauth2.0_token(dropbox, dropbox_app, cache = cache) 90 | 91 | # make sure we got a token 92 | if (!inherits(dropbox_token, "Token2.0")) { 93 | stop("something went wrong, try again") 94 | } 95 | 96 | # cache token in rdrop2 namespace 97 | .dstate$token <- dropbox_token 98 | } 99 | } 100 | 101 | 102 | 103 | #' Retrieve oauth2 token from rdrop2-namespaced environment 104 | #' 105 | #' Retrieves a token if it is previously stored, otherwise prompts user to get one. 106 | #' 107 | #' @keywords internal 108 | get_dropbox_token <- function() { 109 | 110 | if (!exists('.dstate') || is.null(.dstate$token)) { 111 | drop_auth() 112 | } else { 113 | .dstate$token 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /R/drop_content_hash.R: -------------------------------------------------------------------------------- 1 | #' Compute a "content hash" using the same algorithm as dropbox. This 2 | #' can be used to verify the content against the \code{content_hash} 3 | #' field returned in \code{\link{drop_dir}}. 4 | #' 5 | #' Dropbox returns a hash of file contents in \code{\link{drop_dir}}. 6 | #' However, this is not a straightforward file hash. Instead the file 7 | #' is divided into 4MB chunks, each of those is hashed and then the 8 | #' concatenation of the hashes is itself hashed (see 9 | #' \href{https://www.dropbox.com/developers/reference/content-hash}{this 10 | #' page} in the dropbox developer documentation for the details). 11 | #' It's entirely unclear why it does not compute a hash of the file 12 | #' itself, but here we are. 13 | #' 14 | #' @title Compute Dropbox's content hash for one or more files 15 | #' 16 | #' @param file A vector of filenames 17 | #' 18 | #' @return A character vector the same length as \code{file}. Each 19 | #' element is 64 character string which is the unique hash. Two 20 | #' files that have the same hash have the same contents. Compare 21 | #' this hash of a local file with the \code{content_hash} field from 22 | #' \code{\link{drop_dir}} to see if you have the same file as 23 | #' dropbox. 24 | #' 25 | #' @export 26 | #' @examples 27 | #' \dontrun{ 28 | #' write.csv(mtcars, file = "mtt.csv") 29 | #' drop_upload("mtt.csv") 30 | #' files <- drop_dir() 31 | #' # Dropbox's reported hash 32 | #' files$content_hash[files$name == "mtt.csv"] 33 | #' # Our computed hash: 34 | #' drop_content_hash("mtt.csv") 35 | #' } 36 | drop_content_hash <- function(file) { 37 | if (!is.character(file)) { 38 | stop("Expected 'file' to be a character vector") 39 | } 40 | if (length(file) != 1L) { 41 | return(vapply(file, drop_content_hash, character(1), USE.NAMES = FALSE)) 42 | } 43 | 44 | con <- file(file, "rb") 45 | on.exit(close(con)) 46 | 47 | block_size <- 4L * 1024L * 1024L 48 | 49 | n <- ceiling(file.size(file) / block_size) 50 | h <- vector("list", n) 51 | for (i in seq_len(n)) { 52 | bytes <- readBin(con, raw(1), block_size) 53 | h[[i]] <- sha256(bytes, raw = TRUE) 54 | } 55 | 56 | sha256(unlist(h)) 57 | } 58 | 59 | sha256 <- function(bytes, raw = FALSE) { 60 | digest::digest(bytes, algo = "sha256", serialize = FALSE, raw = raw) 61 | } 62 | -------------------------------------------------------------------------------- /R/drop_dir.R: -------------------------------------------------------------------------------- 1 | #' List folder contents and associated metadata. 2 | #' 3 | #' Can be used to either see all items in a folder, or only items that have changed since a previous request was made. 4 | #' 5 | #' @param path path to folder in Dropbox to list contents of. Defaults to the root directory. 6 | #' @param recursive If TRUE, the list folder operation will be applied recursively to all subfolders and the response will contain contents of all subfolders. Defaults to FALSE. 7 | #' @param include_media_info If TRUE, FileMetadata.media_info is set for photo and video. Defaults to FALSE. 8 | #' @param include_deleted If TRUE, the results will include entries for files and folders that used to exist but were deleted. Defaults to FALSE. 9 | #' @param include_has_explicit_shared_members If TRUE, the results will include a flag for each file indicating whether or not that file has any explicit members. Defaults to FALSE. 10 | #' @param include_mounted_folders If TRUE, the results will include entries under mounted folders which includes app folder, shared folder and team folder. Defaults to TRUE. 11 | #' @param limit The maximum number of results to return per request. Note: This is an approximate number and there can be slightly more entries returned in some cases. Defaults to NULL, no limit. 12 | #' @param cursor string or boolean: \itemize{ 13 | #' \item{If FALSE, return metadata of items in \code{path}} 14 | #' \item{If TRUE, return a cursor to be used for detecting changed later} 15 | #' \item{If a string, return metadata of changed items since the cursor was fetched} 16 | #' } 17 | #' @template token 18 | #' 19 | #' @return Either a \code{tbl_df} of items in folder, one row per file or folder, with metadata values as columns, or a character string giving a cursor to be used later for change detection (see \code{cursor}). 20 | #' 21 | #' @examples \dontrun{ 22 | #' 23 | #' # list files in root directory 24 | #' drop_dir() 25 | #' 26 | #' # get a cursor from root directory, 27 | #' # upload a new file, 28 | #' # return only information about new file 29 | #' cursor <- drop_dir(cursor = TRUE) 30 | #' drop_upload("some_new_file") 31 | #' drop_dir(cursor = cursor) 32 | #' } 33 | #' 34 | #' @export 35 | drop_dir <- function( 36 | path = "", 37 | recursive = FALSE, 38 | include_media_info = FALSE, 39 | include_deleted = FALSE, 40 | include_has_explicit_shared_members = FALSE, 41 | include_mounted_folders = TRUE, 42 | limit = NULL, 43 | cursor = FALSE, 44 | dtoken = get_dropbox_token() 45 | ) { 46 | 47 | # check args 48 | #assertive::assert_is_a_string(path) 49 | #if (!is.null(limit)) assertive::assert_is_numeric(limit) 50 | #assertive::assert_is_any_of(cursor, c("logical", "character")) 51 | assertthat::assert_that(is.character(path), 52 | is.null(limit) || is.numeric(limit), 53 | class(cursor) %in% c("logical", "character")) 54 | 55 | # this API doesn't accept "/", so don't add slashes to empty path, remove if given 56 | if (path != "") path <- add_slashes(path) 57 | if (path == "/") path <- "" 58 | 59 | # force limit to integer 60 | if (!is.null(limit)) limit <- as.integer(limit) 61 | 62 | # behavior depends on cursor 63 | if (is.character(cursor)) { 64 | 65 | # list changes since cursor 66 | content <- drop_list_folder_continue(cursor, dtoken) 67 | 68 | } else if (cursor) { 69 | 70 | # get a cursor to track changes against 71 | content <- drop_list_folder_get_latest_cursor( 72 | path, 73 | recursive, 74 | include_media_info, 75 | include_deleted, 76 | include_has_explicit_shared_members, 77 | include_mounted_folders, 78 | limit, 79 | dtoken 80 | ) 81 | return(content$cursor) 82 | 83 | } else { 84 | 85 | # list files normally 86 | content <- drop_list_folder( 87 | path, 88 | recursive, 89 | include_media_info, 90 | include_deleted, 91 | include_has_explicit_shared_members, 92 | include_mounted_folders, 93 | limit, 94 | dtoken 95 | ) 96 | 97 | } 98 | 99 | # extract list of content metadata 100 | results <- content$entries 101 | 102 | # if no limit was given, make additional requests until all content retrieved 103 | if (is.null(limit)) { 104 | while (content$has_more) { 105 | 106 | # update content, append results 107 | content <- drop_list_folder_continue(content$cursor) 108 | results <- append(results, content$entries) 109 | } 110 | } 111 | 112 | # coerce to tibble, one row per item found 113 | dplyr::bind_rows(purrr::map(results, LinearizeNestedList)) 114 | } 115 | 116 | 117 | #' List contents of a Dropbox folder. 118 | #' 119 | #' For internal use; drop_dir should generally be used to list files in a folder. 120 | #' 121 | #' @return a list with three elements: \code{entries}, \code{cursor}, and \code{has_more}. 122 | #' 123 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder}{API reference} 124 | #' 125 | #' @noRd 126 | #' 127 | #' @keywords internal 128 | drop_list_folder <- function( 129 | path, 130 | recursive = FALSE, 131 | include_media_info = FALSE, 132 | include_deleted = FALSE, 133 | include_has_explicit_shared_members = FALSE, 134 | include_mounted_folders = TRUE, 135 | limit = NULL, 136 | dtoken = get_dropbox_token() 137 | ) { 138 | 139 | url <- "https://api.dropboxapi.com/2/files/list_folder" 140 | 141 | req <- httr::POST( 142 | url = url, 143 | httr::config(token = dtoken), 144 | body = drop_compact(list( 145 | path = path, 146 | recursive = recursive, 147 | include_media_info = include_media_info, 148 | include_deleted = include_deleted, 149 | include_has_explicit_shared_members = include_has_explicit_shared_members, 150 | include_mounted_folders = include_mounted_folders, 151 | limit = limit 152 | )), 153 | encode = "json" 154 | ) 155 | 156 | httr::stop_for_status(req) 157 | 158 | httr::content(req) 159 | } 160 | 161 | 162 | #' Fetch additional results from a cursor 163 | #' 164 | #' @return see \code{drop_list_folder} 165 | #' 166 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue}{Dropbox API} 167 | #' 168 | #' @noRd 169 | #' 170 | #' @keywords internal 171 | drop_list_folder_continue <- function(cursor, dtoken = get_dropbox_token()) { 172 | 173 | url <- "https://api.dropboxapi.com/2/files/list_folder/continue" 174 | 175 | req <- httr::POST( 176 | url = url, 177 | httr::config(token = dtoken), 178 | body = list(cursor = cursor), 179 | encode = "json" 180 | ) 181 | 182 | httr::stop_for_status(req) 183 | 184 | httr::content(req) 185 | } 186 | 187 | 188 | #' Get the current cursor for a set of path + options 189 | #' 190 | #' @return a cursor, a string uniquely identifying a folder and how much of it has been listed 191 | #' 192 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-get_latest_cursor}{Dropbox API} 193 | #' 194 | #' @noRd 195 | #' 196 | #' @keywords internal 197 | drop_list_folder_get_latest_cursor <- function( 198 | path, 199 | recursive = FALSE, 200 | include_media_info = FALSE, 201 | include_deleted = FALSE, 202 | include_has_explicit_shared_members = FALSE, 203 | include_mounted_folders = TRUE, 204 | limit = NULL, 205 | dtoken = get_dropbox_token() 206 | ) { 207 | 208 | url <- "https://api.dropboxapi.com/2/files/list_folder/get_latest_cursor" 209 | 210 | req <- httr::POST( 211 | url = url, 212 | httr::config(token = dtoken), 213 | body = drop_compact(list( 214 | path = path, 215 | recursive = recursive, 216 | include_media_info = include_media_info, 217 | include_deleted = include_deleted, 218 | include_has_explicit_shared_members = include_has_explicit_shared_members, 219 | include_mounted_folders = include_mounted_folders, 220 | limit = limit 221 | )), 222 | encode = "json" 223 | ) 224 | 225 | httr::stop_for_status(req) 226 | 227 | httr::content(req) 228 | } 229 | -------------------------------------------------------------------------------- /R/drop_download.R: -------------------------------------------------------------------------------- 1 | #' Download a file from Dropbox to disk. 2 | #' 3 | #' @param path path to a file in Dropbox 4 | #' @param local_path path to save file to. If NULL (the default), saves file to working directory with same name. If not null, but a valid folder, file will be saved in this folder with same basename as path. If not null and not a folder, file will be saved to this path exactly. 5 | #' @param overwrite If TRUE, overwrite local file. Defaults to FALSE 6 | #' @param progress If TRUE, show a progress bar for large file downloads. Defaults to TRUE in interactive sessions, otherwise FALSE. 7 | #' @param verbose if TRUE, emit message giving location and size of the newly downloaded file. Defaults to TRUE in interactive sessions, otherwise FALSE. 8 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-download}{API documentation} 9 | #' @template token 10 | #' 11 | #' @return TRUE if successful; error thrown otherwise. 12 | #' 13 | #' @examples \dontrun{ 14 | #' 15 | #' # download a file to the current working directory 16 | #' drop_get("dataset.zip") 17 | #' 18 | #' # download again, overwriting previous result 19 | #' drop_get("dataset.zip", overwrite = TRUE) 20 | #' 21 | #' # download to a different path, keeping file name constant 22 | #' # will download to "some/other/place/dataset.zip" 23 | #' drop_get("dataset.zip", local_path = "some/other/place/") 24 | #' 25 | #' # download to to a different path, changing filename 26 | #' drop_get("dataset.zip", local_path = "some/other/place/not_a_dataset.zip") 27 | #' } 28 | #' 29 | #' @export 30 | drop_download <- function( 31 | path, 32 | local_path = NULL, 33 | overwrite = FALSE, 34 | progress = interactive(), 35 | verbose = interactive(), 36 | dtoken = get_dropbox_token() 37 | ) { 38 | 39 | # if path isn't an id or revision, ensure it has leading slash 40 | if (!grepl("^(id|rev):", path)) path <- add_slashes(path) 41 | 42 | # if no local path given, download it to working directory 43 | # if path given is folder, append filename to it 44 | if (is.null(local_path)) { 45 | local_path = basename(path) 46 | } else if (dir.exists(local_path)) { 47 | local_path <- file.path(local_path, basename(path)) 48 | } 49 | 50 | url <- "https://content.dropboxapi.com/2/files/download" 51 | 52 | req <- httr::POST( 53 | url = url, 54 | httr::config(token = dtoken), 55 | httr::add_headers("Dropbox-API-Arg" = jsonlite::toJSON( 56 | list( 57 | path = path 58 | ), 59 | auto_unbox = TRUE 60 | )), 61 | if (progress) httr::progress(), 62 | httr::write_disk(local_path, overwrite) 63 | ) 64 | 65 | httr::stop_for_status(req) 66 | 67 | # print message in verbose mode 68 | if (verbose) { 69 | 70 | size <- file.size(local_path) 71 | class(size) <- "object_size" 72 | 73 | message(sprintf( 74 | "Downloaded %s to %s: %s on disk", 75 | path, 76 | local_path, 77 | format(size, units = "auto") 78 | )) 79 | } 80 | 81 | # must have been successful 82 | TRUE 83 | } 84 | 85 | 86 | #' Downloads a file from Dropbox 87 | #' 88 | #' @template path 89 | #' @param local_file The name of the local copy. Leave this blank if you're fine with the original name. 90 | #' @param overwrite Default is \code{FALSE} but can be set to \code{TRUE}. 91 | #' @param progress Progress bars are turned off by default. Set to \code{TRUE} ot turn this on. Progress is only reported when file sizes are known. Otherwise just bytes downloaded. 92 | #' @template token 93 | #' @template verbose 94 | #' 95 | #' @examples \dontrun{ 96 | #' drop_get(path = 'dataset.zip', local_file = "~/Desktop") 97 | #' # To overwrite the existing file 98 | #' drop_get(path = 'dataset.zip', overwrite = TRUE) 99 | #' } 100 | #' 101 | #' @export 102 | drop_get <- function( 103 | path = NULL, 104 | local_file = NULL, 105 | overwrite = FALSE, 106 | verbose = FALSE, 107 | progress = FALSE, 108 | dtoken = get_dropbox_token() 109 | ) { 110 | 111 | .Deprecated("drop_download") 112 | 113 | #assertive::assert_is_not_null(path) 114 | assertthat::assert_that(!is.null(path)) 115 | 116 | if (drop_exists(path, dtoken = dtoken)) { 117 | filename <- ifelse(is.null(local_file), basename(path), local_file) 118 | 119 | drop_download(path, filename, overwrite, progress, verbose, dtoken) 120 | 121 | if (!verbose) { 122 | # prints file sizes in kb but this could also be pretty printed 123 | message(sprintf("\n %s on disk %s KB", filename, file.size(filename)/1000)) 124 | TRUE 125 | } else { 126 | drop_get_metadata(path) 127 | } 128 | } else { 129 | message("File not found on Dropbox \n") 130 | FALSE 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /R/drop_file_ops.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #' Copies a file or folder to a new location. 4 | #' 5 | #' @template from_to 6 | #' @template verbose 7 | #' @template token 8 | #' @param allow_shared_folder If \code{TRUE}, copy will copy contents in shared 9 | #' folder 10 | #' @param autorename If there's a conflict, have the Dropbox server try to 11 | #' autorename the file to avoid the conflict. 12 | #' @param allow_ownership_transfer Allow moves by owner even if it would result 13 | #' in an ownership transfer for the content being moved. This does not apply 14 | #' to copies. The default for this field is False. 15 | #' @export 16 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-copy_v2}{API documentation} 17 | #' @examples \dontrun{ 18 | #' write.csv(mtcars, file = "mt.csv") 19 | #' drop_upload("mt.csv") 20 | #' drop_create("drop_test2") 21 | #' drop_copy("mt.csv", "drop_test2/mt2.csv") 22 | #' } 23 | drop_copy <- 24 | function(from_path = NULL, 25 | to_path = NULL, 26 | allow_shared_folder = FALSE, 27 | autorename = FALSE, 28 | allow_ownership_transfer = FALSE, 29 | verbose = FALSE, 30 | dtoken = get_dropbox_token()) { 31 | copy_url <- "https://api.dropboxapi.com/2/files/copy_v2" 32 | 33 | from_path <- add_slashes(from_path) 34 | to_path <- add_slashes(to_path) 35 | 36 | # Copying a file into a folder 37 | file_to_folder <- 38 | c(drop_type(from_path) == "file", 39 | drop_type(to_path) == "folder") 40 | to_path <- 41 | ifelse(all(file_to_folder), paste0(to_path, from_path), to_path) 42 | 43 | # coping a folder to another folder 44 | folder_to_folder <- 45 | c(drop_type(from_path) == "folder", 46 | drop_type(to_path) == "folder") 47 | to_path <- 48 | ifelse(all(folder_to_folder), paste0(to_path, from_path), to_path) 49 | 50 | # Copying a file to a file 51 | # Nothing to do, since both paths reflect origin and destination 52 | 53 | # Copying a folder to an existing filename will result in a HTTP 409 (conflict error) 54 | 55 | args <- drop_compact( 56 | list( 57 | from_path = from_path, 58 | to_path = to_path, 59 | allow_shared_folder = allow_shared_folder, 60 | autorename = autorename, 61 | allow_ownership_transfer = allow_ownership_transfer 62 | ) 63 | ) 64 | 65 | if (drop_exists(from_path)) { 66 | # copy 67 | x <- 68 | httr::POST(copy_url, 69 | httr::config(token = dtoken), 70 | body = args, 71 | encode = "json") 72 | res <- httr::content(x) 73 | if (!verbose) { 74 | message(sprintf("%s copied to %s", from_path, res$metadata$path_lower)) 75 | invisible(res) 76 | } else { 77 | pretty_lists(res) 78 | invisible(res) 79 | } 80 | } else { 81 | stop("File or folder not found \n") 82 | } 83 | } 84 | 85 | 86 | #'Moves a file or folder to a new location. 87 | #' 88 | #' @template from_to 89 | #' @template verbose 90 | #' @template token 91 | #' @param allow_shared_folder If \code{TRUE}, copy will copy contents in shared 92 | #' folder 93 | #' @param autorename If there's a conflict, have the Dropbox server try to 94 | #' autorename the file to avoid the conflict. 95 | #' @param allow_ownership_transfer Allow moves by owner even if it would result 96 | #' in an ownership transfer for the content being moved. This does not apply 97 | #' to copies. The default for this field is False. 98 | #' @export 99 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-move_v2}{API documentation} 100 | #' @examples \dontrun{ 101 | #' write.csv(mtcars, file = "mt.csv") 102 | #' drop_upload("mt.csv") 103 | #' drop_create("drop_test2") 104 | #' drop_move("mt.csv", "drop_test2/mt.csv") 105 | #' } 106 | drop_move <- 107 | function(from_path = NULL, 108 | to_path = NULL, 109 | allow_shared_folder = FALSE, 110 | autorename = FALSE, 111 | allow_ownership_transfer = FALSE, 112 | verbose = FALSE, 113 | dtoken = get_dropbox_token()) { 114 | move_url <- "https://api.dropboxapi.com/2/files/move_v2" 115 | 116 | from_path <- add_slashes(from_path) 117 | to_path <- add_slashes(to_path) 118 | 119 | # Moving a file into a folder 120 | file_to_folder <- 121 | c(drop_type(from_path) == "file", 122 | drop_type(to_path) == "folder") 123 | to_path <- 124 | ifelse(all(file_to_folder), paste0(to_path, from_path), to_path) 125 | 126 | # Moving a folder to another folder 127 | folder_to_folder <- 128 | c(drop_type(from_path) == "folder", 129 | drop_type(to_path) == "folder") 130 | to_path <- 131 | ifelse(all(folder_to_folder), paste0(to_path, from_path), to_path) 132 | 133 | # Moving a file to a file 134 | # Nothing to do, since both paths reflect origin and destination 135 | 136 | # Moving a folder to an existing filename will result in a HTTP 409 (conflict error) 137 | 138 | args <- drop_compact( 139 | list( 140 | from_path = from_path, 141 | to_path = to_path, 142 | allow_shared_folder = allow_shared_folder, 143 | autorename = autorename, 144 | allow_ownership_transfer = allow_ownership_transfer 145 | ) 146 | ) 147 | 148 | if (drop_exists(from_path)) { 149 | # move 150 | x <- 151 | httr::POST(move_url, 152 | httr::config(token = dtoken), 153 | body = args, 154 | encode = "json") 155 | res <- httr::content(x) 156 | 157 | if (!verbose) { 158 | message(sprintf("%s moved to %s", from_path, res$metadata$path_lower)) 159 | invisible(res) 160 | } else { 161 | pretty_lists(res) 162 | invisible(res) 163 | } 164 | } else { 165 | stop("File or folder not found \n") 166 | } 167 | } 168 | 169 | 170 | #'Deletes a file or folder. 171 | #' 172 | #' @template path 173 | #' @template verbose 174 | #' @template token 175 | #' @export 176 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-delete_v2}{API documentation} 177 | drop_delete <- 178 | function (path = NULL, 179 | verbose = FALSE, 180 | dtoken = get_dropbox_token()) { 181 | create_url <- "https://api.dropboxapi.com/2/files/delete_v2" 182 | if (drop_exists(path)) { 183 | path <- add_slashes(path) 184 | x <- 185 | httr::POST( 186 | create_url, 187 | httr::config(token = dtoken), 188 | body = list(path = path), 189 | encode = "json" 190 | ) 191 | res <- httr::content(x) 192 | 193 | if (verbose) { 194 | res 195 | } else { 196 | invisible(res) 197 | } 198 | } else { 199 | # Since file/folder wasn't found, report a stop error 200 | stop("File not found on current path") 201 | } 202 | } 203 | 204 | 205 | #'Creates a folder on Dropbox 206 | #' 207 | #'Returns a list containing the following fields: "size", "rev", "thumb_exists", 208 | #'"bytes", "modified", "path", "is_dir", "icon", "root", "revision" 209 | #'@template path 210 | #'@param autorename Set to \code{TRUE} to automatically rename. Default is FALSE. 211 | #'@template verbose 212 | #'@template token 213 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder_v2}{API documentation} 214 | #'@export 215 | #' @examples \dontrun{ 216 | #' drop_create(path = "foobar") 217 | #'} 218 | drop_create <- 219 | function(path = NULL, 220 | autorename = FALSE, 221 | verbose = FALSE, 222 | dtoken = get_dropbox_token()) { 223 | 224 | # if a folder exists, but autorename is TRUE, proceed 225 | # However, if a folder exists, and autorename if FALSE, fail in the else. 226 | if (!drop_exists(path) || autorename) { 227 | create_url <- "https://api.dropboxapi.com/2/files/create_folder_v2" 228 | 229 | path <- add_slashes(path) 230 | x <- 231 | httr::POST( 232 | create_url, 233 | config(token = dtoken), 234 | body = list(path = path, autorename = autorename), 235 | encode = "json" 236 | ) 237 | results <- httr::content(x) 238 | 239 | if (verbose) { 240 | pretty_lists(results) 241 | invisible(results) 242 | } else { 243 | message(sprintf("Folder %s created successfully \n", results$metadata$path_lower)) 244 | invisible(results) 245 | } 246 | 247 | invisible(results) 248 | } else { 249 | stop("Folder already exists") 250 | } 251 | } 252 | 253 | 254 | 255 | 256 | 257 | 258 | #' Checks to see if a file/folder exists on Dropbox 259 | #' 260 | #' Since many file operations such as move, copy, delete and history can only act 261 | #' on files that currently exist on a Dropbox store, checking to see if the 262 | #' \code{path} is valid before operating prevents bad API calls from being sent 263 | #' to the server. This functions returns a logical response after checking if a 264 | #' file path is valid on Dropbox. 265 | #' 266 | #' @param path The full path to a Dropbox file 267 | #' @template token 268 | #' 269 | #' @return boolean; TRUE is the file or folder exists, FALSE if it does not. 270 | #' 271 | #' @examples \dontrun{ 272 | #' drop_create("existential_test") 273 | #' drop_exists("existential_test") 274 | #' drop_delete("existential_test") 275 | #' } 276 | #' 277 | #' @export 278 | drop_exists <- function(path = NULL, dtoken = get_dropbox_token()) { 279 | #assertive::assert_is_not_null(path) 280 | assertthat::assert_that(!is.null(path)) 281 | 282 | if (!grepl('^/', path)) 283 | path <- paste0("/", path) 284 | dir_name <- suppressMessages(dirname(path)) 285 | # In issue #142, this part below (the drop_dir call) fails when drop_dir is 286 | # looking to see if a second level folder exists (when it doesn't.) One safe 287 | # option is to only run drop_dir('/', recursive = TRUE) and then grep through 288 | # that. Downside: It would take forever if this was a really large account. 289 | # 290 | # Other solution is to use purrr::safely to trap the error and return FALSE 291 | # (TODO): Explore uninteded consequence of this. 292 | safe_dir_check <- 293 | purrr::safely(drop_get_metadata, otherwise = FALSE, quiet = TRUE) 294 | dir_listing <- safe_dir_check(path = path, dtoken = dtoken) 295 | # browser() 296 | if (length(dir_listing$result) == 1) { 297 | # This means that object does not exist on Dropbox 298 | FALSE 299 | } else { 300 | # Root of path (dir_name), exists/ 301 | paths_only <- dir_listing$result$path_display 302 | 303 | if (path %in% paths_only) { 304 | TRUE 305 | } else { 306 | FALSE 307 | } 308 | } 309 | 310 | 311 | } 312 | 313 | #' Checks if an object is a file on Dropbox 314 | #' 315 | #' @noRd 316 | drop_is_file <- function(x, dtoken = get_dropbox_token()) { 317 | x <- drop_get_metadata(x) 318 | ifelse(x$.tag == "file", TRUE, FALSE) 319 | } 320 | 321 | #' Checks if an object is a folder on Dropbox 322 | #' 323 | #' @noRd 324 | drop_is_folder <- function(x, dtoken = get_dropbox_token()) { 325 | x <- drop_get_metadata(x) 326 | ifelse(x$.tag == "folder", TRUE, FALSE) 327 | } 328 | 329 | #' Checks on a name and returns file, folder, or FALSE for dropbox status 330 | #' @noRd 331 | drop_type <- function(x, dtoken = get_dropbox_token()) { 332 | safe_meta <- 333 | purrr::safely(drop_get_metadata, otherwise = FALSE, quiet = TRUE) 334 | x <- safe_meta(x) 335 | if (length(x$result) == 1 && !x$result) { 336 | FALSE 337 | } else { 338 | x$result$.tag 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /R/drop_get_metadata.R: -------------------------------------------------------------------------------- 1 | #' Retrieve metadata for a file or folder. 2 | #' 3 | #' Details vary by input and args. 4 | #' 5 | #' @param path Path to a file or folder on Dropbox. Can also be an ID ("id:...") or revision ("rev:..."). 6 | #' @param include_media_info If TRUE, additional metadata for photo or video is returns. Defaults to FALSE. 7 | #' @param include_deleted If TRUE, metadata will be returned for a deleted file, otherwise error. Defaults to FALSE. 8 | #' @param include_has_explicit_shared_members If TRUE, the results will include a flag for each file indicating whether or not that file has any explicit members. Defaults to FALSE. 9 | #' @template token 10 | #' 11 | #' @return possibly-nested list of all available metadata for specified file/folder/id/revision. 12 | #' 13 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata}{API Documentation} 14 | #' 15 | #' @export 16 | drop_get_metadata <- function( 17 | path, 18 | include_media_info = FALSE, 19 | include_deleted = FALSE, 20 | include_has_explicit_shared_members = FALSE, 21 | dtoken = get_dropbox_token() 22 | ) { 23 | 24 | url <- "https://api.dropboxapi.com/2/files/get_metadata" 25 | 26 | if (!grepl("^(id|rev):", path)) path <- add_slashes(path) 27 | 28 | req <- httr::POST( 29 | url = url, 30 | httr::config(token = dtoken), 31 | body = list( 32 | path = path, 33 | include_media_info = include_media_info, 34 | include_deleted = include_deleted, 35 | include_has_explicit_shared_members = include_has_explicit_shared_members 36 | ), 37 | encode = "json" 38 | ) 39 | 40 | httr::stop_for_status(req) 41 | 42 | httr::content(req) 43 | } 44 | -------------------------------------------------------------------------------- /R/drop_history.R: -------------------------------------------------------------------------------- 1 | #' Obtains metadata for all available revisions of a file, including the current 2 | #' revision. 3 | #' 4 | #' Does not include deleted revisions. 5 | #' 6 | #' @param path path to a file in dropbox. 7 | #' @param limit maximum number of revisions to return; defaults to 10. 8 | #' @template token 9 | #' 10 | #' @return \code{tbl_df} of metadata, one row per revision. 11 | #' 12 | #' @examples \dontrun{ 13 | #' write.csv(iris, file = "iris.csv") 14 | #' drop_upload("iris.csv") 15 | #' write.csv(iris[iris$Species == "setosa", ], file = "iris.csv") 16 | #' drop_upload("iris.csv") 17 | #' drop_history("iris.csv") 18 | #' } 19 | #' 20 | #' @export 21 | drop_history <- function(path, limit = 10, dtoken = get_dropbox_token()) { 22 | 23 | content <- drop_list_revisions(path, limit, dtoken) 24 | 25 | dplyr::bind_rows(content$entries) 26 | } 27 | 28 | 29 | #' Get revision history of a file 30 | #' 31 | #' Does not include deletions. 32 | #' 33 | #' @param path path to a file in Dropbox. 34 | #' @param limit maximum number of revisions to return; defaults to 10. 35 | #' @template token 36 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-list_revisions}{API documentation} 37 | #' 38 | #' @return list with elements \itemize{ 39 | #' \item{\code{is_deleted}}{logical; has the file been deleted?} 40 | #' \item{\code{entries}}{list of metadata lists, one per revisions} 41 | #' \item{\code{server_deleted}}{} 42 | #' } 43 | #' 44 | #' @noRd 45 | #' 46 | #' @keywords internal 47 | drop_list_revisions <- function(path, limit = 10, dtoken = get_dropbox_token()) { 48 | 49 | url <- "https://api.dropboxapi.com/2/files/list_revisions" 50 | 51 | req <- httr::POST( 52 | url = url, 53 | httr::config(token = dtoken), 54 | body = list( 55 | path = add_slashes(path), 56 | limit = limit 57 | ), 58 | encode = "json" 59 | ) 60 | 61 | httr::stop_for_status(req) 62 | 63 | httr::content(req) 64 | } 65 | -------------------------------------------------------------------------------- /R/drop_media.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #'Returns a link directly to a file. 4 | #' 5 | #'Similar to \code{drop_shared}. The difference is that this bypasses the 6 | #'Dropbox webserver, used to provide a preview of the file, so that you can 7 | #'effectively stream the contents of your media. This URL should not be used to 8 | #'display content directly in the browser. IMPORTANT: The media link will expire 9 | #' after 4 hours. So you'll need to cache the content with knitr cache OR re-run 10 | #' the function call after expiry. 11 | #'@template path 12 | #' @template token 13 | #'@export 14 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link}{API documentation} 15 | #' @examples \dontrun{ 16 | #' drop_media('Public/gifs/duck_rabbit.gif') 17 | #'} 18 | drop_media <- function(path = NULL, dtoken = get_dropbox_token()) { 19 | # assertive::assert_is_not_null(path) 20 | assertthat::assert_that(!is.null(path)) 21 | 22 | if(drop_exists(path)) { 23 | media_url <- "https://api.dropbox.com/2/files/get_temporary_link" 24 | path <- add_slashes(path) 25 | res <- POST(media_url, body = list(path = path), httr::config(token = dtoken), encode = "json") 26 | content(res) 27 | } else { 28 | stop("File not found \n") 29 | FALSE 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /R/drop_read_csv.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #' drop_read_csv 4 | #' 5 | #' A lightweight wrapper around \code{read.csv} to read csv files from Dropbox into memory 6 | #' @param file Name of file with full path relative to Dropbox root 7 | #' @param dest A temporary directory where a csv file is downloaded before being read into memory 8 | #' @param ... Additional arguments into \code{read.csv} 9 | #' @template token 10 | #' @export 11 | #' @examples \dontrun{ 12 | #' write.csv(iris, file = "iris.csv") 13 | #' drop_upload("iris.csv") 14 | #' # Now let's read this back into an R session 15 | #' new_iris <- drop_read_csv("iris.csv") 16 | #'} 17 | drop_read_csv <- function(file, dest = tempdir(), dtoken = get_dropbox_token(), ...) { 18 | localfile = paste0(dest, "/", basename(file)) 19 | drop_download(file, localfile, overwrite = TRUE, dtoken = dtoken) 20 | utils::read.csv(localfile, ...) 21 | } 22 | -------------------------------------------------------------------------------- /R/drop_search.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #'Returns metadata for all files and folders whose filename contains the given 5 | #'search string as a substring. 6 | #' 7 | #'@param query The search string. This string is split (on spaces) into 8 | #' individual words. Files and folders will be returned if they contain all 9 | #' words in the search string. 10 | #'@template path 11 | #'@param start The starting index within the search results (used for paging). 12 | #' The default for this field is 0 13 | #'@param max_results The maximum number of search results to return. The default 14 | #' for this field is 100. 15 | #'@param mode Mode can take the option of filename, filename_and_content, or search deleted files with deleted_filename 16 | #'@template token 17 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-search}{API documentation} 18 | #'@export 19 | #' @examples \dontrun{ 20 | #' # If you know me, you know why this query exists 21 | #' drop_search('gif') %>% select(path, is_dir, mime_type) 22 | #'} 23 | drop_search <- function(query, 24 | path = "", 25 | start = 0, 26 | max_results = 100, 27 | mode = "filename", 28 | dtoken = get_dropbox_token()) { 29 | available_modes <- 30 | c("filename", "filename_and_content", "deleted_filename") 31 | # assertive::assert_any_are_matching_fixed(available_modes, mode) 32 | assertthat::assert_that(mode %in% available_modes) 33 | 34 | # A search cannot have a negative start index and a negative max_results 35 | #assertive::assert_all_are_non_negative(start, max_results) 36 | assertthat::assert_that(start >= 0, 37 | max_results >= 0) 38 | 39 | args <- drop_compact( 40 | list( 41 | query = query, 42 | path = path, 43 | start = as.integer(start), 44 | max_results = as.integer(max_results), 45 | mode = mode 46 | ) 47 | ) 48 | 49 | search_url <- "https://api.dropboxapi.com/2/files/search" 50 | res <- 51 | httr::POST(search_url, 52 | body = args, 53 | httr::config(token = dtoken), 54 | encode = "json") 55 | httr::stop_for_status(res) 56 | httr::content(res) 57 | # TODO 58 | # Need to do a verbose return but also print a nice data.frame 59 | # One way to do that is with purrr::flatten 60 | # e.g. purrr::flatten(results$matches) 61 | # But, do we want purrr as another import??? 62 | } 63 | -------------------------------------------------------------------------------- /R/drop_shared.R: -------------------------------------------------------------------------------- 1 | #' Creates and returns a shared link to a file or folder. 2 | #' 3 | #' @template path 4 | #' @param requested_visibility Can be `public`, `team_only`, or `password`. If the 5 | #' password option is chosen one must specify the `link_password`. Note that 6 | #' for basic (i.e. free) Dropbox accounts, the only option is to publicly 7 | #' share. Private sharing requires a pro account. 8 | #' @param link_password The password needed to access the document if 9 | #' `request_visibility` is set to password. 10 | #' @param expires Set the expiry time. The timestamp format is 11 | #' "\%Y-\%m-\%dT\%H:\%M:\%SZ"). If no timestamp is specified, link never expires 12 | #' @template token 13 | #' @export 14 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#sharing-create_shared_link_with_settings}{API documentation} 15 | #' @examples \dontrun{ 16 | #' write.csv(mtcars, file = "mt.csv") 17 | #' drop_upload("mt.csv") 18 | #' drop_share("mt.csv") 19 | #' # If you have a pro account, you can share files privately 20 | #' drop_share("mt.csv", requested_visibility = "password", link_password = "test") 21 | #'} 22 | drop_share <- function(path = NULL, 23 | requested_visibility = "public", 24 | link_password = NULL, 25 | expires = NULL, 26 | dtoken = get_dropbox_token()) { 27 | # This is a list because in the POST call it becomes a nested JSON. 28 | # A sample response looks like this: 29 | 30 | # curl -X POST https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings \ 31 | # --header "Authorization: Bearer " \ 32 | # --header "Content-Type: application/json" \ 33 | # --data "{\"path\": \"/Prime_Numbers.txt\",\"settings\": {\"requested_visibility\": \"public\"} 34 | 35 | # Check to see if only supported modes are specified 36 | visibilities <- c("public", "team_only", "password") 37 | # assertive::assert_any_are_matching_fixed(visibilities, requested_visibility) 38 | assertthat::assert_that(requested_visibility %in% visibilities) 39 | 40 | # TODO 41 | # Once the new drop_exists is done, one must check to see if a file/folder 42 | # exists on Dropbox before proceeding 43 | 44 | path <- add_slashes(path) 45 | settings <- 46 | drop_compact( 47 | list( 48 | requested_visibility = requested_visibility, 49 | link_password = link_password, 50 | expires = expires 51 | ) 52 | ) 53 | # TODO: Check to see if this is necessary when we have encode to json below 54 | share_url <- 55 | "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings" 56 | 57 | req <- httr::POST( 58 | url = share_url, 59 | httr::config(token = dtoken), 60 | body = list(path = path, settings = settings), 61 | encode = "json" 62 | ) 63 | # stopping for status otherwise content fails 64 | httr::stop_for_status(req) 65 | response <- httr::content(req) 66 | response 67 | } 68 | 69 | #' List all shared links 70 | #' 71 | #' This function returns a list of all links that are currently being shared 72 | #' @template token 73 | #' @param verbose Print verbose output 74 | #' 75 | #' @export 76 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#sharing-list_shared_links}{API documentation} 77 | #' 78 | #' @examples \dontrun{ 79 | #' drop_list_shared_links() 80 | #' } 81 | drop_list_shared_links <- 82 | function(verbose = TRUE, dtoken = get_dropbox_token()) { 83 | shared_links_url <- 84 | "https://api.dropboxapi.com/2/sharing/list_shared_links" 85 | res <- 86 | httr::POST(shared_links_url, httr::config(token = dtoken), encode = "json") 87 | httr::stop_for_status(res) 88 | z <- httr::content(res) 89 | if (verbose) { 90 | invisible(z) 91 | pretty_lists(z) 92 | } else { 93 | invisible(z) 94 | # TODO 95 | # Clean up the verbose and non-verbose options 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /R/drop_upload.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #'Uploads a file to Dropbox. 7 | #' 8 | #'This function will allow you to write files of any size to Dropbox(even ones 9 | #'that cannot be read into memory) by uploading them in chunks. 10 | #' 11 | #'@param file Relative path to local file. 12 | #'@param path The relative path on Dropbox where the file should get uploaded. 13 | #'@param mode - "add" - will not overwrite an existing file in case of a 14 | #' conflict. With this mode, when a a duplicate file.txt is uploaded, it will 15 | #' become file (2).txt. - "overwrite" will always overwrite a file - 16 | #'@param autorename This logical determines what happens when there is a 17 | #' conflict. If true, the file being uploaded will be automatically renamed to 18 | #' avoid the conflict. (For example, test.txt might be automatically renamed to 19 | #' test (1).txt.) The new name can be obtained from the returned metadata. If 20 | #' false, the call will fail with a 409 (Conflict) response code. The default is `TRUE` 21 | #'@param mute Set to FALSE to prevent a notification trigger on the desktop and 22 | #' mobile apps 23 | #' @references \href{https://www.dropbox.com/developers/documentation/http/documentation#files-upload}{API documentation} 24 | #'@template verbose 25 | #'@template token 26 | #'@export 27 | #' @examples \dontrun{ 28 | #' write.csv(mtcars, file = "mtt.csv") 29 | #' drop_upload("mtt.csv") 30 | #'} 31 | drop_upload <- function(file, 32 | path = NULL, 33 | mode = "overwrite", 34 | autorename = TRUE, 35 | mute = FALSE, 36 | verbose = FALSE, 37 | dtoken = get_dropbox_token()) { 38 | put_url <- "https://content.dropboxapi.com/2/files/upload" 39 | 40 | # Check that object exists locally before adding slashes 41 | # assertive::assert_all_are_existing_files(file, severity = ("stop")) 42 | assertthat::assert_that(file.exists(file)) 43 | 44 | # Check to see if only supported modes are specified 45 | standard_modes <- c("overwrite", "add", "update") 46 | # assertive::assert_any_are_matching_fixed(standard_modes, mode) 47 | assertthat::assert_that(mode %in% standard_modes) 48 | 49 | # Dropbox API requires a / before an object name. 50 | if (is.null(path)) { 51 | path <- add_slashes(basename(file)) 52 | } else { 53 | path <- paste0("/", strip_slashes(path), "/", basename(file)) 54 | } 55 | 56 | req <- httr::POST( 57 | url = put_url, 58 | httr::config(token = dtoken), 59 | httr::add_headers("Dropbox-API-Arg" = jsonlite::toJSON( 60 | list( 61 | path = path, 62 | mode = mode, 63 | autorename = autorename, 64 | mute = mute 65 | ), 66 | auto_unbox = TRUE 67 | )), 68 | body = httr::upload_file(file, type = "application/octet-stream") 69 | # application/octet-stream is to save to a file to disk and not worry about 70 | # what application/function might handle it. This lets another application 71 | # figure out how to read it. So for this purpose we're totally ok. 72 | ) 73 | httr::stop_for_status(req) 74 | response <- httr::content(req) 75 | 76 | if (verbose) { 77 | pretty_lists(response) 78 | invisible(response) 79 | } else { 80 | invisible(response) 81 | message( 82 | sprintf( 83 | 'File %s uploaded as %s successfully at %s', 84 | file, 85 | response$path_display, 86 | response$server_modified 87 | ) 88 | ) 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /R/drop_utils.R: -------------------------------------------------------------------------------- 1 | #' Pipe operator 2 | #' 3 | #' @name %>% 4 | #' @rdname pipe 5 | #' @keywords internal 6 | #' @export 7 | #' @importFrom magrittr %>% 8 | #' @usage lhs \%>\% rhs 9 | NULL 10 | 11 | 12 | #' A local version of list compact from plyr. 13 | #' @noRd 14 | drop_compact <- function(l) Filter(Negate(is.null), l) 15 | 16 | 17 | #' A small function to strip trailing slashes from a path 18 | #' @noRd 19 | strip_slashes <- function(path) { 20 | if (length(path) && grepl("/$", path)) { 21 | path <- substr(path, 1, nchar(path) - 1) 22 | } 23 | # Also remove leading slashes 24 | if (length(path) && grepl("^/", path)) { 25 | path <- substr(path, 2, nchar(path)) 26 | } 27 | 28 | path 29 | } 30 | 31 | #' A small function to add a prefix slash 32 | #' @noRd 33 | add_slashes <- function(path) { 34 | if (length(path) && !grepl("^/", path)) { 35 | path <- paste0("/", path) 36 | } 37 | path 38 | } 39 | 40 | #' @noRd 41 | # This is an internal function to linearize lists 42 | # Source: https://gist.github.com/mrdwab/4205477 43 | # Author page (currently unreachable): https://sites.google.com/site/akhilsbehl/geekspace/articles/r/linearize_nested_lists_in 44 | # Original Author: Akhil S Bhel 45 | # Notes: Current author could not be reached and original site () appears defunct. Copyright remains with original author 46 | LinearizeNestedList <- function(NList, LinearizeDataFrames=FALSE, 47 | NameSep="/", ForceNames=FALSE) { 48 | # LinearizeNestedList: 49 | # 50 | # https://sites.google.com/site/akhilsbehl/geekspace/ 51 | # articles/r/linearize_nested_lists_in_r 52 | # 53 | # Akhil S Bhel 54 | # 55 | # Implements a recursive algorithm to linearize nested lists upto any 56 | # arbitrary level of nesting (limited by R's allowance for recursion-depth). 57 | # By linearization, it is meant to bring all list branches emanating from 58 | # any nth-nested trunk upto the top-level trunk s.t. the return value is a 59 | # simple non-nested list having all branches emanating from this top-level 60 | # branch. 61 | # 62 | # Since dataframes are essentially lists a boolean option is provided to 63 | # switch on/off the linearization of dataframes. This has been found 64 | # desirable in the author's experience. 65 | # 66 | # Also, one'd typically want to preserve names in the lists in a way as to 67 | # clearly denote the association of any list element to it's nth-level 68 | # history. As such we provide a clean and simple method of preserving names 69 | # information of list elements. The names at any level of nesting are 70 | # appended to the names of all preceding trunks using the `NameSep` option 71 | # string as the seperator. The default `/` has been chosen to mimic the unix 72 | # tradition of filesystem hierarchies. The default behavior works with 73 | # existing names at any n-th level trunk, if found; otherwise, coerces simple 74 | # numeric names corresponding to the position of a list element on the 75 | # nth-trunk. Note, however, that this naming pattern does not ensure unique 76 | # names for all elements in the resulting list. If the nested lists had 77 | # non-unique names in a trunk the same would be reflected in the final list. 78 | # Also, note that the function does not at all handle cases where `some` 79 | # names are missing and some are not. 80 | # 81 | # Clearly, preserving the n-level hierarchy of branches in the element names 82 | # may lead to names that are too long. Often, only the depth of a list 83 | # element may only be important. To deal with this possibility a boolean 84 | # option called `ForceNames` has been provided. ForceNames shall drop all 85 | # original names in the lists and coerce simple numeric names which simply 86 | # indicate the position of an element at the nth-level trunk as well as all 87 | # preceding trunk numbers. 88 | # 89 | # Returns: 90 | # LinearList: Named list. 91 | # 92 | # Sanity checks: 93 | # 94 | stopifnot(is.character(NameSep), length(NameSep) == 1) 95 | stopifnot(is.logical(LinearizeDataFrames), length(LinearizeDataFrames) == 1) 96 | stopifnot(is.logical(ForceNames), length(ForceNames) == 1) 97 | if (! is.list(NList)) return(NList) 98 | # 99 | # If no names on the top-level list coerce names. Recursion shall handle 100 | # naming at all levels. 101 | # 102 | if (is.null(names(NList)) | ForceNames == TRUE) 103 | names(NList) <- as.character(1:length(NList)) 104 | # 105 | # If simply a dataframe deal promptly. 106 | # 107 | if (is.data.frame(NList) & LinearizeDataFrames == FALSE) 108 | return(NList) 109 | if (is.data.frame(NList) & LinearizeDataFrames == TRUE) 110 | return(as.list(NList)) 111 | # 112 | # Book-keeping code to employ a while loop. 113 | # 114 | A <- 1 115 | B <- length(NList) 116 | # 117 | # We use a while loop to deal with the fact that the length of the nested 118 | # list grows dynamically in the process of linearization. 119 | # 120 | while (A <= B) { 121 | Element <- NList[[A]] 122 | EName <- names(NList)[A] 123 | if (is.list(Element)) { 124 | # 125 | # Before and After to keep track of the status of the top-level trunk 126 | # below and above the current element. 127 | # 128 | if (A == 1) { 129 | Before <- NULL 130 | } else { 131 | Before <- NList[1:(A - 1)] 132 | } 133 | if (A == B) { 134 | After <- NULL 135 | } else { 136 | After <- NList[(A + 1):B] 137 | } 138 | # 139 | # Treat dataframes specially. 140 | # 141 | if (is.data.frame(Element)) { 142 | if (LinearizeDataFrames == TRUE) { 143 | # 144 | # `Jump` takes care of how much the list shall grow in this step. 145 | # 146 | Jump <- length(Element) 147 | NList[[A]] <- NULL 148 | # 149 | # Generate or coerce names as need be. 150 | # 151 | if (is.null(names(Element)) | ForceNames == TRUE) 152 | names(Element) <- as.character(1:length(Element)) 153 | # 154 | # Just throw back as list since dataframes have no nesting. 155 | # 156 | Element <- as.list(Element) 157 | # 158 | # Update names 159 | # 160 | names(Element) <- paste(EName, names(Element), sep=NameSep) 161 | # 162 | # Plug the branch back into the top-level trunk. 163 | # 164 | NList <- c(Before, Element, After) 165 | } 166 | Jump <- 1 167 | } else { 168 | NList[[A]] <- NULL 169 | # 170 | # Go recursive! :) 171 | # 172 | if (is.null(names(Element)) | ForceNames == TRUE) 173 | names(Element) <- as.character(1:length(Element)) 174 | Element <- LinearizeNestedList(Element, LinearizeDataFrames, 175 | NameSep, ForceNames) 176 | names(Element) <- paste(EName, names(Element), sep=NameSep) 177 | Jump <- length(Element) 178 | NList <- c(Before, Element, After) 179 | } 180 | } else { 181 | Jump <- 1 182 | } 183 | # 184 | # Update book-keeping variables. 185 | # 186 | A <- A + Jump 187 | B <- length(NList) 188 | } 189 | return(NList) 190 | } 191 | 192 | #' A pretty list printer. Reduces extraneous space. 193 | #' @noRd 194 | pretty_lists <- function(x) 195 | { 196 | # assertive::assert_is_list(x) 197 | assertthat::assert_that(is.list(x)) 198 | 199 | for(key in names(x)){ 200 | value <- format(x[[key]]) 201 | if(value == "") next 202 | cat(key, "=", value, "\n") 203 | } 204 | invisible(x) 205 | } 206 | 207 | 208 | #' @noRd 209 | release_questions <- function() { 210 | c("For the love of God did I add skip_on_cran?") 211 | } 212 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rdrop2 - Dropbox interface from R ![a_box](docs/rdrop2-logo.png) 2 | 3 | 4 | 5 | # 🚨🚨🚨 Call for maintainers 🚨🚨🚨 6 | 7 | The package is currently not maintained and up for adoption. If you are interested in taking over as maintainer, please send an email to karthik.ram@gmail.com. 8 | 9 | If we can't find another maintainer before something breaks on CRAN, the package will be archived. 🙏 10 | 🚨🚨🚨 11 | 12 | 13 | --- 14 | 15 | 16 | [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/latest/active.svg)](http://www.repostatus.org/#active) 17 | [![Build Status](https://travis-ci.org/karthik/rdrop2.svg)](https://travis-ci.org/karthik/rdrop2) [![Coverage Status](https://coveralls.io./repos/karthik/rdrop2/badge.svg)](https://coveralls.io/r/karthik/rdrop2) [![](http://cranlogs.r-pkg.org/badges/rdrop2)](http://cran.rstudio.com/web/packages/rdrop2/index.html) [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/rdrop2)](http://cran.r-project.org/web/packages/rdrop2) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.998912.svg)](https://doi.org/10.5281/zenodo.998912) 18 | 19 | __Maintainers:__ Karthik Ram (@karthik) and Clayton Yochum (@ClaytonJY) 20 | 21 | 22 | This package provides programmatic access to Dropbox from R. The functions in this package provide access to a full suite of file operations, including dir/copy/move/delete operations, account information and the ability to upload and download files from any Dropbox account. 23 | 24 | 25 | ### Installation 26 | 27 | 28 | ``` 29 | # Current CRAN version (0.8.1) 30 | install.packages('rdrop2') 31 | 32 | # or the development version (0.8.1.9999) 33 | devtools::install_github("karthik/rdrop2") 34 | ``` 35 | 36 | ### Authentication 37 | 38 | ```r 39 | library(rdrop2) 40 | drop_auth() 41 | # This will launch your browser and request access to your Dropbox account. You will be prompted to log in if you aren't already logged in. 42 | # Once completed, close your browser window and return to R to complete authentication. 43 | # The credentials are automatically cached (you can prevent this) for future use. 44 | 45 | # If you wish to save the tokens, for local/remote use 46 | 47 | token <- drop_auth() 48 | saveRDS(token, file = "token.rds") 49 | 50 | # Then in any drop_* function, pass `dtoken = token 51 | # Tokens are valid until revoked. 52 | 53 | ``` 54 | 55 | #### Retrieve Dropbox account information 56 | 57 | ```r 58 | library(dplyr) 59 | drop_acc() %>% data.frame() 60 | # Returns the following fields 61 | # [1] "account_id" "name.given_name" 62 | # [3] "name.surname" "name.familiar_name" 63 | # [5] "name.display_name" "name.abbreviated_name" 64 | # [7] "email" "email_verified" 65 | # [9] "disabled" "country" 66 | # [11] "locale" "referral_link" 67 | # [13] "is_paired" ".tag" 68 | ``` 69 | 70 | #### Dropbox directory listing 71 | 72 | ```r 73 | write.csv(mtcars, "mtcars.csv") 74 | drop_upload("mtcars.csv") 75 | drop_dir() 76 | # If your account is not empty, it returns the following fields 77 | # [1] ".tag" "name" "path_lower" 78 | # [4] "path_display" "id" "client_modified" 79 | # [7] "server_modified" "rev" "size" 80 | # [10] "content_hash" 81 | # 82 | # 83 | # or specify a path 84 | drop_dir('public/gifs') 85 | ``` 86 | 87 | |.tag |name |path_lower |path_display |id |client_modified |server_modified |rev | size|content_hash | 88 | |:----|:----------|:-----------|:------------|:-------------------------|:--------------------|:--------------------|:------------|----:|:----------------------------------------------------------------| 89 | |file |mtcars.csv |/mtcars.csv |/mtcars.csv |id:b-ac9BwzYUAAAAAAAAAxFQ |2017-09-27T16:21:56Z |2017-09-27T16:21:57Z |691634207848 | 1783|8c00dcec5f3e6bf58a42dcf354f0d5199a43567e88a9d80291bd2b85f53a54a5 | 90 | 91 | #### Filter directory listing by object type (file/folder) 92 | 93 | ```r 94 | drop_dir() %>% 95 | filter(.tag == "folder") 96 | ``` 97 | 98 | #### Create folders on Dropbox 99 | 100 | 101 | ```r 102 | drop_create('drop_test') 103 | # or provide the full path where it needs to be created 104 | drop_create('public/drop_test') 105 | ``` 106 | 107 | #### Upload a file into Dropbox 108 | 109 | __csv files__ 110 | ```r 111 | write.csv(mtcars, 'mtcars.csv') 112 | drop_upload('mtcars.csv') 113 | # or upload to a specific folder 114 | drop_upload('mtcars.csv', path = "drop_test") 115 | ``` 116 | 117 | You can also do this for any other file type and large files are supported regardless of your memory. 118 | 119 | 120 | #### Download a file 121 | 122 | ```r 123 | drop_download('mtcars.csv') 124 | # or add path if file is not in root 125 | drop_download("test_folder/mtcars.csv") 126 | ``` 127 | 128 | #### Delete a file 129 | 130 | ```r 131 | drop_delete('mtcars.csv') 132 | ``` 133 | 134 | #### Move files 135 | 136 | ```r 137 | drop_create("new_folder") 138 | drop_move("mtcars.csv", "new_folder/mtcars.csv") 139 | ``` 140 | 141 | __Copy files__ 142 | 143 | ```r 144 | drop_create("new_folder2") 145 | drop_copy("new_folder/mtcars.csv", "new_folder2/mtcars.csv") 146 | ``` 147 | 148 | 149 | #### Search and download files 150 | 151 | I frequently use a duck season rabbit season gif. This is how I could search and download from my public Dropbox account. 152 | 153 | ```r 154 | x <- drop_search("rabbit") 155 | drop_download(x$matches[[1]]$metadata$path_lower, local_path = '~/Desktop/bugs.gif') 156 | 157 | # Downloaded /public/gifs/duck_rabbit.gif to ~/Desktop/bugs.gif: 329.2 Kb on disk 158 | ``` 159 | 160 | #### Read csv files directly from Dropbox 161 | 162 | ```r 163 | write.csv(iris, file = "iris.csv") 164 | drop_upload("iris.csv") 165 | # Now let's read this back into an R session 166 | # Note that there is a quiet download happening to your temp dir 167 | new_iris <- drop_read_csv("iris.csv") 168 | ``` 169 | 170 | #### Accessing Dropbox on Shiny and remote servers 171 | 172 | If you expect to access a Dropbox account via Shiny or on a remote cluster, EC2, Digital Ocean etc, you can leave the cached `oauth` file in the same directory, or pass the `token` explicitly to `drop_auth`. You can also save the output of `drop_auth` into an R object, sink that to disk, and pass that as a token. If using on Travis or similar, you should consider [encrypting the oauth cache file](http://docs.travis-ci.com/user/encrypting-files/) to prevent unauthorized access to your Dropbox account. If you have multiple tokens and accounts, it is also possible to override the environment token and explicitly pass a specific token for each `drop_` function. 173 | 174 | ```r 175 | token <- drop_auth() 176 | saveRDS(token, "droptoken.rds") 177 | # Upload droptoken to your server 178 | # ******** WARNING ******** 179 | # Losing this file will give anyone 180 | # complete control of your Dropbox account 181 | # You can then revoke the rdrop2 app from your 182 | # dropbox account and start over. 183 | # ******** WARNING ******** 184 | # read it back with readRDS 185 | token <- readRDS("droptoken.rds") 186 | # Then pass the token to each drop_ function 187 | drop_acc(dtoken = token) 188 | ``` 189 | 190 | 191 | __Meta__ 192 | 193 | * Please note that this project is released with a [Contributor Code of Conduct](CONDUCT.md). By participating in this project you agree to abide by its terms. 194 | 195 | * For bug reports and known problems, please look over the [issues](https://github.com/karthik/rdrop2/issues) before filing a new report. 196 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | Comments to CRAN maintainers: 2 | 3 | Submission on August 3 had outdated docs in the commit. Looks like it triggered this message: 4 | 5 | New submission 6 | 7 | Package was archived on CRAN 8 | 9 | Found the following (possibly) invalid URLs: 10 | URL: https://www.dropbox.com/developers/core/docs 11 | From: man/drop_auth.Rd 12 | Status: 404 13 | Message: Not Found 14 | 15 | This is fixed in the latest update. 16 | 17 | Package was archived because of assertive.reflection. Uwe Ligges wrote today saying that assertive is back and that we should bump version to get this back on CRAN. We are doing that in this version. We will remove assertive in the next CRAN update. 18 | 19 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who 4 | contribute through reporting issues, posting feature requests, updating documentation, 5 | submitting pull requests or patches, and other activities. 6 | 7 | We are committed to making participation in this project a harassment-free experience for 8 | everyone, regardless of level of experience, gender, gender identity and expression, 9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 10 | 11 | Examples of unacceptable behavior by participants include the use of sexual language or 12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment, 13 | insults, or other unprofessional conduct. 14 | 15 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed 18 | from the project team. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 21 | opening an issue or contacting one or more of the project maintainers. 22 | 23 | This Code of Conduct is adapted from the Contributor Covenant 24 | (http:contributor-covenant.org), version 1.0.0, available at 25 | http://contributor-covenant.org/version/1/0/0/ 26 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing to rdrop2 3 | 4 | 5 | * Before filing an issue, make sure it's not already on the queue or something listed for a future milestone. Please be sure to include package version (`packageVersion("rdrop2")` or your `devtools::session_info()` output) in your issue report. 6 | * If you have a feature update or fix to contribute, fork the repo and send a pull request with detailed enough notes so we can clearly understand what you have changed. 7 | 8 | ⚠ __IMPORTANT INFORMATION REGARDING TESTING__ ⚠ 9 | 10 | - Travis checks will _not_ pass on your pull request because the credentials are only decrypted on Travis for the maintainers (@karthik, @ClaytonJY). 11 | 12 | - Before sending a PR, make sure tests pass for you locally. To test: 13 | 14 | ```r 15 | # first authenticate into your own account 16 | token <- drop_auth(new_user = TRUE) 17 | saveRDS(token, "tests/testthat/token.rds") 18 | # Now run the tests as usual 19 | ``` 20 | 21 | Since `token.rds` is git ignored (as is `.httr-oauth`) your credentials will NOT end up on this repository. But you must have tests pass (and make a note of that) when you PR. A maintainer will checkout your request, make sure everything passes, and following comments, re-trigger Travis before merging. 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please start your issue with a brief description of the problem. 2 | 3 | Then, fill out as much as you can below, excluding parts you're not sure of or which don't apply. We'd rather know about an issue than not, so don't worry if you can't answer everything. 4 | 5 | The rest of this applies functional issues (code); remove it for other problems, like issues about documentation. 6 | 7 | Provide code to reproduce the issue. This will usually need to involve code to create and upload one or more files/folders. Below is an example you can edit. 8 | 9 | ```r 10 | library(rdrop2) 11 | 12 | drop_auth() 13 | 14 | # create & upload some file 15 | write.csv(mtcars, "mtcars.csv") 16 | drop_upload("mtcars.csv") 17 | 18 | # the problem call 19 | drop_something("mtcars.csv") 20 | ``` 21 | 22 | 23 | ## Expected Behaviour 24 | 25 | What did you expect to happen from the above code? 26 | 27 | 28 | ## Actual Behaviour 29 | 30 | What happened instead? 31 | 32 | 33 | ## Account Type 34 | 35 | Are you using Dropbox Basic, Dropbox Pro, or Dropbox for Business? 36 | 37 | 38 | ## Session Info 39 | 40 | Replace the output below (inside the backticks) with output from `devtools::session_info()` (or `sessionInfo()` if you don't have `devtools` installed) 41 | 42 |
43 | 
44 | ```r
45 | > devtools::session_info()
46 | Session info -----------------------------------------------------------------------
47 |  setting  value
48 |  version  R version 3.4.1 (2017-06-30)
49 |  system   x86_64, linux-gnu
50 |  ui       RStudio (1.0.153)
51 |  language (EN)
52 |  collate  en_US.UTF-8
53 |  tz       America/New_York
54 |  date     2017-10-01
55 | 
56 | Packages ---------------------------------------------------------------------------
57 |  package   * version date       source
58 |  base      * 3.4.2   2017-09-29 local
59 |  compiler    3.4.2   2017-09-29 local
60 |  datasets  * 3.4.2   2017-09-29 local
61 |  devtools    1.13.3  2017-08-02 CRAN (R 3.4.1)
62 |  digest      0.6.12  2017-01-27 CRAN (R 3.4.0)
63 |  graphics  * 3.4.2   2017-09-29 local
64 |  grDevices * 3.4.2   2017-09-29 local
65 |  memoise     1.1.0   2017-04-21 CRAN (R 3.4.0)
66 |  methods   * 3.4.2   2017-09-29 local
67 |  stats     * 3.4.2   2017-09-29 local
68 |  tools       3.4.2   2017-09-29 local
69 |  utils     * 3.4.2   2017-09-29 local
70 |  withr       2.0.0   2017-07-28 cran (@2.0.0)
71 | ```
72 | 
73 | 
74 | -------------------------------------------------------------------------------- /docs/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes #XYZ. 2 | 3 | Short description of changes and motivation. 4 | 5 | Details: 6 | - 7 | - 8 | - 9 | -------------------------------------------------------------------------------- /docs/rdrop2-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthik/rdrop2/81bedd2f0fbd0ee55dd163230c1a0b26a80162cc/docs/rdrop2-logo.png -------------------------------------------------------------------------------- /man-roxygen/file_limit.R: -------------------------------------------------------------------------------- 1 | #' @param file_limit Default is 10000. (max is 25000). When listing a folder, 2 | #' the service won't report listings containing more than the specified amount 3 | #' of files and will instead respond with a 406 (Not Acceptable) status 4 | #' response. 5 | -------------------------------------------------------------------------------- /man-roxygen/from_to.R: -------------------------------------------------------------------------------- 1 | #' @param from_path Source file or folder 2 | #' @param to_path destination file or folder 3 | -------------------------------------------------------------------------------- /man-roxygen/include_membership.R: -------------------------------------------------------------------------------- 1 | #' @param include_membership If true, metadata for a shared folder will 2 | #' include a list of members and a list of groups. 3 | -------------------------------------------------------------------------------- /man-roxygen/locale.R: -------------------------------------------------------------------------------- 1 | #' @param locale Dropbox uses the locale parameter to specify language settings 2 | #' of content responses. If your app supports any language other than English, 3 | #' insert the appropriate IETF language tag. When a supported language is 4 | #' specified, Dropbox will returned translated size and/or user_error fields 5 | #' (where applicable) 6 | 7 | -------------------------------------------------------------------------------- /man-roxygen/path.R: -------------------------------------------------------------------------------- 1 | #' @param path Path in the user's Dropbox, relative to root 2 | 3 | 4 | -------------------------------------------------------------------------------- /man-roxygen/token.R: -------------------------------------------------------------------------------- 1 | #' @param dtoken The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 2 | #' will try to automatically locate your local credential cache and use them. 3 | #' However, if the credentials are not found, the function will initiate a new 4 | #' authentication request. You can override this in \code{\link{drop_auth}} by 5 | #' pointing to a different location where your credentials are stored. 6 | 7 | -------------------------------------------------------------------------------- /man-roxygen/verbose.R: -------------------------------------------------------------------------------- 1 | #' @param verbose By default verbose output is \code{FALSE}. Set to \code{TRUE} 2 | #' if you need to troubleshoot any output or grab additional parameters. 3 | -------------------------------------------------------------------------------- /man/drop_acc.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_acc.R 3 | \name{drop_acc} 4 | \alias{drop_acc} 5 | \title{Get information about current Dropbox account.} 6 | \usage{ 7 | drop_acc(dtoken = get_dropbox_token()) 8 | } 9 | \arguments{ 10 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 11 | will try to automatically locate your local credential cache and use them. 12 | However, if the credentials are not found, the function will initiate a new 13 | authentication request. You can override this in \code{\link{drop_auth}} by 14 | pointing to a different location where your credentials are stored.} 15 | } 16 | \value{ 17 | Nested list with elements \code{account_id}, 18 | \code{name} (list), \code{email}, \code{email_verified}, \code{disabled}, 19 | \code{locale}, \code{referral_link}, \code{is_paired}, \code{account_type} 20 | (list). 21 | 22 | If available, may also return \code{profile_photo_url}, 23 | \code{country}, \code{team} (list), \code{team_member_id}. 24 | } 25 | \description{ 26 | Fields returned will vary by account; 27 | } 28 | \examples{ 29 | \dontrun{ 30 | 31 | acc_info <- drop_acc() 32 | 33 | # extract display name 34 | acc_info$name$display_name 35 | } 36 | } 37 | \references{ 38 | \href{https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account}{API documentation} 39 | } 40 | -------------------------------------------------------------------------------- /man/drop_auth.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_auth.R 3 | \name{drop_auth} 4 | \alias{drop_auth} 5 | \title{Authentication for Dropbox} 6 | \usage{ 7 | drop_auth( 8 | new_user = FALSE, 9 | key = "mmhfsybffdom42w", 10 | secret = "l8zeqqqgm1ne5z0", 11 | cache = TRUE, 12 | rdstoken = NA 13 | ) 14 | } 15 | \arguments{ 16 | \item{new_user}{Set to \code{TRUE} if you need to switch to a new user 17 | account or just flush existing token. Default is \code{FALSE}.} 18 | 19 | \item{key}{Your application key. \code{rdrop2} already comes with a key/secret but 20 | you are welcome to swap out with our own. Since these keys are shipped with 21 | the package, there is a small chance they could be voided if someone abuses 22 | the key. If you plan to use this in production, or for an internal tool, 23 | the recommended practice is to create a new application on Dropbox and use 24 | those keys for your purposes.} 25 | 26 | \item{secret}{Your application secret. Like \code{key}, \code{rdrop2} comes 27 | with a secret but you are welcome to swap out with our own.} 28 | 29 | \item{cache}{By default your credentials are locally cached in a file called 30 | \code{.httr-oauth}. Set to FALSE if you need to authenticate separately 31 | each time.} 32 | 33 | \item{rdstoken}{File path to stored RDS token. In server environments where 34 | interactive OAuth is not possible, a token can be created on a desktop 35 | client and used in production. See examples.} 36 | } 37 | \value{ 38 | A Token2.0 object, invisibly 39 | } 40 | \description{ 41 | This function authenticates you into Dropbox. The documentation for the 42 | \href{https://www.dropbox.com/developers/documentation?_tk=pilot_lp&_ad=topbar1&_camp=docs}{core Dropbox API} 43 | provides more details including alternate methods if you desire to 44 | reimplement your own. 45 | } 46 | \examples{ 47 | \dontrun{ 48 | 49 | # To either read token from .httr-oauth in the working directory or open a 50 | # web browser to authenticate (and cache a token) 51 | drop_auth() 52 | 53 | # If you want to overwrite an existing local token and switch to a new 54 | # user, set new_user to TRUE. 55 | drop_auth(new_user = TRUE) 56 | 57 | # To store a token for re-use (more flexible than .httr-oauth), save the 58 | # output of drop_auth and save it to an RDS file 59 | token <- drop_auth() 60 | saveRDS(token, "/path/to/tokenfile.RDS") 61 | 62 | # To use a stored token provide token location 63 | drop_auth(rdstoken = "/path/to/tokenfile.RDS") 64 | } 65 | } 66 | \references{ 67 | \href{https://www.dropbox.com/developers/documentation/http/documentation#authorization}{API documentation} 68 | } 69 | -------------------------------------------------------------------------------- /man/drop_content_hash.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_content_hash.R 3 | \name{drop_content_hash} 4 | \alias{drop_content_hash} 5 | \title{Compute Dropbox's content hash for one or more files} 6 | \usage{ 7 | drop_content_hash(file) 8 | } 9 | \arguments{ 10 | \item{file}{A vector of filenames} 11 | } 12 | \value{ 13 | A character vector the same length as \code{file}. Each 14 | element is 64 character string which is the unique hash. Two 15 | files that have the same hash have the same contents. Compare 16 | this hash of a local file with the \code{content_hash} field from 17 | \code{\link{drop_dir}} to see if you have the same file as 18 | dropbox. 19 | } 20 | \description{ 21 | Compute a "content hash" using the same algorithm as dropbox. This 22 | can be used to verify the content against the \code{content_hash} 23 | field returned in \code{\link{drop_dir}}. 24 | } 25 | \details{ 26 | Dropbox returns a hash of file contents in \code{\link{drop_dir}}. 27 | However, this is not a straightforward file hash. Instead the file 28 | is divided into 4MB chunks, each of those is hashed and then the 29 | concatenation of the hashes is itself hashed (see 30 | \href{https://www.dropbox.com/developers/reference/content-hash}{this 31 | page} in the dropbox developer documentation for the details). 32 | It's entirely unclear why it does not compute a hash of the file 33 | itself, but here we are. 34 | } 35 | \examples{ 36 | \dontrun{ 37 | write.csv(mtcars, file = "mtt.csv") 38 | drop_upload("mtt.csv") 39 | files <- drop_dir() 40 | # Dropbox's reported hash 41 | files$content_hash[files$name == "mtt.csv"] 42 | # Our computed hash: 43 | drop_content_hash("mtt.csv") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /man/drop_copy.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_file_ops.R 3 | \name{drop_copy} 4 | \alias{drop_copy} 5 | \title{Copies a file or folder to a new location.} 6 | \usage{ 7 | drop_copy( 8 | from_path = NULL, 9 | to_path = NULL, 10 | allow_shared_folder = FALSE, 11 | autorename = FALSE, 12 | allow_ownership_transfer = FALSE, 13 | verbose = FALSE, 14 | dtoken = get_dropbox_token() 15 | ) 16 | } 17 | \arguments{ 18 | \item{from_path}{Source file or folder} 19 | 20 | \item{to_path}{destination file or folder} 21 | 22 | \item{allow_shared_folder}{If \code{TRUE}, copy will copy contents in shared 23 | folder} 24 | 25 | \item{autorename}{If there's a conflict, have the Dropbox server try to 26 | autorename the file to avoid the conflict.} 27 | 28 | \item{allow_ownership_transfer}{Allow moves by owner even if it would result 29 | in an ownership transfer for the content being moved. This does not apply 30 | to copies. The default for this field is False.} 31 | 32 | \item{verbose}{By default verbose output is \code{FALSE}. Set to \code{TRUE} 33 | if you need to troubleshoot any output or grab additional parameters.} 34 | 35 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 36 | will try to automatically locate your local credential cache and use them. 37 | However, if the credentials are not found, the function will initiate a new 38 | authentication request. You can override this in \code{\link{drop_auth}} by 39 | pointing to a different location where your credentials are stored.} 40 | } 41 | \description{ 42 | Copies a file or folder to a new location. 43 | } 44 | \examples{ 45 | \dontrun{ 46 | write.csv(mtcars, file = "mt.csv") 47 | drop_upload("mt.csv") 48 | drop_create("drop_test2") 49 | drop_copy("mt.csv", "drop_test2/mt2.csv") 50 | } 51 | } 52 | \references{ 53 | \href{https://www.dropbox.com/developers/documentation/http/documentation#files-copy_v2}{API documentation} 54 | } 55 | -------------------------------------------------------------------------------- /man/drop_create.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_file_ops.R 3 | \name{drop_create} 4 | \alias{drop_create} 5 | \title{Creates a folder on Dropbox} 6 | \usage{ 7 | drop_create( 8 | path = NULL, 9 | autorename = FALSE, 10 | verbose = FALSE, 11 | dtoken = get_dropbox_token() 12 | ) 13 | } 14 | \arguments{ 15 | \item{path}{Path in the user's Dropbox, relative to root} 16 | 17 | \item{autorename}{Set to \code{TRUE} to automatically rename. Default is FALSE.} 18 | 19 | \item{verbose}{By default verbose output is \code{FALSE}. Set to \code{TRUE} 20 | if you need to troubleshoot any output or grab additional parameters.} 21 | 22 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 23 | will try to automatically locate your local credential cache and use them. 24 | However, if the credentials are not found, the function will initiate a new 25 | authentication request. You can override this in \code{\link{drop_auth}} by 26 | pointing to a different location where your credentials are stored.} 27 | } 28 | \description{ 29 | Returns a list containing the following fields: "size", "rev", "thumb_exists", 30 | "bytes", "modified", "path", "is_dir", "icon", "root", "revision" 31 | } 32 | \examples{ 33 | \dontrun{ 34 | drop_create(path = "foobar") 35 | } 36 | } 37 | \references{ 38 | \href{https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder_v2}{API documentation} 39 | } 40 | -------------------------------------------------------------------------------- /man/drop_delete.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_file_ops.R 3 | \name{drop_delete} 4 | \alias{drop_delete} 5 | \title{Deletes a file or folder.} 6 | \usage{ 7 | drop_delete(path = NULL, verbose = FALSE, dtoken = get_dropbox_token()) 8 | } 9 | \arguments{ 10 | \item{path}{Path in the user's Dropbox, relative to root} 11 | 12 | \item{verbose}{By default verbose output is \code{FALSE}. Set to \code{TRUE} 13 | if you need to troubleshoot any output or grab additional parameters.} 14 | 15 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 16 | will try to automatically locate your local credential cache and use them. 17 | However, if the credentials are not found, the function will initiate a new 18 | authentication request. You can override this in \code{\link{drop_auth}} by 19 | pointing to a different location where your credentials are stored.} 20 | } 21 | \description{ 22 | Deletes a file or folder. 23 | } 24 | \references{ 25 | \href{https://www.dropbox.com/developers/documentation/http/documentation#files-delete_v2}{API documentation} 26 | } 27 | -------------------------------------------------------------------------------- /man/drop_dir.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_dir.R 3 | \name{drop_dir} 4 | \alias{drop_dir} 5 | \title{List folder contents and associated metadata.} 6 | \usage{ 7 | drop_dir( 8 | path = "", 9 | recursive = FALSE, 10 | include_media_info = FALSE, 11 | include_deleted = FALSE, 12 | include_has_explicit_shared_members = FALSE, 13 | include_mounted_folders = TRUE, 14 | limit = NULL, 15 | cursor = FALSE, 16 | dtoken = get_dropbox_token() 17 | ) 18 | } 19 | \arguments{ 20 | \item{path}{path to folder in Dropbox to list contents of. Defaults to the root directory.} 21 | 22 | \item{recursive}{If TRUE, the list folder operation will be applied recursively to all subfolders and the response will contain contents of all subfolders. Defaults to FALSE.} 23 | 24 | \item{include_media_info}{If TRUE, FileMetadata.media_info is set for photo and video. Defaults to FALSE.} 25 | 26 | \item{include_deleted}{If TRUE, the results will include entries for files and folders that used to exist but were deleted. Defaults to FALSE.} 27 | 28 | \item{include_has_explicit_shared_members}{If TRUE, the results will include a flag for each file indicating whether or not that file has any explicit members. Defaults to FALSE.} 29 | 30 | \item{include_mounted_folders}{If TRUE, the results will include entries under mounted folders which includes app folder, shared folder and team folder. Defaults to TRUE.} 31 | 32 | \item{limit}{The maximum number of results to return per request. Note: This is an approximate number and there can be slightly more entries returned in some cases. Defaults to NULL, no limit.} 33 | 34 | \item{cursor}{string or boolean: \itemize{ 35 | \item{If FALSE, return metadata of items in \code{path}} 36 | \item{If TRUE, return a cursor to be used for detecting changed later} 37 | \item{If a string, return metadata of changed items since the cursor was fetched} 38 | }} 39 | 40 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 41 | will try to automatically locate your local credential cache and use them. 42 | However, if the credentials are not found, the function will initiate a new 43 | authentication request. You can override this in \code{\link{drop_auth}} by 44 | pointing to a different location where your credentials are stored.} 45 | } 46 | \value{ 47 | Either a \code{tbl_df} of items in folder, one row per file or folder, with metadata values as columns, or a character string giving a cursor to be used later for change detection (see \code{cursor}). 48 | } 49 | \description{ 50 | Can be used to either see all items in a folder, or only items that have changed since a previous request was made. 51 | } 52 | \examples{ 53 | \dontrun{ 54 | 55 | # list files in root directory 56 | drop_dir() 57 | 58 | # get a cursor from root directory, 59 | # upload a new file, 60 | # return only information about new file 61 | cursor <- drop_dir(cursor = TRUE) 62 | drop_upload("some_new_file") 63 | drop_dir(cursor = cursor) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /man/drop_download.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_download.R 3 | \name{drop_download} 4 | \alias{drop_download} 5 | \title{Download a file from Dropbox to disk.} 6 | \usage{ 7 | drop_download( 8 | path, 9 | local_path = NULL, 10 | overwrite = FALSE, 11 | progress = interactive(), 12 | verbose = interactive(), 13 | dtoken = get_dropbox_token() 14 | ) 15 | } 16 | \arguments{ 17 | \item{path}{path to a file in Dropbox} 18 | 19 | \item{local_path}{path to save file to. If NULL (the default), saves file to working directory with same name. If not null, but a valid folder, file will be saved in this folder with same basename as path. If not null and not a folder, file will be saved to this path exactly.} 20 | 21 | \item{overwrite}{If TRUE, overwrite local file. Defaults to FALSE} 22 | 23 | \item{progress}{If TRUE, show a progress bar for large file downloads. Defaults to TRUE in interactive sessions, otherwise FALSE.} 24 | 25 | \item{verbose}{if TRUE, emit message giving location and size of the newly downloaded file. Defaults to TRUE in interactive sessions, otherwise FALSE.} 26 | 27 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 28 | will try to automatically locate your local credential cache and use them. 29 | However, if the credentials are not found, the function will initiate a new 30 | authentication request. You can override this in \code{\link{drop_auth}} by 31 | pointing to a different location where your credentials are stored.} 32 | } 33 | \value{ 34 | TRUE if successful; error thrown otherwise. 35 | } 36 | \description{ 37 | Download a file from Dropbox to disk. 38 | } 39 | \examples{ 40 | \dontrun{ 41 | 42 | # download a file to the current working directory 43 | drop_get("dataset.zip") 44 | 45 | # download again, overwriting previous result 46 | drop_get("dataset.zip", overwrite = TRUE) 47 | 48 | # download to a different path, keeping file name constant 49 | # will download to "some/other/place/dataset.zip" 50 | drop_get("dataset.zip", local_path = "some/other/place/") 51 | 52 | # download to to a different path, changing filename 53 | drop_get("dataset.zip", local_path = "some/other/place/not_a_dataset.zip") 54 | } 55 | 56 | } 57 | \references{ 58 | \href{https://www.dropbox.com/developers/documentation/http/documentation#files-download}{API documentation} 59 | } 60 | -------------------------------------------------------------------------------- /man/drop_exists.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_file_ops.R 3 | \name{drop_exists} 4 | \alias{drop_exists} 5 | \title{Checks to see if a file/folder exists on Dropbox} 6 | \usage{ 7 | drop_exists(path = NULL, dtoken = get_dropbox_token()) 8 | } 9 | \arguments{ 10 | \item{path}{The full path to a Dropbox file} 11 | 12 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 13 | will try to automatically locate your local credential cache and use them. 14 | However, if the credentials are not found, the function will initiate a new 15 | authentication request. You can override this in \code{\link{drop_auth}} by 16 | pointing to a different location where your credentials are stored.} 17 | } 18 | \value{ 19 | boolean; TRUE is the file or folder exists, FALSE if it does not. 20 | } 21 | \description{ 22 | Since many file operations such as move, copy, delete and history can only act 23 | on files that currently exist on a Dropbox store, checking to see if the 24 | \code{path} is valid before operating prevents bad API calls from being sent 25 | to the server. This functions returns a logical response after checking if a 26 | file path is valid on Dropbox. 27 | } 28 | \examples{ 29 | \dontrun{ 30 | drop_create("existential_test") 31 | drop_exists("existential_test") 32 | drop_delete("existential_test") 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /man/drop_get.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_download.R 3 | \name{drop_get} 4 | \alias{drop_get} 5 | \title{Downloads a file from Dropbox} 6 | \usage{ 7 | drop_get( 8 | path = NULL, 9 | local_file = NULL, 10 | overwrite = FALSE, 11 | verbose = FALSE, 12 | progress = FALSE, 13 | dtoken = get_dropbox_token() 14 | ) 15 | } 16 | \arguments{ 17 | \item{path}{Path in the user's Dropbox, relative to root} 18 | 19 | \item{local_file}{The name of the local copy. Leave this blank if you're fine with the original name.} 20 | 21 | \item{overwrite}{Default is \code{FALSE} but can be set to \code{TRUE}.} 22 | 23 | \item{verbose}{By default verbose output is \code{FALSE}. Set to \code{TRUE} 24 | if you need to troubleshoot any output or grab additional parameters.} 25 | 26 | \item{progress}{Progress bars are turned off by default. Set to \code{TRUE} ot turn this on. Progress is only reported when file sizes are known. Otherwise just bytes downloaded.} 27 | 28 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 29 | will try to automatically locate your local credential cache and use them. 30 | However, if the credentials are not found, the function will initiate a new 31 | authentication request. You can override this in \code{\link{drop_auth}} by 32 | pointing to a different location where your credentials are stored.} 33 | } 34 | \description{ 35 | Downloads a file from Dropbox 36 | } 37 | \examples{ 38 | \dontrun{ 39 | drop_get(path = 'dataset.zip', local_file = "~/Desktop") 40 | # To overwrite the existing file 41 | drop_get(path = 'dataset.zip', overwrite = TRUE) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /man/drop_get_metadata.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_get_metadata.R 3 | \name{drop_get_metadata} 4 | \alias{drop_get_metadata} 5 | \title{Retrieve metadata for a file or folder.} 6 | \usage{ 7 | drop_get_metadata( 8 | path, 9 | include_media_info = FALSE, 10 | include_deleted = FALSE, 11 | include_has_explicit_shared_members = FALSE, 12 | dtoken = get_dropbox_token() 13 | ) 14 | } 15 | \arguments{ 16 | \item{path}{Path to a file or folder on Dropbox. Can also be an ID ("id:...") or revision ("rev:...").} 17 | 18 | \item{include_media_info}{If TRUE, additional metadata for photo or video is returns. Defaults to FALSE.} 19 | 20 | \item{include_deleted}{If TRUE, metadata will be returned for a deleted file, otherwise error. Defaults to FALSE.} 21 | 22 | \item{include_has_explicit_shared_members}{If TRUE, the results will include a flag for each file indicating whether or not that file has any explicit members. Defaults to FALSE.} 23 | 24 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 25 | will try to automatically locate your local credential cache and use them. 26 | However, if the credentials are not found, the function will initiate a new 27 | authentication request. You can override this in \code{\link{drop_auth}} by 28 | pointing to a different location where your credentials are stored.} 29 | } 30 | \value{ 31 | possibly-nested list of all available metadata for specified file/folder/id/revision. 32 | } 33 | \description{ 34 | Details vary by input and args. 35 | } 36 | \references{ 37 | \href{https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata}{API Documentation} 38 | } 39 | -------------------------------------------------------------------------------- /man/drop_history.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_history.R 3 | \name{drop_history} 4 | \alias{drop_history} 5 | \title{Obtains metadata for all available revisions of a file, including the current 6 | revision.} 7 | \usage{ 8 | drop_history(path, limit = 10, dtoken = get_dropbox_token()) 9 | } 10 | \arguments{ 11 | \item{path}{path to a file in dropbox.} 12 | 13 | \item{limit}{maximum number of revisions to return; defaults to 10.} 14 | 15 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 16 | will try to automatically locate your local credential cache and use them. 17 | However, if the credentials are not found, the function will initiate a new 18 | authentication request. You can override this in \code{\link{drop_auth}} by 19 | pointing to a different location where your credentials are stored.} 20 | } 21 | \value{ 22 | \code{tbl_df} of metadata, one row per revision. 23 | } 24 | \description{ 25 | Does not include deleted revisions. 26 | } 27 | \examples{ 28 | \dontrun{ 29 | write.csv(iris, file = "iris.csv") 30 | drop_upload("iris.csv") 31 | write.csv(iris[iris$Species == "setosa", ], file = "iris.csv") 32 | drop_upload("iris.csv") 33 | drop_history("iris.csv") 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /man/drop_list_shared_links.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_shared.R 3 | \name{drop_list_shared_links} 4 | \alias{drop_list_shared_links} 5 | \title{List all shared links} 6 | \usage{ 7 | drop_list_shared_links(verbose = TRUE, dtoken = get_dropbox_token()) 8 | } 9 | \arguments{ 10 | \item{verbose}{Print verbose output} 11 | 12 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 13 | will try to automatically locate your local credential cache and use them. 14 | However, if the credentials are not found, the function will initiate a new 15 | authentication request. You can override this in \code{\link{drop_auth}} by 16 | pointing to a different location where your credentials are stored.} 17 | } 18 | \description{ 19 | This function returns a list of all links that are currently being shared 20 | } 21 | \examples{ 22 | \dontrun{ 23 | drop_list_shared_links() 24 | } 25 | } 26 | \references{ 27 | \href{https://www.dropbox.com/developers/documentation/http/documentation#sharing-list_shared_links}{API documentation} 28 | } 29 | -------------------------------------------------------------------------------- /man/drop_media.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_media.R 3 | \name{drop_media} 4 | \alias{drop_media} 5 | \title{Returns a link directly to a file.} 6 | \usage{ 7 | drop_media(path = NULL, dtoken = get_dropbox_token()) 8 | } 9 | \arguments{ 10 | \item{path}{Path in the user's Dropbox, relative to root} 11 | 12 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 13 | will try to automatically locate your local credential cache and use them. 14 | However, if the credentials are not found, the function will initiate a new 15 | authentication request. You can override this in \code{\link{drop_auth}} by 16 | pointing to a different location where your credentials are stored.} 17 | } 18 | \description{ 19 | Similar to \code{drop_shared}. The difference is that this bypasses the 20 | Dropbox webserver, used to provide a preview of the file, so that you can 21 | effectively stream the contents of your media. This URL should not be used to 22 | display content directly in the browser. IMPORTANT: The media link will expire 23 | after 4 hours. So you'll need to cache the content with knitr cache OR re-run 24 | the function call after expiry. 25 | } 26 | \examples{ 27 | \dontrun{ 28 | drop_media('Public/gifs/duck_rabbit.gif') 29 | } 30 | } 31 | \references{ 32 | \href{https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link}{API documentation} 33 | } 34 | -------------------------------------------------------------------------------- /man/drop_move.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_file_ops.R 3 | \name{drop_move} 4 | \alias{drop_move} 5 | \title{Moves a file or folder to a new location.} 6 | \usage{ 7 | drop_move( 8 | from_path = NULL, 9 | to_path = NULL, 10 | allow_shared_folder = FALSE, 11 | autorename = FALSE, 12 | allow_ownership_transfer = FALSE, 13 | verbose = FALSE, 14 | dtoken = get_dropbox_token() 15 | ) 16 | } 17 | \arguments{ 18 | \item{from_path}{Source file or folder} 19 | 20 | \item{to_path}{destination file or folder} 21 | 22 | \item{allow_shared_folder}{If \code{TRUE}, copy will copy contents in shared 23 | folder} 24 | 25 | \item{autorename}{If there's a conflict, have the Dropbox server try to 26 | autorename the file to avoid the conflict.} 27 | 28 | \item{allow_ownership_transfer}{Allow moves by owner even if it would result 29 | in an ownership transfer for the content being moved. This does not apply 30 | to copies. The default for this field is False.} 31 | 32 | \item{verbose}{By default verbose output is \code{FALSE}. Set to \code{TRUE} 33 | if you need to troubleshoot any output or grab additional parameters.} 34 | 35 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 36 | will try to automatically locate your local credential cache and use them. 37 | However, if the credentials are not found, the function will initiate a new 38 | authentication request. You can override this in \code{\link{drop_auth}} by 39 | pointing to a different location where your credentials are stored.} 40 | } 41 | \description{ 42 | Moves a file or folder to a new location. 43 | } 44 | \examples{ 45 | \dontrun{ 46 | write.csv(mtcars, file = "mt.csv") 47 | drop_upload("mt.csv") 48 | drop_create("drop_test2") 49 | drop_move("mt.csv", "drop_test2/mt.csv") 50 | } 51 | } 52 | \references{ 53 | \href{https://www.dropbox.com/developers/documentation/http/documentation#files-move_v2}{API documentation} 54 | } 55 | -------------------------------------------------------------------------------- /man/drop_read_csv.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_read_csv.R 3 | \name{drop_read_csv} 4 | \alias{drop_read_csv} 5 | \title{drop_read_csv} 6 | \usage{ 7 | drop_read_csv(file, dest = tempdir(), dtoken = get_dropbox_token(), ...) 8 | } 9 | \arguments{ 10 | \item{file}{Name of file with full path relative to Dropbox root} 11 | 12 | \item{dest}{A temporary directory where a csv file is downloaded before being read into memory} 13 | 14 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 15 | will try to automatically locate your local credential cache and use them. 16 | However, if the credentials are not found, the function will initiate a new 17 | authentication request. You can override this in \code{\link{drop_auth}} by 18 | pointing to a different location where your credentials are stored.} 19 | 20 | \item{...}{Additional arguments into \code{read.csv}} 21 | } 22 | \description{ 23 | A lightweight wrapper around \code{read.csv} to read csv files from Dropbox into memory 24 | } 25 | \examples{ 26 | \dontrun{ 27 | write.csv(iris, file = "iris.csv") 28 | drop_upload("iris.csv") 29 | # Now let's read this back into an R session 30 | new_iris <- drop_read_csv("iris.csv") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /man/drop_search.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_search.R 3 | \name{drop_search} 4 | \alias{drop_search} 5 | \title{Returns metadata for all files and folders whose filename contains the given 6 | search string as a substring.} 7 | \usage{ 8 | drop_search( 9 | query, 10 | path = "", 11 | start = 0, 12 | max_results = 100, 13 | mode = "filename", 14 | dtoken = get_dropbox_token() 15 | ) 16 | } 17 | \arguments{ 18 | \item{query}{The search string. This string is split (on spaces) into 19 | individual words. Files and folders will be returned if they contain all 20 | words in the search string.} 21 | 22 | \item{path}{Path in the user's Dropbox, relative to root} 23 | 24 | \item{start}{The starting index within the search results (used for paging). 25 | The default for this field is 0} 26 | 27 | \item{max_results}{The maximum number of search results to return. The default 28 | for this field is 100.} 29 | 30 | \item{mode}{Mode can take the option of filename, filename_and_content, or search deleted files with deleted_filename} 31 | 32 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 33 | will try to automatically locate your local credential cache and use them. 34 | However, if the credentials are not found, the function will initiate a new 35 | authentication request. You can override this in \code{\link{drop_auth}} by 36 | pointing to a different location where your credentials are stored.} 37 | } 38 | \description{ 39 | Returns metadata for all files and folders whose filename contains the given 40 | search string as a substring. 41 | } 42 | \examples{ 43 | \dontrun{ 44 | # If you know me, you know why this query exists 45 | drop_search('gif') \%>\% select(path, is_dir, mime_type) 46 | } 47 | } 48 | \references{ 49 | \href{https://www.dropbox.com/developers/documentation/http/documentation#files-search}{API documentation} 50 | } 51 | -------------------------------------------------------------------------------- /man/drop_share.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_shared.R 3 | \name{drop_share} 4 | \alias{drop_share} 5 | \title{Creates and returns a shared link to a file or folder.} 6 | \usage{ 7 | drop_share( 8 | path = NULL, 9 | requested_visibility = "public", 10 | link_password = NULL, 11 | expires = NULL, 12 | dtoken = get_dropbox_token() 13 | ) 14 | } 15 | \arguments{ 16 | \item{path}{Path in the user's Dropbox, relative to root} 17 | 18 | \item{requested_visibility}{Can be `public`, `team_only`, or `password`. If the 19 | password option is chosen one must specify the `link_password`. Note that 20 | for basic (i.e. free) Dropbox accounts, the only option is to publicly 21 | share. Private sharing requires a pro account.} 22 | 23 | \item{link_password}{The password needed to access the document if 24 | `request_visibility` is set to password.} 25 | 26 | \item{expires}{Set the expiry time. The timestamp format is 27 | "\%Y-\%m-\%dT\%H:\%M:\%SZ"). If no timestamp is specified, link never expires} 28 | 29 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 30 | will try to automatically locate your local credential cache and use them. 31 | However, if the credentials are not found, the function will initiate a new 32 | authentication request. You can override this in \code{\link{drop_auth}} by 33 | pointing to a different location where your credentials are stored.} 34 | } 35 | \description{ 36 | Creates and returns a shared link to a file or folder. 37 | } 38 | \examples{ 39 | \dontrun{ 40 | write.csv(mtcars, file = "mt.csv") 41 | drop_upload("mt.csv") 42 | drop_share("mt.csv") 43 | # If you have a pro account, you can share files privately 44 | drop_share("mt.csv", requested_visibility = "password", link_password = "test") 45 | } 46 | } 47 | \references{ 48 | \href{https://www.dropbox.com/developers/documentation/http/documentation#sharing-create_shared_link_with_settings}{API documentation} 49 | } 50 | -------------------------------------------------------------------------------- /man/drop_upload.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_upload.R 3 | \name{drop_upload} 4 | \alias{drop_upload} 5 | \title{Uploads a file to Dropbox.} 6 | \usage{ 7 | drop_upload( 8 | file, 9 | path = NULL, 10 | mode = "overwrite", 11 | autorename = TRUE, 12 | mute = FALSE, 13 | verbose = FALSE, 14 | dtoken = get_dropbox_token() 15 | ) 16 | } 17 | \arguments{ 18 | \item{file}{Relative path to local file.} 19 | 20 | \item{path}{The relative path on Dropbox where the file should get uploaded.} 21 | 22 | \item{mode}{- "add" - will not overwrite an existing file in case of a 23 | conflict. With this mode, when a a duplicate file.txt is uploaded, it will 24 | become file (2).txt. - "overwrite" will always overwrite a file -} 25 | 26 | \item{autorename}{This logical determines what happens when there is a 27 | conflict. If true, the file being uploaded will be automatically renamed to 28 | avoid the conflict. (For example, test.txt might be automatically renamed to 29 | test (1).txt.) The new name can be obtained from the returned metadata. If 30 | false, the call will fail with a 409 (Conflict) response code. The default is `TRUE`} 31 | 32 | \item{mute}{Set to FALSE to prevent a notification trigger on the desktop and 33 | mobile apps} 34 | 35 | \item{verbose}{By default verbose output is \code{FALSE}. Set to \code{TRUE} 36 | if you need to troubleshoot any output or grab additional parameters.} 37 | 38 | \item{dtoken}{The Dropbox token generated by \code{\link{drop_auth}}. rdrop2 39 | will try to automatically locate your local credential cache and use them. 40 | However, if the credentials are not found, the function will initiate a new 41 | authentication request. You can override this in \code{\link{drop_auth}} by 42 | pointing to a different location where your credentials are stored.} 43 | } 44 | \description{ 45 | This function will allow you to write files of any size to Dropbox(even ones 46 | that cannot be read into memory) by uploading them in chunks. 47 | } 48 | \examples{ 49 | \dontrun{ 50 | write.csv(mtcars, file = "mtt.csv") 51 | drop_upload("mtt.csv") 52 | } 53 | } 54 | \references{ 55 | \href{https://www.dropbox.com/developers/documentation/http/documentation#files-upload}{API documentation} 56 | } 57 | -------------------------------------------------------------------------------- /man/get_dropbox_token.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_auth.R 3 | \name{get_dropbox_token} 4 | \alias{get_dropbox_token} 5 | \title{Retrieve oauth2 token from rdrop2-namespaced environment} 6 | \usage{ 7 | get_dropbox_token() 8 | } 9 | \description{ 10 | Retrieves a token if it is previously stored, otherwise prompts user to get one. 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /man/pipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/drop_utils.R 3 | \name{\%>\%} 4 | \alias{\%>\%} 5 | \title{Pipe operator} 6 | \usage{ 7 | lhs \%>\% rhs 8 | } 9 | \description{ 10 | Pipe operator 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /rDrop2.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: No 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(rdrop2) 3 | 4 | test_check("rdrop2") 5 | -------------------------------------------------------------------------------- /tests/testthat/helper-01.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #' Use this function to clean out your Dropbox in case there are stray files left over from a failed test. 4 | clean_dropbox <- function(x = "" , dtoken = get_dropbox_token()) { 5 | if(x != "y") { 6 | x <- 7 | readline( 8 | "WARNING: this will delete everything in your Dropbox account. \n Do not do this unless this is a test account. Are you sure?? (y/n)" 9 | ) 10 | } 11 | if (x == "y") { 12 | files <- drop_dir() 13 | suppressWarnings(sapply(files$path_lower, drop_delete)) 14 | } 15 | } 16 | 17 | 18 | #' Function makes file/folder names unique to prevent race conditions during concurrent tests. Pun on race condition 19 | traceless <- function(file) { 20 | paste0("rdrop2_package_test_", uuid::UUIDgenerate(), "_", file) 21 | } 22 | 23 | 24 | # This should clean out any remaining/old test files and folders 25 | clean_test_data <- function(pattern = "rdrop2_package_test", dtoken = get_dropbox_token()) { 26 | files <- drop_dir() 27 | 28 | if (nrow(files) > 0) { 29 | filenames <- files[["name"]] 30 | 31 | matching_files <- grep(pattern, filenames, value = TRUE) 32 | 33 | if (length(matching_files)) { 34 | suppressWarnings(purrr::walk(matching_files, drop_delete)) 35 | } 36 | } 37 | } 38 | 39 | # Counts files matching a pattern 40 | drop_file_count <- function(x, dtoken = get_dropbox_token()) { 41 | y <- drop_dir() 42 | if(nrow(y) > 0) { 43 | z <- grepl(x, y$name) 44 | sum(z, na.rm = TRUE) 45 | } else { 46 | 0 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/testthat/rdrop2_package_test_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthik/rdrop2/81bedd2f0fbd0ee55dd163230c1a0b26a80162cc/tests/testthat/rdrop2_package_test_image.png -------------------------------------------------------------------------------- /tests/testthat/test-01_rdrop-auth.R: -------------------------------------------------------------------------------- 1 | context("authorization") 2 | test_that("Able to authenticate from saved RDS token", { 3 | skip_on_cran() 4 | 5 | # read cached token and check its class 6 | expect_is(drop_auth(rdstoken = "token.rds"), "Token2.0") 7 | }) 8 | 9 | 10 | # drop_acc 11 | # ...................................... 12 | 13 | context("Testing that acc info works correctly") 14 | 15 | test_that("Account information works correctly", { 16 | skip_on_cran() 17 | 18 | acc_info <- drop_acc() 19 | # expect list 20 | expect_is(acc_info, "list") 21 | # name element should be its own list 22 | expect_is(acc_info$name, "list") 23 | }) 24 | 25 | -------------------------------------------------------------------------------- /tests/testthat/test-02_rdrop2-upload.R: -------------------------------------------------------------------------------- 1 | # !diagnostics off 2 | context("Testing drop_upload") 3 | 4 | # Test a basic file upload 5 | # ------------------------- 6 | test_that("Test that basic file ops work correctly", { 7 | skip_on_cran() 8 | 9 | # This is a simple test to see if we can upload a csv file successfully 10 | 11 | file_name <- traceless("file-ops.csv") 12 | 13 | write.csv(mtcars, file_name) 14 | row_count <- nrow(mtcars) 15 | print(file_name) 16 | # Check to see if file uploads are successful 17 | expect_message(drop_upload(file_name), "successfully at") 18 | unlink(file_name) 19 | drop_download(file_name) 20 | y <- utils::read.csv(file_name) 21 | server_row_count <- nrow(y) 22 | # Make sure the downloaded csv has the same number of rows 23 | expect_equal(row_count, server_row_count) 24 | unlink(file_name) 25 | drop_delete(file_name) 26 | }) 27 | 28 | # Test upload of an image 29 | # ------------------------ 30 | 31 | test_that("Image upload works correctly", { 32 | skip_on_cran() 33 | # This test is to see if we can upload an image (a png in this case) and make 34 | # sure that it maintains file integrity. We compare hashes of local file, then 35 | # the roundtrip copy. 36 | dest <- testthat::test_path('rdrop2_package_test_image.png') 37 | # No point continuing if you can’t find the file 38 | expect_true(file.exists(dest)) 39 | # Hash the file locally 40 | local_file_hash <- digest::digest(dest) 41 | # Upload it to Dropbox. The test fails here in the assertion inside the function 42 | # that makes sure the file exists before uploading 43 | drop_upload(dest) 44 | # Now delete the local copy 45 | unlink(dest) 46 | # Now download the file again, but make sure it's back in the testthat/tests directoryQ 47 | drop_download(path = basename(dest), local_path = testthat::test_path(".")) 48 | # Compute the hash on the return file 49 | roundtrip_file_hash <- digest::digest(dest) 50 | # Are they the same? 51 | expect_equal(local_file_hash, roundtrip_file_hash) 52 | # Delete the Dropbox copy 53 | drop_delete(dest) 54 | }) 55 | 56 | 57 | # Test upload of a non existent file 58 | # ---------------------------------- 59 | 60 | test_that("Upload of a non-existent file fails", { 61 | skip_on_cran() 62 | 63 | expect_error(drop_upload("higgledy-piggledy.csv")) 64 | expect_error(drop_upload("higgledy-piggledy.csv", mode = "stuff")) 65 | }) 66 | 67 | # Test autorename 68 | # ------------------ 69 | test_that("Autorename upload works correctly", { 70 | skip_on_cran() 71 | 72 | autorename_folder <- traceless("autorename_test") 73 | drop_create(autorename_folder) 74 | blank <- drop_dir(autorename_folder) 75 | expect_equal(nrow(blank), 0) 76 | write.csv(iris, file = "iris.csv") 77 | drop_upload("iris.csv", path = autorename_folder) 78 | one_file <- drop_dir(autorename_folder) 79 | expect_equal(nrow(one_file), 1) 80 | expect_identical(one_file[1,]$path_lower, paste0("/", autorename_folder, "/iris.csv")) 81 | # Write a slightly different object to the same filename 82 | write.csv(iris[1:6, ], file = "iris.csv") 83 | drop_upload("iris.csv", path = autorename_folder, mode = "add") 84 | two_files <- drop_dir(autorename_folder) 85 | expect_equal(nrow(two_files), 2) 86 | # This is what I should expect 87 | expected_files <- 88 | c(paste0("/", autorename_folder, "/iris.csv"), 89 | paste0("/", autorename_folder, "/iris (1).csv")) 90 | expect_identical(two_files$path_lower, expected_files) 91 | write.csv(iris[1:5, ], file = "iris.csv") 92 | drop_upload("iris.csv", path = autorename_folder, mode = "add") 93 | three_files <- drop_dir(autorename_folder) 94 | e_3files <- 95 | c( 96 | paste0("/", autorename_folder, "/iris.csv"), 97 | paste0("/", autorename_folder,"/iris (1).csv"), 98 | paste0("/", autorename_folder,"/iris (2).csv") 99 | ) 100 | expect_identical(three_files$path_lower, e_3files) 101 | drop_delete(autorename_folder) 102 | unlink("iris.csv") 103 | }) 104 | 105 | -------------------------------------------------------------------------------- /tests/testthat/test-04-rdrop2-metadata.R: -------------------------------------------------------------------------------- 1 | context("Testing drop_get_metadata") 2 | 3 | test_that("Able to retrieve metadata for file in multiple ways", { 4 | skip_on_cran() 5 | 6 | # upload new file to root 7 | file_name <- traceless("test-drop-get-metadata.csv") 8 | write.csv(mtcars, file_name) 9 | drop_upload(file_name) 10 | 11 | # lookup by path 12 | metadata <- drop_get_metadata(file_name) 13 | 14 | expect_is(metadata, "list") 15 | expect_equal(metadata$.tag, "file") 16 | 17 | # lookup by id 18 | expect_identical(metadata, drop_get_metadata(metadata$id)) 19 | 20 | # lookup by revision 21 | expect_identical(metadata, drop_get_metadata(paste0("rev:", metadata$rev))) 22 | 23 | # delete 24 | drop_delete(file_name) 25 | 26 | # get deleted metadata 27 | deleted_metadata <- drop_get_metadata(file_name, include_deleted = TRUE) 28 | 29 | expect_is(deleted_metadata, "list") 30 | expect_equal(deleted_metadata$.tag, "deleted") 31 | expect_identical( 32 | metadata[c("name", "path_lower", "path_display")], 33 | deleted_metadata[c("name", "path_lower", "path_display")] 34 | ) 35 | 36 | # cleanup 37 | unlink(file_name) 38 | }) 39 | -------------------------------------------------------------------------------- /tests/testthat/test-05-rdrop2-dir.R: -------------------------------------------------------------------------------- 1 | context("testing drop_dir") 2 | 3 | test_that("drop_dir lists files normally", { 4 | skip_on_cran() 5 | 6 | # create folders and objects 7 | folder_name <- traceless("test-drop_dir") 8 | drop_create(folder_name) 9 | 10 | file_name <- traceless("test-drop_dir.csv") 11 | write.csv(mtcars, file_name) 12 | drop_upload(file_name, path = folder_name) 13 | 14 | # list contents 15 | results <- drop_dir(folder_name) 16 | 17 | expect_is(results, "tbl_df") 18 | expect_equal(nrow(results), 1) 19 | 20 | # add more things 21 | subfolder_name <- paste0(folder_name, "/", traceless("test-drop_subdir")) 22 | drop_create(subfolder_name) 23 | 24 | subfile_name <- traceless("test-drop-subfile.csv") 25 | write.csv(iris, subfile_name) 26 | drop_upload(subfile_name, path = subfolder_name) 27 | 28 | results <- drop_dir(folder_name) 29 | expect_equal(nrow(results), 2) 30 | 31 | results <- drop_dir(folder_name, recursive = TRUE) 32 | expect_equal(nrow(results), 4) # also returns entry for folder_name 33 | 34 | # cleanup 35 | unlink(file_name) 36 | unlink(subfile_name) 37 | drop_delete(folder_name) 38 | }) 39 | 40 | 41 | test_that("drop_dir can detect changes in directory", { 42 | skip_on_cran() 43 | 44 | # create a folder 45 | folder_name <- traceless("drop_dir") 46 | drop_create(folder_name) 47 | 48 | # put a file in it 49 | file_name <- traceless("drop_dir.csv") 50 | write.csv(mtcars, file_name) 51 | drop_upload(file_name, paste0(folder_name, "/", file_name)) 52 | 53 | # get a cursor 54 | cursor <- drop_dir(folder_name, cursor = TRUE) 55 | expect_is(cursor, "character") 56 | 57 | # add another file 58 | file_name2 <- traceless("drop_dir.csv") 59 | write.csv(iris, file_name2) 60 | drop_upload(file_name2, paste0(folder_name, "/", file_name2)) 61 | 62 | # check for contents since cursor 63 | entries <- drop_dir(cursor = cursor) 64 | expect_is(entries, "tbl_df") 65 | expect_equal(nrow(entries), 1) 66 | expect_equal(entries$name[1], file_name2) 67 | 68 | # check whole folder 69 | entries <- drop_dir(folder_name) 70 | expect_is(entries, "tbl_df") 71 | expect_equal(nrow(entries), 2) 72 | expect_equal(entries$name, c(file_name, file_name2)) 73 | 74 | # cleanup 75 | drop_delete(folder_name) 76 | unlink(file_name) 77 | unlink(file_name2) 78 | }) 79 | -------------------------------------------------------------------------------- /tests/testthat/test-06-rdrop2-download.R: -------------------------------------------------------------------------------- 1 | context("testing drop_download") 2 | 3 | test_that("drop_download works as expected", { 4 | skip_on_cran() 5 | 6 | # create and upload a file for testing 7 | file_name <- traceless("test-drop-download.csv") 8 | write.csv(mtcars, file_name) 9 | drop_upload(file_name) 10 | 11 | # download to same path 12 | unlink(file_name) 13 | expect_true(drop_download(file_name)) 14 | expect_true(file.exists(file_name)) 15 | 16 | # download to a new path 17 | new_path <- traceless("test-drop_download_newpath.csv") 18 | expect_true(drop_download(file_name, new_path)) 19 | expect_true(file.exists(new_path)) 20 | 21 | # dowload to an implied path 22 | new_dir <- traceless("test-drop_download_newdir") 23 | dir.create(new_dir) 24 | implied_path <- file.path(new_dir, file_name) 25 | expect_true(drop_download(file_name, new_dir)) 26 | expect_true(file.exists(implied_path)) 27 | 28 | # cleanup 29 | unlink(file_name) 30 | unlink(new_path) 31 | unlink(new_dir, recursive = TRUE) 32 | drop_delete(file_name) 33 | }) 34 | 35 | test_that("drop_get works, but is deprecated", { 36 | skip_on_cran() 37 | 38 | # create and upload a file for testing, then delete locally 39 | file_name <- traceless("test-drop_download.csv") 40 | write.csv(mtcars, file_name) 41 | drop_upload(file_name) 42 | unlink(file_name) 43 | 44 | # check for deprecation warning 45 | expect_warning( 46 | drop_get(file_name), 47 | "is deprecated" 48 | ) 49 | 50 | # make sure the file was downloaded 51 | expect_true(file.exists(file_name)) 52 | 53 | # cleanup 54 | unlink(file_name) 55 | drop_delete(file_name) 56 | }) 57 | -------------------------------------------------------------------------------- /tests/testthat/test-07-drop_ops.R: -------------------------------------------------------------------------------- 1 | # !diagnostics off 2 | 3 | context("Testing drop copy") 4 | # For now I haven't used traceless to make these tests more readable 5 | # while we work through them 6 | 7 | test_that("drop_copy works correctly", { 8 | skip_on_cran() 9 | 10 | # # Copying files to files only 11 | # # ------------------------ 12 | # We need to start with a clean slate 13 | # clean_test_data("iris-test-copy") 14 | cfile_name <- traceless("iris-test-copy.csv") 15 | # Copy a file to a new name 16 | write.csv(iris, cfile_name) 17 | drop_upload(cfile_name) 18 | # Copy to a new name, same folder 19 | cfile_name2 <- traceless("iris-test-copy.csv") 20 | drop_copy(cfile_name, cfile_name2) 21 | exp_2 <- sort(c(cfile_name, cfile_name2)) 22 | server_exp_2 <- sort(drop_dir()$name) 23 | cat("\n") 24 | cat(exp_2) 25 | cat("\n") 26 | cat(server_exp_2) 27 | expect_identical(exp_2, server_exp_2) 28 | # Copy to same name, but autorename is TRUE 29 | file_3 <- drop_copy(cfile_name, cfile_name, autorename = TRUE) 30 | # There is a problem here 31 | # num_copy_files3 <- drop_file_count("iris-test-copy") 32 | # expect_equal(num_copy_files3 , 3) 33 | drop_delete(cfile_name2) 34 | drop_delete(file_3$metadata$path_lower) 35 | # 36 | # # # Copying files to folders 37 | # # # ------------------------ 38 | drop_create("copy_folder") 39 | drop_copy(cfile_name, "copy_folder") 40 | dc_dir <- drop_dir("copy_folder") %>% dplyr::select(name) %>% dplyr::pull() 41 | expect_identical(dc_dir, cfile_name) 42 | drop_delete(cfile_name) 43 | # 44 | # # Copying folders to existing folders 45 | # # ------------------------ 46 | drop_create("copy_folder_2") 47 | drop_copy("copy_folder", "copy_folder_2") 48 | copy_folder_2_contents <- drop_dir("copy_folder_2/copy_folder") %>% dplyr::select(name) %>% dplyr::pull() 49 | expect_identical(copy_folder_2_contents, cfile_name) 50 | drop_delete("copy_folder_2") 51 | # 52 | # # Copying files to new folders 53 | # # ------------------------ 54 | drop_copy("copy_folder", "kerfuffle") 55 | d1 <- drop_dir("copy_folder") %>% dplyr::select(name) %>% dplyr::pull() %>% sort 56 | d2 <- drop_dir("kerfuffle") %>% dplyr::select(name) %>% dplyr::pull() %>% sort 57 | expect_identical(d1, d2) 58 | drop_delete("kerfuffle") 59 | drop_delete("copy_folder") 60 | unlink(cfile_name) 61 | unlink(cfile_name2) 62 | }) 63 | 64 | # -------------------------- 65 | # Drop Move 66 | # -------------------------- 67 | 68 | 69 | # drop_move 70 | test_that("Moving files works correctly", { 71 | skip_on_cran() 72 | 73 | file_name <- traceless("move.csv") 74 | write.csv(iris, file = file_name) 75 | drop_upload(file_name) 76 | 77 | mtest <- traceless("move_test") 78 | drop_create(mtest) 79 | drop_move(file_name, paste0("/", mtest, "/", file_name)) 80 | res <- drop_dir(mtest) 81 | # problem 82 | expect_identical(basename(file_name), basename(res$path_lower)) 83 | # Now test that the file is there. 84 | # do a search for the path/file 85 | # the make sure it exists 86 | 87 | # cleanup 88 | drop_delete(mtest) 89 | unlink(file_name) 90 | }) 91 | 92 | # -------------------------- 93 | # Drop Create 94 | # -------------------------- 95 | 96 | test_that("Drop create works correctly", { 97 | skip_on_cran() 98 | 99 | folder_1 <- traceless("folder_1") 100 | drop_create(folder_1) 101 | expect_true(drop_is_folder(folder_1)) 102 | expect_error(drop_create(folder_1)) 103 | drop_create(folder_1, autorename = TRUE) 104 | folder_duplicate <- paste0(folder_1, " (1)") 105 | drop_delete(folder_1) 106 | drop_delete(folder_duplicate) 107 | }) 108 | 109 | # -------------------------- 110 | # Drop Delete 111 | # -------------------------- 112 | 113 | test_that("Drop delete works correctly", { 114 | skip_on_cran() 115 | 116 | file_name <- traceless("delete.csv") 117 | write.csv(iris, file = file_name) 118 | drop_upload(file_name) 119 | expect_equal(drop_file_count("delete.csv"), 1) 120 | drop_delete(file_name) 121 | expect_equal(drop_file_count("delete.csv"), 0) 122 | fake_file <- traceless("delete.csv") 123 | # Fails 124 | expect_error(drop_delete(fake_file)) 125 | unlink(file_name) 126 | }) 127 | 128 | # -------------------------- 129 | # Drop Exists 130 | # -------------------------- 131 | 132 | test_that("drop_exists works correctly", { 133 | skip_on_cran() 134 | 135 | folder_name <- traceless("drop_exists") 136 | folder_name2 <- paste0(folder_name, "/", "sub_folder") 137 | drop_create(folder_name) 138 | # This should create a subfolder inside folder_name 139 | drop_create(folder_name2) 140 | 141 | # A check on a non existent sub folder should return FALSE 142 | fake_nested_path <- paste0(traceless("foo"), "/", traceless("foo"), "/", traceless("foo")) 143 | expect_false(drop_exists(fake_nested_path)) 144 | 145 | expect_true(drop_exists(folder_name)) 146 | expect_true(drop_exists(folder_name2)) 147 | expect_false(drop_exists(traceless("stuffnthings"))) 148 | 149 | # Now test files inside subfolders 150 | write.csv(iris, file = "iris.csv") 151 | drop_upload("iris.csv", path = folder_name) 152 | expect_true(drop_exists(paste0(folder_name, "/iris.csv"))) 153 | 154 | #cleanup 155 | drop_delete(folder_name) 156 | unlink("iris.csv") 157 | }) 158 | 159 | # -------------------------- 160 | # Drop Misc 161 | # -------------------------- 162 | 163 | # Nothing here yet, but we should test the misc functions 164 | # a the bottom of the helper files. 165 | 166 | 167 | -------------------------------------------------------------------------------- /tests/testthat/test-08-rdrop2-content-hash.R: -------------------------------------------------------------------------------- 1 | context("testing drop_content_hash") 2 | 3 | test_that("known good values", { 4 | # These were generated by hand and tested - they serve as regression 5 | # tests and do not require dropbox access. 6 | 7 | four_mb <- 4L * 1024L * 1024L 8 | 9 | # one short string 10 | b1 <- charToRaw("hello rdrop2") 11 | # one complete block 12 | b2 <- as.raw(rep(0:255, length.out = four_mb)) 13 | # one block plus one byte 14 | b3 <- c(b2, as.raw(255L)) 15 | # several blocks 16 | b4 <- as.raw(rep(0:255, length.out = 3.5 * four_mb)) 17 | 18 | h1 <- "1aa2b7623dfff520c4abdc1227d10bc4e229b74005b66b3458345830e556f4de" 19 | h2 <- "894bbb52d1212d6bcbe9967f1a2169138c4d4af0c8dfbaeae86cd1d3f0c03faf" 20 | h3 <- "9149387a91f71c7c2149b8427d15526b71c1a38d6c6f999ad71486a2ce788d57" 21 | h4 <- "f61d3ae93fa4f7646d37949fc0885141bf682faa9a130896b93459c650335d0f" 22 | 23 | write_bytes <- function(bytes) { 24 | path <- tempfile() 25 | writeBin(bytes, path) 26 | path 27 | } 28 | 29 | f1 <- write_bytes(b1) 30 | f2 <- write_bytes(b2) 31 | f3 <- write_bytes(b3) 32 | f4 <- write_bytes(b4) 33 | 34 | on.exit(unlink(c(f1, f2, f3, f4))) 35 | 36 | expect_equal(drop_content_hash(f1), h1) 37 | expect_equal(drop_content_hash(f2), h2) 38 | expect_equal(drop_content_hash(f3), h3) 39 | expect_equal(drop_content_hash(f4), h4) 40 | 41 | ## This is what is actually going on: 42 | expect_equal(sha256(sha256(b1, TRUE), FALSE), h1) 43 | expect_equal(sha256(sha256(b2, TRUE), FALSE), h2) 44 | expect_equal(sha256(c(sha256(b3[seq_len(four_mb)], TRUE), 45 | sha256(b3[-seq_len(four_mb)], TRUE)), 46 | FALSE), h3) 47 | }) 48 | 49 | test_that("vectorisation", { 50 | write_bytes <- function(bytes) { 51 | path <- tempfile() 52 | writeBin(bytes, path) 53 | path 54 | } 55 | 56 | paths <- vapply(1:5, function(.) write_bytes(sample(as.raw(0:255))), 57 | character(1)) 58 | hash <- vapply(paths, drop_content_hash, character(1)) 59 | 60 | expect_equal(drop_content_hash(paths), unname(hash)) 61 | expect_equal(drop_content_hash(character(0)), character(0)) 62 | i <- c(1, 1, 2, 3) 63 | expect_equal(drop_content_hash(paths[i]), unname(hash)[i]) 64 | }) 65 | 66 | test_that("content_hash agrees with dropbox", { 67 | skip_on_cran() 68 | 69 | # create folders and objects 70 | folder_name <- traceless("test-drop_dir") 71 | drop_create(folder_name) 72 | 73 | file_name <- traceless("test-drop_dir.csv") 74 | write.csv(mtcars, file_name) 75 | drop_upload(file_name, path = folder_name) 76 | 77 | on.exit({ 78 | drop_delete(folder_name) 79 | unlink(file_name) 80 | }) 81 | 82 | info <- drop_dir(folder_name) 83 | expect_equal(drop_content_hash(file_name), info$content_hash) 84 | }) 85 | 86 | 87 | test_that("input validation", { 88 | expect_error(drop_content_hash(1:10), 89 | "Expected 'file' to be a character vector") 90 | }) 91 | -------------------------------------------------------------------------------- /tests/testthat/test-99-rdrop2.R: -------------------------------------------------------------------------------- 1 | # These are the remaining tests after auth and upload.If the tests are getting 2 | # to be more than 30 lines, split them off into a separate file. Execution order 3 | # is is all of the `helper-*.R` files and then all of the `test-*.R` files 4 | 5 | context("old tests") 6 | 7 | 8 | # drop_shared 9 | test_that("drop_share works correctly", { 10 | skip_on_cran() 11 | 12 | file_name <- traceless("share.csv") 13 | write.csv(iris, file = file_name) 14 | drop_upload(file_name) 15 | res <- drop_share(file_name) 16 | expect_equal(length(res), 11) 17 | share_names <- sort(c(".tag", "url", "id", "name", "path_lower", "link_permissions", 18 | "preview_type","client_modified", "server_modified", "rev", "size")) 19 | res_names <- sort(names(res)) 20 | expect_identical(share_names, res_names) 21 | 22 | # cleanup 23 | unlink(file_name) 24 | drop_delete(file_name) 25 | }) 26 | 27 | 28 | # drop_search 29 | test_that("drop_search works correctly", { 30 | skip_on_cran() 31 | 32 | folder_name <- traceless("test-drop_search") 33 | #paste0("test-drop_search-", uuid::UUIDgenerate()) 34 | drop_create(folder_name) 35 | 36 | write.csv(mtcars, "mtcars.csv") 37 | drop_upload("mtcars.csv", path = folder_name) 38 | 39 | x <- drop_search("mt") 40 | 41 | expect_equal(x$matches[[1]]$metadata$name, "mtcars.csv") 42 | 43 | # A search with no query should fail 44 | expect_error(drop_search()) 45 | 46 | #cleanup 47 | drop_delete(folder_name) 48 | unlink("mtcars.csv") 49 | }) 50 | 51 | 52 | # drop_history 53 | test_that("drop_history works correctly", { 54 | skip_on_cran() 55 | 56 | # upload once 57 | file_name <- traceless("drop_history_iris.csv") 58 | write.csv(iris, file_name) 59 | drop_upload(file_name) 60 | 61 | revisions <- drop_history(file_name) 62 | 63 | expect_is(revisions, "tbl_df") 64 | expect_equal(nrow(revisions), 1) 65 | 66 | # delete, edit, upload again 67 | # TODO: add proper revision once drop_upload supports it 68 | drop_delete(file_name) 69 | write.csv(iris[iris$Species == "setosa",], file_name) 70 | drop_upload(file_name) 71 | 72 | revisions <- drop_history(file_name) 73 | expect_equal(nrow(revisions), 2) 74 | 75 | # test limit arguments 76 | revisions <- drop_history(file_name, limit = 1) 77 | expect_equal(nrow(revisions), 1) 78 | 79 | # cleanup 80 | drop_delete(file_name) 81 | unlink(file_name) 82 | }) 83 | 84 | 85 | # drop_exists 86 | test_that("drop_exists works correctly", { 87 | skip_on_cran() 88 | 89 | folder_name <- traceless("drop_exists") 90 | drop_create(folder_name) 91 | 92 | expect_true(drop_exists(folder_name)) 93 | expect_false(drop_exists(traceless("stuffnthings"))) 94 | 95 | # Now test files inside subfolders 96 | write.csv(iris, file = "iris.csv") 97 | drop_upload("iris.csv", path = folder_name) 98 | expect_true(drop_exists(paste0(folder_name, "/iris.csv"))) 99 | 100 | #cleanup 101 | drop_delete(folder_name) 102 | unlink("iris.csv") 103 | }) 104 | 105 | 106 | # drop_media 107 | test_that("drop_media works correctly", { 108 | skip_on_cran() 109 | 110 | file_name <- traceless("drop_media") 111 | download.file("http://media4.giphy.com/media/YaXcVXGvBQlEI/200.gif", 112 | destfile = file_name) 113 | drop_upload(file_name) 114 | 115 | media_url <- drop_media(file_name) 116 | expect_match(media_url$link, "https://dl.dropboxusercontent.com") 117 | 118 | # cleanup 119 | unlink(file_name) 120 | drop_delete(file_name) 121 | }) 122 | 123 | # minor test for strip slashes 124 | test_that("strip slashes works correctly", { 125 | orig <- "//test/" 126 | expect_true(grepl("^//", orig)) 127 | expect_false(grepl("^//", strip_slashes(orig))) 128 | expect_true(grepl("^/", strip_slashes(orig))) 129 | expect_true(grepl("/$", orig)) 130 | expect_false(grepl("/$", strip_slashes(orig))) 131 | }) 132 | 133 | # drop_read_csv 134 | test_that("drop_read_csv works correctly", { 135 | skip_on_cran() 136 | 137 | file_name <- traceless("drop_read.csv") 138 | write.csv(iris, file = file_name) 139 | drop_upload(file_name) 140 | z <- drop_read_csv(file_name) 141 | expect_is(z, "data.frame") 142 | 143 | # cleanup 144 | unlink(file_name) 145 | drop_delete(file_name) 146 | }) 147 | 148 | # Final cleanup of test files 149 | test_that("final cleanup", { 150 | skip_on_cran() 151 | clean_test_data() 152 | }) 153 | -------------------------------------------------------------------------------- /tests/testthat/token.rds.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthik/rdrop2/81bedd2f0fbd0ee55dd163230c1a0b26a80162cc/tests/testthat/token.rds.enc --------------------------------------------------------------------------------