* team tests

* channel testing

* don't test replies yet

* also don't test deleting messages

* tests pass
This commit is contained in:
Hong Ooi 2021-02-12 04:37:29 +11:00 коммит произвёл GitHub
Родитель 0ae79a78cc
Коммит 7f17bc1779
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 203 добавлений и 40 удалений

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

@ -119,7 +119,7 @@ list_sharepoint_sites <- function(tenant=Sys.getenv("CLIMICROSOFT365_TENANT", "c
app <- choose_app(app)
login <- do_login(tenant, app, scopes, ...)
login$get_user$list_sharepoint_sites()
login$get_user()$list_sharepoint_sites()
}
#' @rdname client

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

@ -18,8 +18,8 @@
#' - `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.
#' - `list_files()`: List the files for 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.
#' - `upload_file()`: Uploads a file to the channel.
#' - `download_file()`: Downloads a file from the channel.
#'
@ -35,7 +35,7 @@
#' Note that message attachments are actually uploaded to the channel's file listing (a directory in the team's primary shared document folder). Support for attachments is somewhat experimental, so if you want to be sure that it works, upload the file separately using the `upload_file()` method.
#'
#' @seealso
#' [ms_team], [ms_chat_message]
#' [ms_team], [ms_drive], [ms_chat_message]
#'
#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
#' [Microsoft Teams API reference](https://docs.microsoft.com/en-us/graph/api/resources/teams-api-overview?view=graph-rest-1.0)
@ -114,7 +114,7 @@ public=list(
private$get_drive()$download_file(src, dest, ...)
},
upload_file=function(src, dest, ...)
upload_file=function(src, dest=basename(src), ...)
{
dest <- file.path(self$properties$displayName, dest)
private$get_drive()$upload_file(src, dest, ...)

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

@ -1,6 +1,6 @@
#' Teams chat message
#'
#' Class representing a message in a Teams channel or chat.
#' Class representing a message in a Teams channel. Currently Microsoft365R only supports channels, not chats between individuals.
#'
#' @docType class
#' @section Fields:
@ -10,14 +10,14 @@
#' - `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.
#' - `update(...)`: Update the item's properties (metadata) in Microsoft Graph. To update the list _data_, update the `fields` property. See the examples below.
#' - `do_operation(...)`: Carry out an arbitrary operation on the item.
#' - `sync_fields()`: Synchronise the R object with the item metadata in Microsoft Graph.
#' - `delete(confirm=TRUE)`: Delete this message. Currently the Graph API does not support deleting Teams messages, so this method is disabled.
#' - `update(...)`: Update the message's properties (metadata) in Microsoft Graph.
#' - `do_operation(...)`: Carry out an arbitrary operation on the message.
#' - `sync_fields()`: Synchronise the R object with the message metadata in Microsoft Graph.
#' - `send_reply(body, content_type, attachments)`: Sends a reply to the message. See below.
#' - `list_replies(n=50)`: List the replies to this message. By default, this is limited to the 50 most recent replies; set the `n` argument to change this.
#' - `get_reply(message_id)`: Retrieves a specific reply to the message.
#' - `delete_reply(message_id, confirm=TRUE)`: Deletes a reply to the message. By default, ask for confirmation first.
#' - `delete_reply(message_id, confirm=TRUE)`: Deletes a reply to the message. Currently the Graph API does not support deleting Teams messages, so this method is disabled.
#'
#' @section Initialization:
#' Creating new objects of this class should be done via the `get_message` and `list_messages` method 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 message.
@ -28,9 +28,9 @@
#' - `content_type`: Either "text" (the default) or "html".
#' - `attachments`: Optional vector of filenames.
#'
#' Teams channels don't support nested replies, so replying to a reply will fail.
#' Teams channels don't support nested replies, so any methods dealing with replies will fail if the message object is itself a reply.
#'
#' Note that message attachments are actually uploaded to the channel's file listing (a directory in the team's primary shared document folder). Support for attachments is somewhat experimental, so if you want to be sure that it works, upload the file separately using the `upload_file()` method.
#' Note that message attachments are actually uploaded to the channel's file listing (a directory in the team's primary shared document folder). Support for attachments is somewhat experimental, so if you want to be sure that it works, upload the file separately using the channel's `upload_file()` method.
#'
#' @seealso
#' [ms_team], [ms_channel]
@ -60,6 +60,8 @@ public=list(
self$type <- "Teams message"
parent <- properties$channelIdentity
private$api_type <- file.path("teams", parent[[1]], "channels", parent[[2]], "messages")
if(!is.null(properties$replyToId))
private$api_type <- file.path(private$api_type, properties$replyToId, "replies")
super$initialize(token, tenant, properties)
},
@ -88,9 +90,15 @@ public=list(
delete_reply=function(message_id, confirm=TRUE)
{
private$assert_not_nested_reply()
self$get_reply(message_id)$delete(confirm=confirm)
},
delete=function(confirm=TRUE)
{
stop("Deleting Teams messages is not currently supported", call.=FALSE)
},
print=function(...)
{
parent <- self$properties$channelIdentity

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

@ -14,7 +14,7 @@
#' - `update(...)`: Update the team metadata in Microsoft Graph.
#' - `do_operation(...)`: Carry out an arbitrary operation on the team.
#' - `sync_fields()`: Synchronise the R object with the team metadata in Microsoft Graph.
#' - `list_channels()`: List the channels for this team.
#' - `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.
#' - `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.
@ -52,9 +52,10 @@ public=list(
super$initialize(token, tenant, properties)
},
list_channels=function()
list_channels=function(filter=NULL)
{
res <- private$get_paged_list(self$do_operation("channels"))
opts <- if(!is.null(filter)) list(`$filter`=filter)
res <- private$get_paged_list(self$do_operation("channels", options=opts))
private$init_list_objects(res, "channel", team_id=self$properties$id)
},
@ -62,11 +63,11 @@ public=list(
{
if(!is.null(channel_name) && is.null(channel_id))
{
channels <- self$list_channels()
n <- which(sapply(channels, function(ch) ch$properties$displayName == channel_name))
if(length(n) != 1)
filter <- sprintf("displayName eq '%s'", channel_name)
channels <- self$list_channels(filter=filter)
if(length(channels) != 1)
stop("Invalid channel name", call.=FALSE)
return(channels[[n]])
return(channels[[1]])
}
op <- if(is.null(channel_name) && is.null(channel_id))
"primaryChannel"

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

@ -32,8 +32,8 @@ 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.
\item \code{list_files()}: List the files for 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{upload_file()}: Uploads a file to the channel.
\item \code{download_file()}: Downloads a file from the channel.
}
@ -79,7 +79,7 @@ chan$upload_file("mydocument.docx")
}
}
\seealso{
\link{ms_team}, \link{ms_chat_message}
\link{ms_team}, \link{ms_drive}, \link{ms_chat_message}
\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview},
\href{https://docs.microsoft.com/en-us/graph/api/resources/teams-api-overview?view=graph-rest-1.0}{Microsoft Teams API reference}

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

@ -8,7 +8,7 @@
An R6 object of class \code{ms_chat_message}, inheriting from \code{ms_object}.
}
\description{
Class representing a message in a Teams channel or chat.
Class representing a message in a Teams channel. Currently Microsoft365R only supports channels, not chats between individuals.
}
\section{Fields}{
@ -24,14 +24,14 @@ Class representing a message in a Teams channel or chat.
\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{update(...)}: Update the item's properties (metadata) in Microsoft Graph. To update the list \emph{data}, update the \code{fields} property. See the examples below.
\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{delete(confirm=TRUE)}: Delete this message. Currently the Graph API does not support deleting Teams messages, so this method is disabled.
\item \code{update(...)}: Update the message's properties (metadata) in Microsoft Graph.
\item \code{do_operation(...)}: Carry out an arbitrary operation on the message.
\item \code{sync_fields()}: Synchronise the R object with the message metadata in Microsoft Graph.
\item \code{send_reply(body, content_type, attachments)}: Sends a reply to the message. See below.
\item \code{list_replies(n=50)}: List the replies to this message. By default, this is limited to the 50 most recent replies; set the \code{n} argument to change this.
\item \code{get_reply(message_id)}: Retrieves a specific reply to the message.
\item \code{delete_reply(message_id, confirm=TRUE)}: Deletes a reply to the message. By default, ask for confirmation first.
\item \code{delete_reply(message_id, confirm=TRUE)}: Deletes a reply to the message. Currently the Graph API does not support deleting Teams messages, so this method is disabled.
}
}
@ -49,9 +49,9 @@ To reply to a message, use the \code{send_reply()} method. This has arguments:
\item \code{attachments}: Optional vector of filenames.
}
Teams channels don't support nested replies, so replying to a reply will fail.
Teams channels don't support nested replies, so any methods dealing with replies will fail if the message object is itself a reply.
Note that message attachments are actually uploaded to the channel's file listing (a directory in the team's primary shared document folder). Support for attachments is somewhat experimental, so if you want to be sure that it works, upload the file separately using the \code{upload_file()} method.
Note that message attachments are actually uploaded to the channel's file listing (a directory in the team's primary shared document folder). Support for attachments is somewhat experimental, so if you want to be sure that it works, upload the file separately using the channel's \code{upload_file()} method.
}
\examples{

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

@ -28,7 +28,7 @@ Class representing a team in Microsoft Teams.
\item \code{update(...)}: Update the team metadata in Microsoft Graph.
\item \code{do_operation(...)}: Carry out an arbitrary operation on the team.
\item \code{sync_fields()}: Synchronise the R object with the team metadata in Microsoft Graph.
\item \code{list_channels()}: List the channels for this team.
\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{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.

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

@ -26,29 +26,30 @@ test_that("SharePoint client works",
expect_error(get_sharepoint_site(site_name=site_name, site_url=site_url, site_id=site_id,
tenant=tenant, app=app))
site1 <- get_sharepoint_site(site_name=site_name, tenant=tenant, app=app)
site1 <- try(get_sharepoint_site(site_name=site_name, tenant=tenant, app=app), silent=TRUE)
if(inherits(site1, "try-error"))
skip("SharePoint tests skipped: service not available")
expect_is(site1, "ms_site")
expect_identical(site1$properties$displayName, site_name)
site2 <- get_sharepoint_site(site_url=site_url, tenant=tenant, app=app)
expect_is(site1, "ms_site")
expect_is(site2, "ms_site")
expect_identical(site1$properties$webUrl, site_url)
site3 <- get_sharepoint_site(site_id=site_id, tenant=tenant, app=app)
expect_is(site1, "ms_site")
expect_is(site3, "ms_site")
expect_identical(site1$properties$id, site_id)
expect_identical(site1$properties, site2$properties)
expect_identical(site2$properties, site3$properties)
sites <- list_sharepoint_sites()
expect_is(sites, "list")
expect_true(all(sapply(sites, inherits, "ms_site")))
})
test_that("SharePoint methods work",
{
gr <- AzureGraph::ms_graph$new(token=tok)
testsite <- try(gr$call_graph_endpoint(file.path("sites", site_id)), silent=TRUE)
if(inherits(testsite, "try-error"))
skip("SharePoint tests skipped: service not available")
site <- get_sharepoint_site(site_name, tenant=tenant, app=app)
expect_is(site, "ms_site")

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

@ -0,0 +1,77 @@
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 == "")
skip("Teams tests skipped: Microsoft Graph credentials not set")
if(!interactive())
skip("Teams tests skipped: must be in interactive session")
tok <- try(AzureAuth::get_azure_token(
c("https://graph.microsoft.com/.default",
"openid",
"offline_access"),
tenant=tenant, app=app, version=2),
silent=TRUE)
if(inherits(tok, "try-error"))
skip("Teams tests skipped: no access to tenant")
test_that("Teams client works",
{
expect_error(get_team(team_name=team_name, team_id=team_id, tenant=tenant, app=app))
team1 <- try(get_team(team_name=team_name, tenant=tenant, app=app), silent=TRUE)
if(inherits(team1, "try-error"))
skip("SharePoint tests skipped: service not available")
expect_is(team1, "ms_team")
expect_identical(team1$properties$displayName, team_name)
team2 <- get_team(team_id=team_id, tenant=tenant, app=app)
expect_is(team2, "ms_team")
expect_identical(team1$properties$id, team_id)
teams <- list_teams()
expect_is(teams, "list")
expect_true(all(sapply(teams, inherits, "ms_team")))
})
test_that("Teams methods work",
{
team <- get_team(team_name, tenant=tenant, app=app)
expect_is(team, "ms_team")
# drive -- functionality tested in test02
drives <- team$list_drives()
expect_is(drives, "list")
expect_true(all(sapply(drives, inherits, "ms_drive")))
drv <- team$get_drive()
expect_is(drv, "ms_drive")
grp <- team$get_group()
expect_is(grp, "az_group")
site <- team$get_sharepoint_site()
expect_is(site, "ms_site")
# channels
chans <- team$list_channels()
expect_is(chans, "list")
expect_true(all(sapply(chans, inherits, "ms_channel")))
expect_error(team$get_channel(channel_name, channel_id))
chan0 <- team$get_channel()
expect_is(chan0, "ms_channel")
chan1 <- team$get_channel(channel_name=channel_name)
expect_is(chan1, "ms_channel")
chan2 <- team$get_channel(channel_id=channel_id)
expect_is(chan2, "ms_channel")
})

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

@ -0,0 +1,76 @@
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 == "")
skip("Channel tests skipped: Microsoft Graph credentials not set")
if(!interactive())
skip("Channel tests skipped: must be in interactive session")
tok <- try(AzureAuth::get_azure_token(
c("https://graph.microsoft.com/.default",
"openid",
"offline_access"),
tenant=tenant, app=app, version=2),
silent=TRUE)
if(inherits(tok, "try-error"))
skip("Channel tests skipped: no access to tenant")
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)
expect_is(chan, "ms_channel")
lst <- chan$list_messages()
expect_is(lst, "list")
expect_true(all(sapply(lst, inherits, "ms_chat_message")))
msg_body <- sprintf("Test message: %s", make_name(5))
msg <- chan$send_message(msg_body)
expect_is(msg, "ms_chat_message")
msg2_body <- sprintf("<div>Test message: %s</div", make_name(5))
msg2 <- chan$send_message(msg2_body, content_type="html")
expect_is(msg2, "ms_chat_message")
msg3_body <- sprintf("Test message with attachment: %s", make_name(5))
f0 <- write_file()
msg3 <- chan$send_message(msg3_body, attachments=f0)
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")
# expect_error(repl$send_reply("Reply to reply"))
# expect_silent(msg$delete(confirm=FALSE))
# expect_silent(chan$delete_message(msg2$properties$id, confirm=FALSE))
# expect_silent(chan$delete_message(msg3$properties$id, confirm=FALSE))
f1 <- write_file()
it <- chan$upload_file(f1)
expect_is(it, "ms_drive_item")
flist <- chan$list_files(info="name")
expect_true(basename(f0) %in% flist)
expect_true(basename(f1) %in% flist)
f_dl <- tempfile()
expect_silent(chan$download_file(basename(f1), f_dl))
expect_true(files_identical(f1, f_dl))
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))
})