rstudio / pins-r

Pin, discover, and share resources
https://pins.rstudio.com
Other
316 stars 62 forks source link

Onedrive/Sharepoint board #513

Closed hongooi73 closed 3 years ago

hongooi73 commented 3 years ago

Implements boards in Onedrive/Sharepoint Online, using Microsoft365R as the backend. These use the same underlying API.

There are 3 top-level functions board_onedrive, board_sharepoint and board_ms365. All of these are the same, I've duplicated them because "onedrive" and "sharepoint" are the well-known names whereas "ms365" is rather anonymous. The lower-level code uses "ms365" as the prefix.

Pass an object of class ms_drive (a personal or business Onedrive, or a Sharepoint document library) to create a board:

library(Microsoft365R)
od <- get_personal_onedrive()
board_od <- board_onedrive(od, "path/to/boardname1")

odb <- get_business_onedrive()
board_odb <- board_onedrive(od, "path/to/boardname2")

doclib <- get_sharepoint_site("mysitename")$get_drive("doclibraryname")
board_sp <- board_sharepoint(doclib, "path/to/boardname3")

Basic uploading, downloading and metadata seems to be functional, will try to complete this over the next week.

hongooi73 commented 3 years ago

Deleting files in OD/SPO sometimes has to be done item-by-item, rather than simply deleting the entire folder. There is an arg by_item which can be passed to pin_delete/pin_version_delete, but I'm not sure where to document it. I've added a note in the body for ?pin_delete and ?pin_version_delete for this.

Similarly I added a ... arg to pin_versions_prune so that by_item can be passed in.

hongooi73 commented 3 years ago

Tests pass locally. How I've set it up is:

  1. Create a drive object eg with get_personal_onedrive() or get_business_onedrive()
  2. Save it somewhere
  3. Put the location of the file in the env var PINS_MS365_TEST_DRIVE

This probably isn't the best way to do it in a pipeline, but was easiest to code up. Also, the drive has to allow deleting non-empty folders as noted above. A personal OneDrive should be ok; a SharePoint site or business OneDrive may have doc protection policies.

hongooi73 commented 3 years ago

There are a number of ways to get access to a OneDrive/Sharepoint doc library:

hongooi73 commented 3 years ago

A more "industrial-strength" testing solution might be something like the following:

tenant <- Sys.getenv("PINS_MS365_AAD_TENANT")
app <- Sys.getenv("PINS_MS365_CLIENT_ID")
pwd <- Sys.getenv("PINS_MS365_CLIENT_SECRET")
drive_id <- Sys.getenv("PINS_MS365_DRIVE_ID")

# load MS365R methods (requires >= version 2.31)
loadNamespace("Microsoft365R")

# create a Microsoft Graph client
gr <- AzureGraph::create_graph_login(tenant=tenant, app=app, password=pwd, auth_type="client_credentials")

# get the drive directly without going through a user account or Sharepoint site
drv <- gr$get_drive(drive_id)

board_ms365(drv, path = "pin_testing", cache = tempfile(), ...)

But that requires some work on the backend to create an app registration, give it the necessary permissions to talk to Microsoft Graph, and get the ID of the drive.

hongooi73 commented 3 years ago

Done. Also simplified deletion a bit more.

Thanks for making this package, and for making it so easy to extend!

hongooi73 commented 3 years ago

@hadley I just realised I might need to add some logic to properly handle boards in OneDrive. The problem is that OD is only accessible to yourself; while you can share files and folders with others, this has to be handled as a special case.

How do you handle this scenario with Dropbox and Google Drive? Do you just assume such boards will only have one user?

hadley commented 3 years ago

@hongooi73 I just assume that the user takes care of that, using the appropriate sharing UI.

hongooi73 commented 3 years ago

So, a couple of things:

First, I realised that the simple testing setup above, where we copy a token/drive object around, will only work for 90 days max. After that, the token will expire and since we never save a new refresh token, the expiry is permanent. I don't think there's any easy way to avoid the industrial-strength solution. 🤔

2nd, I've found a way to share a board in OneDrive, but...

Thoughts? I was thinking that I should leave the docs out but keep the functionality in, since sharing in OneDrive won't actually work right now. Note that Sharepoint is fine, since all users should have access to the board.

hadley commented 3 years ago

I was thinking that I should leave the docs out but keep the functionality in, since sharing in OneDrive won't actually work right now.

Yeah that sounds good to me.

hongooi73 commented 3 years ago

Actually, it turns out that a shared OneDrive folder WILL work after all, I just need to sync the data up.

The PR should be ready to go. The interface is a bit inelegant; if you supply a drive item (R6 object) as the path, the drive arg is not used. In other languages that support function overloading, you could have 2 overloads: board_ms365(drive, path) and board_ms365(drive_item), but I'm not sure about the best way to do it here.

hadley commented 3 years ago

Thanks for all your work on this! I'm still on track for a CRAN release tomorrow.

github-actions[bot] commented 2 years ago

This pull request has been automatically locked. If you believe you have found a related problem, please file a new issue (with a reprex: https://reprex.tidyverse.org) and link to this issue.