- Add channel create/delete methods
- Move drive file/folder methods to the `ms_drive_item` class
- Fix `assert_one_arg` missing in added AzureGraph methods
- Restore previous client functions but mark as deprecated
- Pass site and list ID explicitly to `ms_list_item$new()` instead of guessing from properties
- Add folder method for channel
- Mark message deletion as unimplemented
- Add business OneDrive tests
This commit is contained in:
Hong Ooi 2021-02-17 11:10:46 +11:00 коммит произвёл GitHub
Родитель 7f17bc1779
Коммит 17098574dd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 593 добавлений и 216 удалений

Просмотреть файл

@ -1,5 +1,6 @@
# Generated by roxygen2: do not edit by hand
export(business_onedrive)
export(get_business_onedrive)
export(get_personal_onedrive)
export(get_sharepoint_site)
@ -14,4 +15,6 @@ export(ms_list)
export(ms_list_item)
export(ms_site)
export(ms_team)
export(personal_onedrive)
export(sharepoint_site)
import(AzureGraph)

10
NEWS.md
Просмотреть файл

@ -4,16 +4,20 @@
- Add support for Teams:
- Add `list_teams()` and `get_team()` client functions. You can get a team by team name or ID.
- List channels
- Get, list, create and delete channels
- List messages and replies
- Send messages to channels, send replies to messages
- Upload and download files
- Rename the client functions to allow for listing teams and sites:
- In this version only Teams channels are supported; chats between individuals may come later.
- Move implementations for file and folder methods to the `ms_drive_item` class.
- This facilitates managing files for Teams channels, which have associated folders in a shared document library (drive)
- The existing methods for the `ms_drive` class now call down to the `ms_drive_item` methods, with appropriate arguments; their behaviour should be unchanged
- Rename the client functions to allow for listing teams and sites. The original clients are still available, but are deprecated and simply redirect to the new functions. They will be removed in a future version of the package.
- `get_sharepoint_site()` is now `get_sharepoint_site()`
- `get_personal_onedrive()` is now `get_personal_onedrive()`
- `get_business_onedrive()` is now `get_business_onedrive()`
- The first argument to `get_sharepoint_site()` is `site_name` to get a site by name, for consistency with `get_team()`. To get a site by URL, specify the `site_url` argument explicitly: `get_sharepoint_site(site_url="https://my-site-url")`.
- Add `list_sharepoint_sites()` function to list all sites you follow.
- Add `list_sharepoint_sites()` function to list the sites you follow.
## Other changes

Просмотреть файл

