From 75c5b32cbbf7f1314f84c60bfc75dcbaa97e4a18 Mon Sep 17 00:00:00 2001 From: Hong Ooi Date: Mon, 22 May 2023 23:33:59 +1000 Subject: [PATCH 1/2] itemid --- R/ms_drive.R | 56 +++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/R/ms_drive.R b/R/ms_drive.R index 920e093..ca93c91 100644 --- a/R/ms_drive.R +++ b/R/ms_drive.R @@ -15,24 +15,24 @@ #' - `do_operation(...)`: Carry out an arbitrary operation on the drive. #' - `sync_fields()`: Synchronise the R object with the drive metadata in Microsoft Graph. #' - `list_items(...), list_files(...)`: List the files and folders under the specified path. See 'File and folder operations' below. -#' - `download_file(src, dest, overwrite)`: Download a file. -#' - `download_folder(src, dest, overwrite, recursive, parallel)`: Download a folder. +#' - `download_file(src, srcid, dest, overwrite)`: Download a file. +#' - `download_folder(src, srcid, dest, overwrite, recursive, parallel)`: Download a folder. #' - `upload_file(src, dest, blocksize)`: Upload a file. #' - `upload_folder(src, dest, blocksize, recursive, parallel)`: Upload a folder. #' - `create_folder(path)`: Create a folder. -#' - `open_item(path)`: Open a file or folder. +#' - `open_item(path, itemid)`: Open a file or folder. #' - `create_share_link(...)`: Create a shareable link for a file or folder. -#' - `delete_item(path, confirm, by_item)`: Delete a file or folder. By default, ask for confirmation first. For personal OneDrive, deleting a folder will also automatically delete its contents; for business OneDrive or SharePoint document libraries, you may need to set `by_item=TRUE` to delete the contents first depending on your organisation's policies. Note that this can be slow for large folders. -#' - `get_item(path)`: Get an item representing a file or folder. -#' - `get_item_properties(path)`: Get the properties (metadata) for a file or folder. -#' - `set_item_properties(path, ...)`: Set the properties for a file or folder. +#' - `delete_item(path, itemid, confirm, by_item)`: Delete a file or folder. By default, ask for confirmation first. For personal OneDrive, deleting a folder will also automatically delete its contents; for business OneDrive or SharePoint document libraries, you may need to set `by_item=TRUE` to delete the contents first depending on your organisation's policies. Note that this can be slow for large folders. +#' - `get_item(path, itemid)`: Get an item representing a file or folder. +#' - `get_item_properties(path, itemid)`: Get the properties (metadata) for a file or folder. +#' - `set_item_properties(path, itemid, ...)`: Set the properties for a file or folder. #' - `list_shared_items(...), list_shared_files(...)`: List the drive items shared with you. See 'Shared items' below. #' #' @section Initialization: #' Creating new objects of this class should be done via the `get_drive` methods of the [`ms_graph`], [`az_user`] or [`ms_site`] classes. Calling the `new()` method for this class only constructs the R object; it does not call the Microsoft Graph API to retrieve or create the actual drive. #' #' @section File and folder operations: -#' This class exposes methods for carrying out common operations on files and folders. They call down to the corresponding methods for the [`ms_drive_item`] class. In this context, any paths to child items are relative to the root folder of the drive. +#' This class exposes methods for carrying out common operations on files and folders. They call down to the corresponding methods for the [`ms_drive_item`] class. In most cases an item can be specified either by path or ID. The former is more user-friendly but subject to change if the file is moved or renamed; the latter is an opaque string but is immutable regardless of file operations. In general, using the path should be good enough for most purposes. #' #' `open_item` opens the given file or folder in your browser. If the file has an unrecognised type, most browsers will attempt to download it. #' @@ -55,7 +55,7 @@ #' #' `delete_item` deletes a file or folder. By default, it will ask for confirmation first. #' -#' `get_item` retrieves the file or folder with the given path, as an object of class [`ms_drive_item`]. +#' `get_item` retrieves the file or folder with the given path or ID, as an object of class [`ms_drive_item`]. #' #' `get_item_properties` is a convenience function that returns the properties of a file or folder as a list. #' @@ -151,35 +151,41 @@ public=list( private$get_root()$create_folder(path) }, - download_file=function(src, dest=basename(src), overwrite=FALSE) + download_file=function(src=NULL, srcid=NULL, dest=basename(src), overwrite=FALSE) { - self$get_item(src)$download(dest, overwrite=overwrite) + self$get_item(src, srcid)$download(dest, overwrite=overwrite) }, - download_folder=function(src, dest=basename(src), overwrite=FALSE, recursive=FALSE, parallel=FALSE) + download_folder=function(src=NULL, srcid=NULL, dest=basename(src), overwrite=FALSE, recursive=FALSE, + parallel=FALSE) { - self$get_item(src)$download(dest, overwrite=overwrite, recursive=recursive, parallel=parallel) + self$get_item(src, srcid)$ + download(dest, overwrite=overwrite, recursive=recursive, parallel=parallel) }, - create_share_link=function(path, type=c("view", "edit", "embed"), expiry="7 days", password=NULL, scope=NULL) + create_share_link=function(path=NULL, itemid=NULL, type=c("view", "edit", "embed"), expiry="7 days", + password=NULL, scope=NULL) { type <- match.arg(type) - self$get_item(path)$get_share_link(type, expiry, password, scope) + self$get_item(path, itemid)$get_share_link(type, expiry, password, scope) }, - open_item=function(path) + open_item=function(path=NULL, itemid=NULL) { - self$get_item(path)$open() + self$get_item(path, itemid)$open() }, - delete_item=function(path, confirm=TRUE, by_item=FALSE) + delete_item=function(path=NULL, itemid=NULL, confirm=TRUE, by_item=FALSE) { - self$get_item(path)$delete(confirm=confirm, by_item=by_item) + self$get_item(path, itemid)$delete(confirm=confirm, by_item=by_item) }, - get_item=function(path) + get_item=function(path=NULL, itemid=NULL) { - op <- if(path != "/") + assert_one_arg(path, itemid, msg="Must supply one of item path or ID") + op <- if(!is.null(itemid)) + file.path("items", itemid) + else if(path != "/") { path <- curl::curl_escape(gsub("^/|/$", "", path)) # remove any leading and trailing slashes file.path("root:", path) @@ -188,14 +194,14 @@ public=list( ms_drive_item$new(self$token, self$tenant, self$do_operation(op)) }, - get_item_properties=function(path) + get_item_properties=function(path=NULL, itemid=NULL) { - self$get_item(path)$properties + self$get_item(path, itemid)$properties }, - set_item_properties=function(path, ...) + set_item_properties=function(path=NULL, itemid=NULL, ...) { - self$get_item(path)$update(...) + self$get_item(path, itemid)$update(...) }, list_shared_items=function(allow_external=TRUE, filter=NULL, n=Inf, pagesize=1000, info=NULL) From c613e7ce6780baea0e92f0620e8353cdaa882912 Mon Sep 17 00:00:00 2001 From: Hong Ooi Date: Tue, 23 May 2023 03:05:33 +1000 Subject: [PATCH 2/2] document --- NEWS.md | 7 ++++ R/ms_drive.R | 30 +++++++++------- man/ms_drive.Rd | 44 +++++++++++++---------- tests/testthat/test01_onedrive.R | 18 ++++++++-- tests/testthat/test02_business_onedrive.R | 2 +- 5 files changed, 67 insertions(+), 34 deletions(-) diff --git a/NEWS.md b/NEWS.md index 7cbd7c6..4648ddf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,8 +1,15 @@ # Microsoft365R 2.3.4.9000 +## OneDrive/SharePoint + - Fix broken functionality for shared items in OneDrive/Sharepoint. In particular, this should allow using the MS365 backend with the pins package (#149/#129). - The `list_shared_items`/`list_shared_files` method for drives now always returns a list of drive item objects, rather than a data frame. If the `info` argument is supplied with a value other than "items", a warning is issued. - Add folder upload and download functionality for `ms_drive_item$upload()` and `download()`. Subfolders can also be transferred recursively, and optionally in parallel. There are also corresponding `ms_drive$upload_folder()` and `download_folder()` methods. +- Add the ability to use object IDs instead of file/folder paths in `ms_drive` methods, including getting, uploading and downloading. This can be useful since the object ID is immutable, whereas file paths can be changed. See `?ms_drive` for more details. + +## Outlook + +- Fix a bug in specifying multiple addresses in an email (#134, #151). # Microsoft365R 2.3.4 diff --git a/R/ms_drive.R b/R/ms_drive.R index ca93c91..d553d87 100644 --- a/R/ms_drive.R +++ b/R/ms_drive.R @@ -32,10 +32,20 @@ #' Creating new objects of this class should be done via the `get_drive` methods of the [`ms_graph`], [`az_user`] or [`ms_site`] classes. Calling the `new()` method for this class only constructs the R object; it does not call the Microsoft Graph API to retrieve or create the actual drive. #' #' @section File and folder operations: -#' This class exposes methods for carrying out common operations on files and folders. They call down to the corresponding methods for the [`ms_drive_item`] class. In most cases an item can be specified either by path or ID. The former is more user-friendly but subject to change if the file is moved or renamed; the latter is an opaque string but is immutable regardless of file operations. In general, using the path should be good enough for most purposes. +#' This class exposes methods for carrying out common operations on files and folders. They call down to the corresponding methods for the [`ms_drive_item`] class. In most cases an item can be specified either by path or ID. The former is more user-friendly but subject to change if the file is moved or renamed; the latter is an opaque string but is immutable regardless of file operations. +#' +#' `get_item(path, itemid)` retrieves a file or folder, as an object of class [`ms_drive_item`]. Specify either the path or ID, not both. #' #' `open_item` opens the given file or folder in your browser. If the file has an unrecognised type, most browsers will attempt to download it. #' +#' `delete_item` deletes a file or folder. By default, it will ask for confirmation first. +#' +#' `create_share_link(path, itemid, type, expiry, password, scope)` returns a shareable link to the item. +#' +#' `get_item_properties` is a convenience function that returns the properties of a file or folder as a list. +#' +#' `set_item_properties` sets the properties of a file or folder. The new properties should be specified as individual named arguments to the method. Any existing properties that aren't listed as arguments will retain their previous values or be recalculated based on changes to other properties, as appropriate. You can also call the `update` method on the corresponding `ms_drive_item` object. +#' #' `list_items(path, info, full_names, pagesize)` lists the items under the specified path. #' #' `list_files` is a synonym for `list_items`. @@ -51,16 +61,6 @@ #' #' `create_folder` creates a folder with the specified path. Trying to create an already existing folder is an error. #' -#' `create_share_link(path, type, expiry, password, scope)` returns a shareable link to the item. -#' -#' `delete_item` deletes a file or folder. By default, it will ask for confirmation first. -#' -#' `get_item` retrieves the file or folder with the given path or ID, as an object of class [`ms_drive_item`]. -#' -#' `get_item_properties` is a convenience function that returns the properties of a file or folder as a list. -#' -#' `set_item_properties` sets the properties of a file or folder. The new properties should be specified as individual named arguments to the method. Any existing properties that aren't listed as arguments will retain their previous values or be recalculated based on changes to other properties, as appropriate. You can also call the `update` method on the corresponding `ms_drive_item` object. -#' #' @section Shared items: #' The `list_shared_items` method shows the files and folders that have been shared with you. This is a named list of drive items, that you can use to access the shared files/folders. The arguments are: #' - `allow_external`: Whether to include items that were shared from outside tenants. The default is FALSE. @@ -108,9 +108,15 @@ #' # file metadata (name, date created, etc) #' drv$get_item_properties("myfile") #' -#' # rename a file +#' # rename a file -- item ID remains the same, while name is changed +#' obj <- drv$get_item("myfile") #' drv$set_item_properties("myfile", name="newname") #' +#' # retrieve the renamed object by ID +#' id <- obj$properties$id +#' obj2 <- drv$get_item(itemid=id) +#' obj$properties$id == obj2$properties$id # TRUE +#' #' # accessing shared files #' shared_df <- drv$list_shared_items() #' shared_df$remoteItem[[1]]$open() diff --git a/man/ms_drive.Rd b/man/ms_drive.Rd index cbfe340..5c79f89 100644 --- a/man/ms_drive.Rd +++ b/man/ms_drive.Rd @@ -29,17 +29,17 @@ Class representing a personal OneDrive or SharePoint document library. \item \code{do_operation(...)}: Carry out an arbitrary operation on the drive. \item \code{sync_fields()}: Synchronise the R object with the drive metadata in Microsoft Graph. \item \verb{list_items(...), list_files(...)}: List the files and folders under the specified path. See 'File and folder operations' below. -\item \code{download_file(src, dest, overwrite)}: Download a file. -\item \code{download_folder(src, dest, overwrite, recursive, parallel)}: Download a folder. +\item \code{download_file(src, srcid, dest, overwrite)}: Download a file. +\item \code{download_folder(src, srcid, dest, overwrite, recursive, parallel)}: Download a folder. \item \code{upload_file(src, dest, blocksize)}: Upload a file. \item \code{upload_folder(src, dest, blocksize, recursive, parallel)}: Upload a folder. \item \code{create_folder(path)}: Create a folder. -\item \code{open_item(path)}: Open a file or folder. +\item \code{open_item(path, itemid)}: Open a file or folder. \item \code{create_share_link(...)}: Create a shareable link for a file or folder. -\item \code{delete_item(path, confirm, by_item)}: Delete a file or folder. By default, ask for confirmation first. For personal OneDrive, deleting a folder will also automatically delete its contents; for business OneDrive or SharePoint document libraries, you may need to set \code{by_item=TRUE} to delete the contents first depending on your organisation's policies. Note that this can be slow for large folders. -\item \code{get_item(path)}: Get an item representing a file or folder. -\item \code{get_item_properties(path)}: Get the properties (metadata) for a file or folder. -\item \code{set_item_properties(path, ...)}: Set the properties for a file or folder. +\item \code{delete_item(path, itemid, confirm, by_item)}: Delete a file or folder. By default, ask for confirmation first. For personal OneDrive, deleting a folder will also automatically delete its contents; for business OneDrive or SharePoint document libraries, you may need to set \code{by_item=TRUE} to delete the contents first depending on your organisation's policies. Note that this can be slow for large folders. +\item \code{get_item(path, itemid)}: Get an item representing a file or folder. +\item \code{get_item_properties(path, itemid)}: Get the properties (metadata) for a file or folder. +\item \code{set_item_properties(path, itemid, ...)}: Set the properties for a file or folder. \item \verb{list_shared_items(...), list_shared_files(...)}: List the drive items shared with you. See 'Shared items' below. } } @@ -51,10 +51,20 @@ Creating new objects of this class should be done via the \code{get_drive} metho \section{File and folder operations}{ -This class exposes methods for carrying out common operations on files and folders. They call down to the corresponding methods for the \code{\link{ms_drive_item}} class. In this context, any paths to child items are relative to the root folder of the drive. +This class exposes methods for carrying out common operations on files and folders. They call down to the corresponding methods for the \code{\link{ms_drive_item}} class. In most cases an item can be specified either by path or ID. The former is more user-friendly but subject to change if the file is moved or renamed; the latter is an opaque string but is immutable regardless of file operations. + +\code{get_item(path, itemid)} retrieves a file or folder, as an object of class \code{\link{ms_drive_item}}. Specify either the path or ID, not both. \code{open_item} opens the given file or folder in your browser. If the file has an unrecognised type, most browsers will attempt to download it. +\code{delete_item} deletes a file or folder. By default, it will ask for confirmation first. + +\code{create_share_link(path, itemid, type, expiry, password, scope)} returns a shareable link to the item. + +\code{get_item_properties} is a convenience function that returns the properties of a file or folder as a list. + +\code{set_item_properties} sets the properties of a file or folder. The new properties should be specified as individual named arguments to the method. Any existing properties that aren't listed as arguments will retain their previous values or be recalculated based on changes to other properties, as appropriate. You can also call the \code{update} method on the corresponding \code{ms_drive_item} object. + \code{list_items(path, info, full_names, pagesize)} lists the items under the specified path. \code{list_files} is a synonym for \code{list_items}. @@ -71,16 +81,6 @@ Transferring files in parallel can result in substantial speedup for a large num } \code{create_folder} creates a folder with the specified path. Trying to create an already existing folder is an error. - -\code{create_share_link(path, type, expiry, password, scope)} returns a shareable link to the item. - -\code{delete_item} deletes a file or folder. By default, it will ask for confirmation first. - -\code{get_item} retrieves the file or folder with the given path, as an object of class \code{\link{ms_drive_item}}. - -\code{get_item_properties} is a convenience function that returns the properties of a file or folder as a list. - -\code{set_item_properties} sets the properties of a file or folder. The new properties should be specified as individual named arguments to the method. Any existing properties that aren't listed as arguments will retain their previous values or be recalculated based on changes to other properties, as appropriate. You can also call the \code{update} method on the corresponding \code{ms_drive_item} object. } \section{Shared items}{ @@ -131,9 +131,15 @@ drv$create_share_link("myfile", password="Use-strong-passwords!") # file metadata (name, date created, etc) drv$get_item_properties("myfile") -# rename a file +# rename a file -- item ID remains the same, while name is changed +obj <- drv$get_item("myfile") drv$set_item_properties("myfile", name="newname") +# retrieve the renamed object by ID +id <- obj$properties$id +obj2 <- drv$get_item(itemid=id) +obj$properties$id == obj2$properties$id # TRUE + # accessing shared files shared_df <- drv$list_shared_items() shared_df$remoteItem[[1]]$open() diff --git a/tests/testthat/test01_onedrive.R b/tests/testthat/test01_onedrive.R index 50e7fb4..876d490 100644 --- a/tests/testthat/test01_onedrive.R +++ b/tests/testthat/test01_onedrive.R @@ -31,8 +31,8 @@ test_that("OneDrive personal works", src <- "../resources/file.json" dest <- file.path(newfolder, basename(src)) newsrc <- tempfile() - expect_silent(od$upload_file(src, dest)) - expect_silent(od$download_file(dest, newsrc)) + expect_silent(od$upload_file(src, dest=dest)) + expect_silent(od$download_file(dest, dest=newsrc)) expect_true(files_identical(src, newsrc)) @@ -177,6 +177,20 @@ test_that("Nested folder creation/deletion works", }) +test_that("Get item by ID works", +{ + dir1 <- make_name(10) + src <- "../resources/file.json" + + obj <- od$upload_file(src, file.path(dir1, "file.json")) + id <- obj$properties$id + obj2 <- od$get_item(itemid=id) + expect_identical(obj$properties$id, obj2$properties$id) + + expect_silent(obj2$delete(confirm=FALSE)) +}) + + test_that("Folder upload/download works", { srcdir <- tempfile() diff --git a/tests/testthat/test02_business_onedrive.R b/tests/testthat/test02_business_onedrive.R index df9a89e..4253e0f 100644 --- a/tests/testthat/test02_business_onedrive.R +++ b/tests/testthat/test02_business_onedrive.R @@ -31,7 +31,7 @@ test_that("OneDrive for Business works", dest <- file.path(newfolder, basename(src)) newsrc <- tempfile() expect_silent(od$upload_file(src, dest)) - expect_silent(od$download_file(dest, newsrc)) + expect_silent(od$download_file(dest, dest=newsrc)) expect_true(files_identical(src, newsrc))