@ -94,6 +94,7 @@ add_graph_methods <- function()
ms_graph$set("public", "get_sharepoint_site", overwrite=TRUE,
function(site_url=NULL, site_id=NULL)
{
assert_one_arg <- get("assert_one_arg", getNamespace("Microsoft365R"))
assert_one_arg(site_url, site_id, msg="Supply exactly one of site URL or ID")
op <- if(!is.null(site_url))
{

44
R/client_deprecated.R Normal file
Просмотреть файл

@ -0,0 +1,44 @@
#' Deprecated client functions
#'
#' @param tenant For `business_onedrive` and `sharepoint_site`, the name of your Azure Active Directory (AAD) tenant. If not supplied, use the value of the `CLIMICROSOFT365_TENANT` environment variable, or "common" if that is unset.
#' @param app A custom app registration ID to use for authentication. For `personal_onedrive`, the default is to use Microsoft365R's internal app ID. For `business_onedrive` and `sharepoint_site`, see below.
#' @param scopes The Microsoft Graph scopes (permissions) to obtain.
#' @param site_url,site_id For `sharepoint_site`, the web URL and ID of the SharePoint site to retrieve. Supply one or the other, but not both.
#' @param ... Optional arguments to be passed to `AzureGraph::create_graph_login`.
#' @details
#' These functions have been replaced by [get_personal_onedrive], [get_business_onedrive] and [get_sharepoint_site]. They will be removed in a later version of the package.
#' @rdname Microsoft365R-deprecated
#' @aliases client-deprecated
#' @export
personal_onedrive <- function(app=.microsoft365r_app_id,
scopes=c("Files.ReadWrite.All", "User.Read"),
...)
{
.Deprecated("get_personal_onedrive")
get_personal_onedrive(app=app, scopes=scopes, ...)
}
#' @rdname Microsoft365R-deprecated
#' @export
business_onedrive <- function(tenant=Sys.getenv("CLIMICROSOFT365_TENANT", "common"),
app=Sys.getenv("CLIMICROSOFT365_AADAPPID"),
scopes=".default",
...)
{
.Deprecated("get_business_onedrive")
get_business_onedrive(tenant=tenant, app=app, scopes=scopes, ...)
}
#' @rdname Microsoft365R-deprecated
#' @export
sharepoint_site <- function(site_url=NULL, site_id=NULL,
tenant=Sys.getenv("CLIMICROSOFT365_TENANT", "common"),
app=Sys.getenv("CLIMICROSOFT365_AADAPPID"),
scopes=".default",
...)
{
.Deprecated("get_sharepoint_site")
get_sharepoint_site(site_url=site_url, site_id=site_id, tenant=tenant, app=app, scopes=scopes, ...)
}

Просмотреть файл

@ -18,10 +18,11 @@
#' - `send_message(body, content_type, attachments)`: Sends a new message to the channel. See below.
#' - `list_messages(n=50)`: Retrieves the messages in the channel. By default, this is limited to the 50 most recent messages; set the `n` argument to change this.
#' - `get_message(message_id)`: Retrieves a specific message in the channel.
#' - `delete_message(message_id, confirm=TRUE)`: Deletes a message. By default, ask for confirmation first. You can only delete your own messages.
#' - `list_files()`: List the files for the channel. See [ms_drive] for the arguments available.
#' - `delete_message(message_id, confirm=TRUE)`: Deletes a message. Currently the Graph API does not support deleting Teams messages, so this method is disabled.
#' - `list_files()`: List the files for the channel. See [ms_drive] for the arguments available for this and the file upload/download methods.
#' - `upload_file()`: Uploads a file to the channel.
#' - `download_file()`: Downloads a file from the channel.
#' - `get_folder()`: Retrieves the files folder for the channel, as a [ms_drive_item] object
#'
#' @section Initialization:
#' Creating new objects of this class should be done via the `get_channel` and `list_channels` methods of the [ms_team] class. 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 channel.
@ -60,6 +61,8 @@
#'
#' chan$upload_file("mydocument.docx")
#'
#' chan$list_files()
#'
#' }
#' @format An R6 object of class `ms_channel`, inheriting from `ms_object`.
#' @export
@ -71,6 +74,8 @@ public=list(
initialize=function(token, tenant=NULL, properties=NULL, team_id=NULL)
{
if(is.null(team_id))
stop("Missing team ID", call.=FALSE)
self$type <- "channel"
self$team_id <- team_id
private$api_type <- file.path("teams", self$team_id, "channels")
@ -104,20 +109,24 @@ public=list(
list_files=function(path="", ...)
{
path <- sub("/$", "", file.path(self$properties$displayName, path))
private$get_drive()$list_files(path, ...)
self$get_folder()$list_files(path, ...)
},
download_file=function(src, dest=basename(src), ...)
{
src <- file.path(self$properties$displayName, src)
private$get_drive()$download_file(src, dest, ...)
self$get_folder()$get_item(src)$download(dest, ...)
},
upload_file=function(src, dest=basename(src), ...)
{
dest <- file.path(self$properties$displayName, dest)
private$get_drive()$upload_file(src, dest, ...)
self$get_folder()$upload(src, dest, ...)
},
get_folder=function()
{
if(is.null(private$folder))
private$folder <- ms_drive_item$new(self$token, self$tenant, self$do_operation("filesFolder"))
private$folder
},
print=function(...)
@ -133,22 +142,5 @@ public=list(
private=list(
get_drive=function(drive_id=NULL)
{
op <- if(is.null(drive_id))
"drive"
else file.path("drives", drive_id)
ms_drive$new(self$token, self$tenant, private$do_group_operation(op))
},
get_group=function()
{
az_group$new(self$token, self$tenant, private$do_group_operation())
},
do_group_operation=function(op="", ...)
{
op <- sub("/$", "", file.path("groups", self$team_id, op))
call_graph_endpoint(self$token, op, ...)
}
folder=NULL
))

Просмотреть файл

@ -29,38 +29,27 @@
#' 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.
#' 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.
#'
#' `list_items(path, info, full_names, pagesize)` lists the items under the specified path. It is the analogue of base R's `dir`/`list.files`. Its arguments are
#' - `path`: The path.
#' - `info`: The information to return: either "partial", "name" or "all". If "partial", a data frame is returned containing the name, size and whether the item is a file or folder. If "name", a vector of file/folder names is returned. If "all", a data frame is returned containing _all_ the properties for each item (this can be large).
#' - `full_names`: Whether to prefix the full path to the names of the items.
#' - `pagesize`: The number of results to return for each call to the REST endpoint. You can try reducing this argument below the default of 1000 if you are experiencing timeouts.
#' `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.
#'
#' `list_items(path, info, full_names, pagesize)` lists the items under the specified path.
#'
#' `list_files` is a synonym for `list_items`.
#'
#' `download_file` and `upload_file` download and upload files from the local machine to the drive. For `upload_file`, the uploading is done in blocks of 32MB by default; you can change this by setting the `blocksize` argument. For technical reasons, the block size [must be a multiple of 320KB](https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0#upload-bytes-to-the-upload-session).
#' `download_file` and `upload_file` transfer files between the local machine and the drive.
#'
#' `create_folder` creates a folder with the specified path. Trying to create an already existing folder is an error.
#'
#' `open_item` opens the given file or folder in your browser.
#'
#' `create_share_link(path, type, expiry, password, scope)` returns a shareable link to the item. Its arguments are
#' - `path`: The path.
#' - `type`: Either "view" for a read-only link, "edit" for a read-write link, or "embed" for a link that can be embedded in a web page. The last one is only available for personal OneDrive.
#' - `expiry`: How long the link is valid for. The default is 7 days; you can set an alternative like "15 minutes", "24 hours", "2 weeks", "3 months", etc. To leave out the expiry date, set this to NULL.
#' - `password`: An optional password to protect the link.
#' - `scope`: Optionally the scope of the link, either "anonymous" or "organization". The latter allows only users in your AAD tenant to access the link, and is only available for OneDrive for Business or SharePoint.
#'
#' This function returns a URL to access the item, for `type="view"` or "`type=edit"`. For `type="embed"`, it returns a list with components `webUrl` containing the URL, and `webHtml` containing a HTML fragment to embed the link in an IFRAME. The default is a viewable link, expiring in 7 days.
#' `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` returns an object of class [ms_drive_item], containing the properties (metadata) for a given file or folder and methods for working with it.
#' `get_item` retrieves the file or folder with the given path, 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.
#' `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.
#'
#' @seealso
#' [get_personal_onedrive], [get_business_onedrive], [ms_site], [ms_drive_item]
@ -82,8 +71,8 @@
#' drv <- site$get_drive()
#'
#' ## file/folder operationss
#' drv$list_items()
#' drv$list_items("path/to/folder", full_names=TRUE)
#' drv$list_files()
#' drv$list_files("path/to/folder", full_names=TRUE)
#'
#' # download a file -- default destination filename is taken from the source
#' drv$download_file("path/to/folder/data.csv")
@ -116,96 +105,17 @@ public=list(
list_items=function(path="/", info=c("partial", "name", "all"), full_names=FALSE, pagesize=1000)
{
info <- match.arg(info)
opts <- switch(info,
partial=list(`$select`="name,size,folder", `$top`=pagesize),
name=list(`$select`="name", `$top`=pagesize),
list(`$top`=pagesize)
)
children <- if(path != "/")
{
path <- curl::curl_escape(gsub("^/|/$", "", path)) # remove any leading and trailing slashes
self$do_operation(paste0("root:/", path, ":/children"), options=opts, simplify=TRUE)
}
else self$do_operation("root/children", options=opts, simplify=TRUE)
# get file list as a data frame
df <- private$get_paged_list(children, simplify=TRUE)
if(is_empty(df))
df <- data.frame(name=character(), size=numeric(), isdir=logical())
else if(info != "name")
{
df$isdir <- if(!is.null(df$folder))
!is.na(df$folder$childCount)
else rep(FALSE, nrow(df))
}
if(full_names)
df$name <- file.path(sub("^/", "", path), df$name)
switch(info,
partial=df[c("name", "size", "isdir")],
name=df$name,
all=
{
firstcols <- c("name", "size", "isdir")
df[c(firstcols, setdiff(names(df), firstcols))]
}
)
private$get_root()$list_items(path, info, full_names, pagesize)
},
upload_file=function(src, dest, blocksize=32768000)
{
dest <- curl::curl_escape(sub("^/", "", dest))
path <- paste0("root:/", dest, ":/createUploadSession")
con <- file(src, open="rb")
on.exit(close(con))
size <- file.size(src)
upload_dest <- self$do_operation(path, http_verb="POST")$uploadUrl
next_blockstart <- 0
next_blockend <- size - 1
repeat
{
next_blocksize <- min(next_blockend - next_blockstart + 1, blocksize)
seek(con, next_blockstart)
body <- readBin(con, "raw", next_blocksize)
thisblock <- length(body)
if(thisblock == 0)
break
headers <- httr::add_headers(
`Content-Length`=thisblock,
`Content-Range`=sprintf("bytes %.0f-%.0f/%.0f",
next_blockstart, next_blockstart + next_blocksize - 1, size)
)
res <- httr::PUT(upload_dest, headers, body=body)
httr::stop_for_status(res)
next_block <- parse_upload_range(httr::content(res), blocksize)
if(is.null(next_block))
break
next_blockstart <- next_block[1]
next_blockend <- next_block[2]
}
invisible(ms_drive_item$new(self$token, self$tenant, httr::content(res)))
private$get_root()$upload(src, dest, blocksize)
},
create_folder=function(path)
{
name <- basename(path)
parent <- dirname(path)
op <- if(parent %in% c(".", "/")) # assume root
"root/children"
else paste0("root:/", sub("^/", "", parent), ":/children")
body <- list(
name=name,
folder=named_list(),
`@microsoft.graph.conflictBehavior`="fail"
)
res <- self$do_operation(op, body=body, http_verb="POST")
ms_drive_item$new(self$token, self$tenant, res)
private$get_root()$create_folder(path)
},
download_file=function(src, dest=basename(src), overwrite=FALSE)
@ -267,6 +177,18 @@ public=list(
cat(format_public_methods(self))
invisible(self)
}
),
private=list(
root=NULL,
get_root=function()
{
if(is.null(private$root))
private$root <- self$get_item("/")
private$root
}
))

Просмотреть файл

@ -10,25 +10,54 @@
#' - `properties`: The item properties (metadata).
#' @section Methods:
#' - `new(...)`: Initialize a new object. Do not call this directly; see 'Initialization' below.
#' - `delete(confirm=TRUE)`: Delete this item. By default, ask for confirmation first.
#' - `delete(confirm=TRUE)`: Delete this item. By default, ask for confirmation first. For personal OneDrives, deleting a folder will also automatically delete its contents; for business OneDrives or SharePoint document libraries, you must delete the folder contents first before deleting the folder.
#' - `update(...)`: Update the item's properties (metadata) in Microsoft Graph.
#' - `do_operation(...)`: Carry out an arbitrary operation on the item.
#' - `sync_fields()`: Synchronise the R object with the item metadata in Microsoft Graph.
#' - `open()`: Open the item in your browser.
#' - `download(dest, overwrite)`: Download the file. Not applicable for a folder.
#' - `create_share_link(type, expiry, password, scope)`: Create a shareable link to the file or folder. See 'Sharing' below.
#' - `list_items(...), list_files(...)`: List the files and folders under the specified path.
#' - `download(dest, overwrite)`: Download the file. Only applicable for a file item.
#' - `create_share_link(type, expiry, password, scope)`: Create a shareable link to the file or folder.
#' - `upload(src, dest, blocksize)`: Upload a file. Only applicable for a folder item.
#' - `create_folder(path)`: Create a folder. Only applicable for a folder item.
#' - `get_item(path)`: Get a child item (file or folder) under this folder.
#' - `is_folder()`: Information function, returns TRUE if this item is a folder.
#'
#' @section Initialization:
#' Creating new objects of this class should be done via the `get_item` method of the [ms_drive] class. 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 item.
#'
#' @section Sharing:
#' `create_share_link(type, expiry, password, scope)` returns a shareable link to the item. Its arguments are
#' @section File and folder operations:
#' This class exposes methods for carrying out common operations on files and folders. Note that for the methods below, any paths to child items are relative to the folder's own path.
#'
#' `open` opens this file or folder in your browser. If the file has an unrecognised type, most browsers will attempt to download it.
#'
#' `list_items(path, info, full_names, pagesize)` lists the items under the specified path. It is the analogue of base R's `dir`/`list.files`. Its arguments are
#' - `path`: The path.
#' - `info`: The information to return: either "partial", "name" or "all". If "partial", a data frame is returned containing the name, size and whether the item is a file or folder. If "name", a vector of file/folder names is returned. If "all", a data frame is returned containing _all_ the properties for each item (this can be large).
#' - `full_names`: Whether to prefix the folder path to the names of the items.
#' - `pagesize`: The number of results to return for each call to the REST endpoint. You can try reducing this argument below the default of 1000 if you are experiencing timeouts.
#'
#' `list_files` is a synonym for `list_items`.
#'
#' `download` downloads the file item to the local machine. It is an error to try to download a folder item.
#'
#' `upload` uploads a file from the local machine into the folder item, and returns another `ms_drive_item` object representing the uploaded file. The uploading is done in blocks of 32MB by default; you can change this by setting the `blocksize` argument. For technical reasons, the block size [must be a multiple of 320KB](https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0#upload-bytes-to-the-upload-session). This returns an `ms_drive_item` object, invisibly.
#'
#' It is an error to try to upload to a file item, or to upload a source directory.
#'
#' `get_item` retrieves the file or folder with the given path, as another object of class `ms_drive_item`.
#'
#' `create_folder` creates a folder with the specified path. Trying to create an already existing folder is an error. This returns an `ms_drive_item` object, invisibly.
#'
#' `create_share_link(path, type, expiry, password, scope)` returns a shareable link to the item. Its arguments are
#' - `path`: The path.
#' - `type`: Either "view" for a read-only link, "edit" for a read-write link, or "embed" for a link that can be embedded in a web page. The last one is only available for personal OneDrive.
#' - `expiry`: How long the link is valid for. The default is 7 days; you can set an alternative like "15 minutes", "24 hours", "2 weeks", "3 months", etc. To leave out the expiry date, set this to NULL.
#' - `password`: An optional password to protect the link.
#' - `scope`: Optionally the scope of the link, either "anonymous" or "organization". The latter allows only users in your AAD tenant to access the link, and is only available for OneDrive for Business or SharePoint.
#'
#' This function returns a URL to access the item, for `type="view"` or "`type=edit"`. For `type="embed"`, it returns a list with components `webUrl` containing the URL, and `webHtml` containing a HTML fragment to embed the link in an IFRAME.
#' This method returns a URL to access the item, for `type="view"` or "`type=edit"`. For `type="embed"`, it returns a list with components `webUrl` containing the URL, and `webHtml` containing a HTML fragment to embed the link in an IFRAME. The default is a viewable link, expiring in 7 days.
#'
#' @seealso
#' [ms_graph], [ms_site], [ms_drive]
#'
@ -39,11 +68,14 @@
#' \dontrun{
#'
#' # personal OneDrive
#' gr2 <- get_graph_login("consumers")
#' me <- gr2$get_user()
#' mydrv <- me$get_drive()
#' mydrv <- get_personal_onedrive()
#'
#' myfile <- drv$get_item("myfile.docx")
#' docs <- mydrv$get_item("Documents")
#' docs$list_files()
#' docs$list_items()
#'
#' # this is the file 'Documents/myfile.docx'
#' myfile <- docs$get_item("myfile.docx")
#' myfile$properties
#'
#' # rename a file
@ -77,6 +109,11 @@ public=list(
super$initialize(token, tenant, properties)
},
is_folder=function()
{
!is.null(self$properties$folder)
},
open=function()
{
httr::BROWSE(self$properties$webUrl)
@ -104,8 +141,106 @@ public=list(
else res$link$webUrl
},
list_items=function(path="", info=c("partial", "name", "all"), full_names=FALSE, pagesize=1000)
{
private$assert_is_folder()
if(path == "/")
path <- ""
info <- match.arg(info)
opts <- switch(info,
partial=list(`$select`="name,size,folder", `$top`=pagesize),
name=list(`$select`="name", `$top`=pagesize),
list(`$top`=pagesize)
)
op <- sub("::", "", paste0(private$make_absolute_path(path), ":/children"))
children <- call_graph_endpoint(self$token, op, options=opts, simplify=TRUE)
# get file list as a data frame
df <- private$get_paged_list(children, simplify=TRUE)
if(is_empty(df))
df <- data.frame(name=character(), size=numeric(), isdir=logical())
else if(info != "name")
{
df$isdir <- if(!is.null(df$folder))
!is.na(df$folder$childCount)
else rep(FALSE, nrow(df))
}
if(full_names)
df$name <- file.path(sub("^/", "", path), df$name)
switch(info,
partial=df[c("name", "size", "isdir")],
name=df$name,
all=
{
firstcols <- c("name", "size", "isdir")
df[c(firstcols, setdiff(names(df), firstcols))]
}
)
},
get_item=function(path)
{
private$assert_is_folder()
op <- private$make_absolute_path(path)
ms_drive_item$new(self$token, self$tenant, call_graph_endpoint(self$token, op))
},
create_folder=function(path)
{
private$assert_is_folder()
body <- list(
name=enc2utf8(path),
folder=named_list(),
`@microsoft.graph.conflictBehavior`="fail"
)
res <- self$do_operation("children", body=body, http_verb="POST")
invisible(ms_drive_item$new(self$token, self$tenant, res))
},
upload=function(src, dest=basename(src), blocksize=32768000)
{
private$assert_is_folder()
con <- file(src, open="rb")
on.exit(close(con))
op <- paste0(private$make_absolute_path(dest), ":/createUploadSession")
upload_dest <- call_graph_endpoint(self$token, op, http_verb="POST")$uploadUrl
size <- file.size(src)
next_blockstart <- 0
next_blockend <- size - 1
repeat
{
next_blocksize <- min(next_blockend - next_blockstart + 1, blocksize)
seek(con, next_blockstart)
body <- readBin(con, "raw", next_blocksize)
thisblock <- length(body)
if(thisblock == 0)
break
headers <- httr::add_headers(
`Content-Length`=thisblock,
`Content-Range`=sprintf("bytes %.0f-%.0f/%.0f",
next_blockstart, next_blockstart + next_blocksize - 1, size)
)
res <- httr::PUT(upload_dest, headers, body=body)
httr::stop_for_status(res)
next_block <- parse_upload_range(httr::content(res), blocksize)
if(is.null(next_block))
break
next_blockstart <- next_block[1]
next_blockend <- next_block[2]
}
invisible(ms_drive_item$new(self$token, self$tenant, httr::content(res)))
},
download=function(dest=self$properties$name, overwrite=FALSE)
{
private$assert_is_file()
filepath <- file.path(self$parentReference$path, self$properties$name)
res <- self$do_operation("content", config=httr::write_disk(dest, overwrite=overwrite),
http_status_handler="pass")
@ -120,7 +255,7 @@ public=list(
print=function(...)
{
file_or_dir <- if(!is.null(self$properties$folder)) "file folder" else "file"
file_or_dir <- if(self$is_folder()) "file folder" else "file"
cat("<Drive item '", self$properties$name, "'>\n", sep="")
cat(" directory id:", self$properties$id, "\n")
cat(" web link:", self$properties$webUrl, "\n")
@ -129,4 +264,33 @@ public=list(
cat(format_public_methods(self))
invisible(self)
}
),
private=list(
make_absolute_path=function(dest)
{
parent <- self$properties$parentReference
name <- self$properties$name
op <- if(name == "root")
file.path("drives", parent$driveId, "root:")
else file.path(parent$path, name)
file.path(op, enc2utf8(dest))
},
assert_is_folder=function()
{
if(!self$is_folder())
stop("This method is only applicable for a folder item", call.=FALSE)
},
assert_is_file=function()
{
if(self$is_folder())
stop("This method is only applicable for a file item", call.=FALSE)
}
))
# alias for convenience
ms_drive_item$set("public", "list_files", overwrite=TRUE, ms_drive_item$public_methods$list_items)

Просмотреть файл

@ -88,7 +88,9 @@ public=list(
items <- self$do_operation("items", options=options, headers, simplify=as_data_frame)
df <- private$get_paged_list(items, simplify=as_data_frame)
if(!as_data_frame)
lapply(df, function(item) ms_list_item$new(self$token, self$tenant, item))
lapply(df, function(item) ms_list_item$new(self$token, self$tenant, item,
site_id=self$properties$parentReference$siteId,
list_id=self$properties$id))
else if(!all_metadata)
df$fields
else df
@ -98,13 +100,17 @@ public=list(
{
fields <- list(...)
res <- self$do_operation("items", body=list(fields=fields), http_verb="POST")
invisible(ms_list_item$new(self$token, self$tenant, res))
invisible(ms_list_item$new(self$token, self$tenant, res,
site_id=self$properties$parentReference$siteId,
list_id=self$properties$id))
},
get_item=function(id)
{
res <- self$do_operation(file.path("items", id))
ms_list_item$new(self$token, self$tenant, res)
ms_list_item$new(self$token, self$tenant, res,
site_id=self$properties$parentReference$siteId,
list_id=self$properties$id)
},
update_item=function(id, ...)

Просмотреть файл

@ -45,11 +45,17 @@ ms_list_item <- R6::R6Class("ms_list_item", inherit=ms_object,
public=list(
initialize=function(token, tenant=NULL, properties=NULL)
site_id=NULL,
list_id=NULL,
initialize=function(token, tenant=NULL, properties=NULL, site_id=NULL, list_id=NULL)
{
if(is.null(site_id) || is.null(list_id))
stop("Must supply parent site and list IDs", call.=FALSE)
self$type <- "list item"
context <- parse_listitem_context(properties[["fields@odata.context"]])
private$api_type <- file.path("sites", context$site_id, "lists", context$list_id, "items")
self$site_id <- site_id
self$list_id <- list_id
private$api_type <- file.path("sites", self$site_id, "lists", self$list_id, "items")
super$initialize(token, tenant, properties)
},
@ -64,14 +70,3 @@ public=list(
}
))
parse_listitem_context <- function(x)
{
if(is.null(x))
stop("Unable to initialize list item object: no OData context", call.=FALSE)
x <- sub("^.+#sites\\('", "", x)
sid <- utils::URLdecode(sub("'\\).+$", "", x))
x <- sub("^.+lists\\('", "", x)
lid <- sub("'\\).+", "", x)
list(site_id=sid, list_id=lid)
}

Просмотреть файл

@ -19,6 +19,7 @@
#' - `list_subsites()`: List the subsites of this site.
#' - `get_lists()`: Returns the lists that are part of this site.
#' - `get_list(list_name, list_id)`: Returns a specific list, either by name or ID.
#' - `get_group()`: Retrieve the Microsoft 365 group associated with the site, if it exists. A site that backs a private Teams channel will not have a group associated with it.
#'
#' @section Initialization:
#' Creating new objects of this class should be done via the `get_sharepoint_site` method of the [ms_graph] or [az_group] 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 site.
@ -78,16 +79,24 @@ public=list(
get_list=function(list_name=NULL, list_id=NULL)
{
op <- if(is.null(list_name) && !is.null(list_id))
assert_one_arg(list_name, list_id, msg="Supply exactly one of list name or ID")
op <- if(!is.null(list_id))
file.path("lists", list_id)
else if(!is.null(list_name) && is.null(list_id))
file.path("lists", curl::curl_escape(list_name))
else stop("Must supply either list name or ID")
else file.path("lists", curl::curl_escape(list_name))
res <- self$do_operation(op)
ms_list$new(self$token, self$tenant, res)
},
get_group=function()
{
filter <- sprintf("displayName eq '%s'", self$properties$displayName)
res <- call_graph_endpoint(self$token, "groups", options=list(`$filter`=filter))$value
if(length(res) != 1)
stop("Unable to get Microsoft 365 group", call.=FALSE)
az_group$new(self$token, self$tenant, res[[1]])
},
print=function(...)
{
cat("<Sharepoint site '", self$properties$displayName, "'>\n", sep="")

Просмотреть файл

@ -16,10 +16,12 @@
#' - `sync_fields()`: Synchronise the R object with the team metadata in Microsoft Graph.
#' - `list_channels(filter=NULL)`: List the channels for this team. Optionally, supply an OData expression to filter the list.
#' - `get_channel(channel_name, channel_id)`: Retrieve a channel. If the name and ID are not specified, returns the primary channel.
#' - `create_channel(channel_name, description, membership)`: Create a new channel. Optionally, you can specify a short text description of the channel, and the type of membership: either standard or private (invitation-only).
#' - `delete_channel(channel_name, channel_id, confirm=TRUE)`: Delete a channel; by default, ask for confirmation first. You cannot delete the primary channel of a team.
#' - `list_drives()`: List the drives (shared document libraries) associated with this team.
#' - `get_drive(drive_id)`: Retrieve a shared document library for this team. If the ID is not specified, this returns the default document library.
#' - `get_sharepoint_site()`: Get the SharePoint site associated with the team.
#' - `get_group()`: Get the Azure Active Directory group associated with the team.
#' - `get_group()`: Retrieve the Microsoft 365 group associated with the team.
#'
#' @section Initialization:
#' Creating new objects of this class should be done via the `get_team` and `list_teams` methods of the [ms_graph], [az_user] or [az_group] 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 team.
@ -38,6 +40,9 @@
#' myteam$get_channel()
#' myteam$get_drive()
#'
#' myteam$create_channel("Test channel", description="A channel for testing")
#' myteam$delete_channel("Test channel")
#'
#' }
#' @format An R6 object of class `ms_team`, inheriting from `ms_object`.
#' @export
@ -77,6 +82,24 @@ public=list(
ms_channel$new(self$token, self$tenant, self$do_operation(op), team_id=self$properties$id)
},
create_channel=function(channel_name, description="", membership=c("standard", "private"))
{
membership <- match.arg(membership)
body <- list(
displayName=channel_name,
description=description,
membershipType=membership
)
ms_channel$new(self$token, self$tenant, self$do_operation("channels", body=body, http_verb="POST"),
team_id=self$properties$id)
},
delete_channel=function(channel_name=NULL, channel_id=NULL, confirm=TRUE)
{
assert_one_arg(channel_name, channel_id, msg="Supply exactly one of channel name or ID")
self$get_channel(channel_name, channel_id)$delete(confirm=confirm)
},
list_drives=function()
{
res <- private$get_paged_list(private$do_group_operation("drives"))

Просмотреть файл

@ -0,0 +1,36 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/client_deprecated.R
\name{personal_onedrive}
\alias{personal_onedrive}
\alias{client-deprecated}
\alias{business_onedrive}
\alias{sharepoint_site}
\title{Deprecated client functions}
\usage{
personal_onedrive(app = .microsoft365r_app_id,
scopes = c("Files.ReadWrite.All", "User.Read"), ...)
business_onedrive(tenant = Sys.getenv("CLIMICROSOFT365_TENANT", "common"),
app = Sys.getenv("CLIMICROSOFT365_AADAPPID"), scopes = ".default", ...)
sharepoint_site(site_url = NULL, site_id = NULL,
tenant = Sys.getenv("CLIMICROSOFT365_TENANT", "common"),
app = Sys.getenv("CLIMICROSOFT365_AADAPPID"), scopes = ".default", ...)
}
\arguments{
\item{app}{A custom app registration ID to use for authentication. For \code{personal_onedrive}, the default is to use Microsoft365R's internal app ID. For \code{business_onedrive} and \code{sharepoint_site}, see below.}
\item{scopes}{The Microsoft Graph scopes (permissions) to obtain.}
\item{...}{Optional arguments to be passed to \code{AzureGraph::create_graph_login}.}
\item{tenant}{For \code{business_onedrive} and \code{sharepoint_site}, the name of your Azure Active Directory (AAD) tenant. If not supplied, use the value of the \code{CLIMICROSOFT365_TENANT} environment variable, or "common" if that is unset.}
\item{site_url, site_id}{For \code{sharepoint_site}, the web URL and ID of the SharePoint site to retrieve. Supply one or the other, but not both.}
}
\description{
Deprecated client functions
}
\details{
These functions have been replaced by \link{get_personal_onedrive}, \link{get_business_onedrive} and \link{get_sharepoint_site}. They will be removed in a later version of the package.
}

Просмотреть файл

@ -32,10 +32,11 @@ Class representing a Microsoft Teams channel.
\item \code{send_message(body, content_type, attachments)}: Sends a new message to the channel. See below.
\item \code{list_messages(n=50)}: Retrieves the messages in the channel. By default, this is limited to the 50 most recent messages; set the \code{n} argument to change this.
\item \code{get_message(message_id)}: Retrieves a specific message in the channel.
\item \code{delete_message(message_id, confirm=TRUE)}: Deletes a message. By default, ask for confirmation first. You can only delete your own messages.
\item \code{list_files()}: List the files for the channel. See \link{ms_drive} for the arguments available.
\item \code{delete_message(message_id, confirm=TRUE)}: Deletes a message. Currently the Graph API does not support deleting Teams messages, so this method is disabled.
\item \code{list_files()}: List the files for the channel. See \link{ms_drive} for the arguments available for this and the file upload/download methods.
\item \code{upload_file()}: Uploads a file to the channel.
\item \code{download_file()}: Downloads a file from the channel.
\item \code{get_folder()}: Retrieves the files folder for the channel, as a \link{ms_drive_item} object
}
}
@ -76,6 +77,8 @@ chan$send_message(msg_text, attachments="myfile.csv")
chan$upload_file("mydocument.docx")
chan$list_files()
}
}
\seealso{

Просмотреть файл

@ -48,42 +48,27 @@ 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.
This class exposes methods for carrying out common operations on files and folders. They call down to the corresponding methods for the \link{ms_drive_item} class. In this context, any paths to child items are relative to the root folder of the drive.
\code{list_items(path, info, full_names, pagesize)} lists the items under the specified path. It is the analogue of base R's \code{dir}/\code{list.files}. Its arguments are
\itemize{
\item \code{path}: The path.
\item \code{info}: The information to return: either "partial", "name" or "all". If "partial", a data frame is returned containing the name, size and whether the item is a file or folder. If "name", a vector of file/folder names is returned. If "all", a data frame is returned containing \emph{all} the properties for each item (this can be large).
\item \code{full_names}: Whether to prefix the full path to the names of the items.
\item \code{pagesize}: The number of results to return for each call to the REST endpoint. You can try reducing this argument below the default of 1000 if you are experiencing timeouts.
}
\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{list_items(path, info, full_names, pagesize)} lists the items under the specified path.
\code{list_files} is a synonym for \code{list_items}.
\code{download_file} and \code{upload_file} download and upload files from the local machine to the drive. For \code{upload_file}, the uploading is done in blocks of 32MB by default; you can change this by setting the \code{blocksize} argument. For technical reasons, the block size \href{https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0#upload-bytes-to-the-upload-session}{must be a multiple of 320KB}.
\code{download_file} and \code{upload_file} transfer files between the local machine and the drive.
\code{create_folder} creates a folder with the specified path. Trying to create an already existing folder is an error.
\code{open_item} opens the given file or folder in your browser.
\code{create_share_link(path, type, expiry, password, scope)} returns a shareable link to the item. Its arguments are
\itemize{
\item \code{path}: The path.
\item \code{type}: Either "view" for a read-only link, "edit" for a read-write link, or "embed" for a link that can be embedded in a web page. The last one is only available for personal OneDrive.
\item \code{expiry}: How long the link is valid for. The default is 7 days; you can set an alternative like "15 minutes", "24 hours", "2 weeks", "3 months", etc. To leave out the expiry date, set this to NULL.
\item \code{password}: An optional password to protect the link.
\item \code{scope}: Optionally the scope of the link, either "anonymous" or "organization". The latter allows only users in your AAD tenant to access the link, and is only available for OneDrive for Business or SharePoint.
}
This function returns a URL to access the item, for \code{type="view"} or "\verb{type=edit"}. For \code{type="embed"}, it returns a list with components \code{webUrl} containing the URL, and \code{webHtml} containing a HTML fragment to embed the link in an IFRAME. The default is a viewable link, expiring in 7 days.
\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} returns an object of class \link{ms_drive_item}, containing the properties (metadata) for a given file or folder and methods for working with it.
\code{get_item} retrieves the file or folder with the given path, as an object of class \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.
\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.
}
\examples{
@ -100,8 +85,8 @@ site <- get_sharepoint_site("My site")
drv <- site$get_drive()
## file/folder operationss
drv$list_items()
drv$list_items("path/to/folder", full_names=TRUE)
drv$list_files()
drv$list_files("path/to/folder", full_names=TRUE)
# download a file -- default destination filename is taken from the source
drv$download_file("path/to/folder/data.csv")

Просмотреть файл

@ -24,13 +24,18 @@ Class representing an item (file or folder) in a OneDrive or SharePoint document
\itemize{
\item \code{new(...)}: Initialize a new object. Do not call this directly; see 'Initialization' below.
\item \code{delete(confirm=TRUE)}: Delete this item. By default, ask for confirmation first.
\item \code{delete(confirm=TRUE)}: Delete this item. By default, ask for confirmation first. For personal OneDrives, deleting a folder will also automatically delete its contents; for business OneDrives or SharePoint document libraries, you must delete the folder contents first before deleting the folder.
\item \code{update(...)}: Update the item's properties (metadata) in Microsoft Graph.
\item \code{do_operation(...)}: Carry out an arbitrary operation on the item.
\item \code{sync_fields()}: Synchronise the R object with the item metadata in Microsoft Graph.
\item \code{open()}: Open the item in your browser.
\item \code{download(dest, overwrite)}: Download the file. Not applicable for a folder.
\item \code{create_share_link(type, expiry, password, scope)}: Create a shareable link to the file or folder. See 'Sharing' below.
\item \verb{list_items(...), list_files(...)}: List the files and folders under the specified path.
\item \code{download(dest, overwrite)}: Download the file. Only applicable for a file item.
\item \code{create_share_link(type, expiry, password, scope)}: Create a shareable link to the file or folder.
\item \code{upload(src, dest, blocksize)}: Upload a file. Only applicable for a folder item.
\item \code{create_folder(path)}: Create a folder. Only applicable for a folder item.
\item \code{get_item(path)}: Get a child item (file or folder) under this folder.
\item \code{is_folder()}: Information function, returns TRUE if this item is a folder.
}
}
@ -39,28 +44,56 @@ Class representing an item (file or folder) in a OneDrive or SharePoint document
Creating new objects of this class should be done via the \code{get_item} method of the \link{ms_drive} class. Calling the \code{new()} method for this class only constructs the R object; it does not call the Microsoft Graph API to retrieve or create the actual item.
}
\section{Sharing}{
\section{File and folder operations}{
\code{create_share_link(type, expiry, password, scope)} returns a shareable link to the item. Its arguments are
This class exposes methods for carrying out common operations on files and folders. Note that for the methods below, any paths to child items are relative to the folder's own path.
\code{open} opens this file or folder in your browser. If the file has an unrecognised type, most browsers will attempt to download it.
\code{list_items(path, info, full_names, pagesize)} lists the items under the specified path. It is the analogue of base R's \code{dir}/\code{list.files}. Its arguments are
\itemize{
\item \code{path}: The path.
\item \code{info}: The information to return: either "partial", "name" or "all". If "partial", a data frame is returned containing the name, size and whether the item is a file or folder. If "name", a vector of file/folder names is returned. If "all", a data frame is returned containing \emph{all} the properties for each item (this can be large).
\item \code{full_names}: Whether to prefix the folder path to the names of the items.
\item \code{pagesize}: The number of results to return for each call to the REST endpoint. You can try reducing this argument below the default of 1000 if you are experiencing timeouts.
}
\code{list_files} is a synonym for \code{list_items}.
\code{download} downloads the file item to the local machine. It is an error to try to download a folder item.
\code{upload} uploads a file from the local machine into the folder item, and returns another \code{ms_drive_item} object representing the uploaded file. The uploading is done in blocks of 32MB by default; you can change this by setting the \code{blocksize} argument. For technical reasons, the block size \href{https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0#upload-bytes-to-the-upload-session}{must be a multiple of 320KB}. This returns an \code{ms_drive_item} object, invisibly.
It is an error to try to upload to a file item, or to upload a source directory.
\code{get_item} retrieves the file or folder with the given path, as another object of class \code{ms_drive_item}.
\code{create_folder} creates a folder with the specified path. Trying to create an already existing folder is an error. This returns an \code{ms_drive_item} object, invisibly.
\code{create_share_link(path, type, expiry, password, scope)} returns a shareable link to the item. Its arguments are
\itemize{
\item \code{path}: The path.
\item \code{type}: Either "view" for a read-only link, "edit" for a read-write link, or "embed" for a link that can be embedded in a web page. The last one is only available for personal OneDrive.
\item \code{expiry}: How long the link is valid for. The default is 7 days; you can set an alternative like "15 minutes", "24 hours", "2 weeks", "3 months", etc. To leave out the expiry date, set this to NULL.
\item \code{password}: An optional password to protect the link.
\item \code{scope}: Optionally the scope of the link, either "anonymous" or "organization". The latter allows only users in your AAD tenant to access the link, and is only available for OneDrive for Business or SharePoint.
}
This function returns a URL to access the item, for \code{type="view"} or "\verb{type=edit"}. For \code{type="embed"}, it returns a list with components \code{webUrl} containing the URL, and \code{webHtml} containing a HTML fragment to embed the link in an IFRAME.
This method returns a URL to access the item, for \code{type="view"} or "\verb{type=edit"}. For \code{type="embed"}, it returns a list with components \code{webUrl} containing the URL, and \code{webHtml} containing a HTML fragment to embed the link in an IFRAME. The default is a viewable link, expiring in 7 days.
}
\examples{
\dontrun{
# personal OneDrive
gr2 <- get_graph_login("consumers")
me <- gr2$get_user()
mydrv <- me$get_drive()
mydrv <- get_personal_onedrive()
myfile <- drv$get_item("myfile.docx")
docs <- mydrv$get_item("Documents")
docs$list_files()
docs$list_items()
# this is the file 'Documents/myfile.docx'
myfile <- docs$get_item("myfile.docx")
myfile$properties
# rename a file

Просмотреть файл

@ -33,6 +33,7 @@ Class representing a SharePoint site.
\item \code{list_subsites()}: List the subsites of this site.
\item \code{get_lists()}: Returns the lists that are part of this site.
\item \code{get_list(list_name, list_id)}: Returns a specific list, either by name or ID.
\item \code{get_group()}: Retrieve the Microsoft 365 group associated with the site, if it exists. A site that backs a private Teams channel will not have a group associated with it.
}
}

Просмотреть файл

@ -30,10 +30,12 @@ Class representing a team in Microsoft Teams.
\item \code{sync_fields()}: Synchronise the R object with the team metadata in Microsoft Graph.
\item \code{list_channels(filter=NULL)}: List the channels for this team. Optionally, supply an OData expression to filter the list.
\item \code{get_channel(channel_name, channel_id)}: Retrieve a channel. If the name and ID are not specified, returns the primary channel.
\item \code{create_channel(channel_name, description, membership)}: Create a new channel. Optionally, you can specify a short text description of the channel, and the type of membership: either standard or private (invitation-only).
\item \code{delete_channel(channel_name, channel_id, confirm=TRUE)}: Delete a channel; by default, ask for confirmation first. You cannot delete the primary channel of a team.
\item \code{list_drives()}: List the drives (shared document libraries) associated with this team.
\item \code{get_drive(drive_id)}: Retrieve a shared document library for this team. If the ID is not specified, this returns the default document library.
\item \code{get_sharepoint_site()}: Get the SharePoint site associated with the team.
\item \code{get_group()}: Get the Azure Active Directory group associated with the team.
\item \code{get_group()}: Retrieve the Microsoft 365 group associated with the team.
}
}
@ -50,6 +52,9 @@ myteam$list_channels()
myteam$get_channel()
myteam$get_drive()
myteam$create_channel("Test channel", description="A channel for testing")
myteam$delete_channel("Test channel")
}
}
\seealso{

Просмотреть файл

@ -46,3 +46,71 @@ test_that("OneDrive personal works",
expect_silent(od$delete_item(newfolder, confirm=FALSE))
})
test_that("Drive item methods work",
{
od <- get_personal_onedrive()
root <- od$get_item("/")
expect_is(root, "ms_drive_item")
tmpname1 <- make_name(10)
folder1 <- root$create_folder(tmpname1)
expect_is(folder1, "ms_drive_item")
expect_true(folder1$is_folder())
tmpname2 <- make_name(10)
folder2 <- folder1$create_folder(tmpname2)
expect_is(folder2, "ms_drive_item")
expect_true(folder2$is_folder())
src <- write_file()
expect_silent(file1 <- root$upload(src))
expect_is(file1, "ms_drive_item")
expect_false(file1$is_folder())
expect_error(file1$create_folder("bad"))
file1_0 <- root$get_item(basename(src))
expect_is(file1_0, "ms_drive_item")
expect_false(file1_0$is_folder())
expect_identical(file1_0$properties$name, file1$properties$name)
dest1 <- tempfile()
expect_silent(file1$download(dest1))
expect_true(files_identical(src, dest1))
expect_silent(file2 <- folder1$upload(src))
expect_is(file2, "ms_drive_item")
dest2 <- tempfile()
expect_silent(file2$download(dest2))
expect_true(files_identical(src, dest2))
dest3 <- tempfile()
expect_silent(file3 <- folder2$upload(src, basename(dest3)))
expect_is(file3, "ms_drive_item")
expect_identical(file3$properties$name, basename(dest3))
expect_silent(file3$download(dest3))
expect_true(files_identical(src, dest3))
file3_1 <- folder1$get_item(file.path(tmpname2, basename(dest3)))
expect_is(file3_1, "ms_drive_item")
expect_identical(file3_1$properties$name, file3$properties$name)
lst0 <- root$list_files()
expect_is(lst0, "data.frame")
lst0_f <- root$list_files(info="name", full_names=TRUE)
expect_type(lst0_f, "character")
expect_true(all(substr(lst0_f, 1, 1) == "/"))
lst0_1 <- root$list_files(tmpname1)
lst1 <- folder1$list_files()
expect_identical(lst0_1, lst1)
lst1_f <- folder1$list_files(tmpname2, info="name", full_names=TRUE)
expect_type(lst1_f, "character")
expect_true(all(grepl(paste0("^", tmpname2), lst1_f)))
expect_silent(file1$delete(confirm=FALSE))
expect_silent(folder1$delete(confirm=FALSE))
})

Просмотреть файл

@ -58,3 +58,74 @@ test_that("OneDrive for Business works",
expect_silent(od$delete_item(newfolder, confirm=FALSE))
})
test_that("Drive item methods work",
{
od <- get_business_onedrive(tenant=tenant, app=app)
root <- od$get_item("/")
expect_is(root, "ms_drive_item")
tmpname1 <- make_name(10)
folder1 <- root$create_folder(tmpname1)
expect_is(folder1, "ms_drive_item")
expect_true(folder1$is_folder())
tmpname2 <- make_name(10)
folder2 <- folder1$create_folder(tmpname2)
expect_is(folder2, "ms_drive_item")
expect_true(folder2$is_folder())
src <- write_file()
expect_silent(file1 <- root$upload(src))
expect_is(file1, "ms_drive_item")
expect_false(file1$is_folder())
expect_error(file1$create_folder("bad"))
file1_0 <- root$get_item(basename(src))
expect_is(file1_0, "ms_drive_item")
expect_false(file1_0$is_folder())
expect_identical(file1_0$properties$name, file1$properties$name)
dest1 <- tempfile()
expect_silent(file1$download(dest1))
expect_true(files_identical(src, dest1))
expect_silent(file2 <- folder1$upload(src))
expect_is(file2, "ms_drive_item")
dest2 <- tempfile()
expect_silent(file2$download(dest2))
expect_true(files_identical(src, dest2))
dest3 <- tempfile()
expect_silent(file3 <- folder2$upload(src, basename(dest3)))
expect_is(file3, "ms_drive_item")
expect_identical(file3$properties$name, basename(dest3))
expect_silent(file3$download(dest3))
expect_true(files_identical(src, dest3))
file3_1 <- folder1$get_item(file.path(tmpname2, basename(dest3)))
expect_is(file3_1, "ms_drive_item")
expect_identical(file3_1$properties$name, file3$properties$name)
lst0 <- root$list_files()
expect_is(lst0, "data.frame")
lst0_f <- root$list_files(info="name", full_names=TRUE)
expect_type(lst0_f, "character")
expect_true(all(substr(lst0_f, 1, 1) == "/"))
lst0_1 <- root$list_files(tmpname1)
lst1 <- folder1$list_files()
expect_identical(lst0_1, lst1)
lst1_f <- folder1$list_files(tmpname2, info="name", full_names=TRUE)
expect_type(lst1_f, "character")
expect_true(all(grepl(paste0("^", tmpname2), lst1_f)))
expect_silent(file3$delete(confirm=FALSE))
expect_silent(folder2$delete(confirm=FALSE))
expect_silent(file2$delete(confirm=FALSE))
expect_silent(folder1$delete(confirm=FALSE))
expect_silent(file1$delete(confirm=FALSE))
})

Просмотреть файл

@ -61,6 +61,9 @@ test_that("SharePoint methods work",
drv <- site$get_drive()
expect_is(drv, "ms_drive")
grp <- site$get_group()
expect_is(grp, "az_group")
# list
lists <- site$get_lists()
expect_is(lists, "list")

Просмотреть файл

@ -2,12 +2,13 @@ tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_NATIVE_APP_ID")
team_name <- Sys.getenv("AZ_TEST_TEAM_NAME")
team_id <- Sys.getenv("AZ_TEST_TEAM_ID")
channel_name <- Sys.getenv("AZ_TEST_CHANNEL_NAME")
channel_id <- Sys.getenv("AZ_TEST_CHANNEL_ID")
if(tenant == "" || app == "" || team_name == "" || team_id == "" || channel_name == "" || channel_id == "")
if(tenant == "" || app == "" || team_name == "" || team_id == "")
skip("Channel tests skipped: Microsoft Graph credentials not set")
if(Sys.getenv("AZ_TEST_CHANNEL_FLAG") == "")
skip("Channel tests skipped: flag not set")
if(!interactive())
skip("Channel tests skipped: must be in interactive session")
@ -25,12 +26,19 @@ test_that("Channel methods work",
team <- get_team(team_id=team_id, tenant=tenant, app=app)
expect_is(team, "ms_team")
chan <- team$get_channel(channel_name=channel_name)
channel_name <- sprintf("Test channel %s", make_name(10))
expect_error(team$get_channel(channel_name=channel_name))
chan <- team$create_channel(channel_name, description="Temporary testing channel")
expect_is(chan, "ms_channel")
expect_false(inherits(chan$properties, "xml_document"))
folder <- chan$get_folder()
expect_is(folder, "ms_drive_item")
lst <- chan$list_messages()
expect_is(lst, "list")
expect_true(all(sapply(lst, inherits, "ms_chat_message")))
expect_identical(length(lst), 0L)
msg_body <- sprintf("Test message: %s", make_name(5))
msg <- chan$send_message(msg_body)
@ -46,11 +54,11 @@ test_that("Channel methods work",
expect_is(msg3, "ms_chat_message")
expect_true(nzchar(msg3$properties$attachments[[1]]$contentUrl))
# repl_body <- sprintf("Test reply: %s", make_name(5))
# repl <- msg$send_reply(repl_body)
# expect_is(repl, "ms_chat_message")
repl_body <- sprintf("Test reply: %s", make_name(5))
repl <- msg$send_reply(repl_body)
expect_is(repl, "ms_chat_message")
# expect_error(repl$send_reply("Reply to reply"))
expect_error(repl$send_reply("Reply to reply"))
# expect_silent(msg$delete(confirm=FALSE))
# expect_silent(chan$delete_message(msg2$properties$id, confirm=FALSE))
@ -68,9 +76,10 @@ test_that("Channel methods work",
expect_silent(chan$download_file(basename(f1), f_dl))
expect_true(files_identical(f1, f_dl))
expect_silent(chan$delete(confirm=FALSE))
drv <- team$get_drive()
itempath0 <- file.path(channel_name, basename(f0))
itempath1 <- file.path(channel_name, basename(f1))
expect_silent(drv$delete_item(itempath0, confirm=FALSE))
expect_silent(drv$delete_item(itempath1, confirm=FALSE))
flist2 <- drv$list_files(channel_name, info="name", full_names=TRUE)
lapply(flist2, function(f) drv$delete_item(f, confirm=FALSE))
drv$delete_item(channel_name, confirm=FALSE)
})