As written, AzureGraph provides support for Microsoft Graph objects derived from Azure Active Directory (AAD): users, groups, app registrations and service principals. This vignette describes how to extend it to support other services.
ms_object
base classAzureGraph provides the ms_object
class to represent a
generic object in Graph. You can extend this to support specific
services by adding custom methods and fields.
For example, the Microsoft365R package
extends AzureGraph to support SharePoint Online sites and OneDrive
filesystems (both personal and business). This is the
ms_site
class from that package, which represents a
SharePoint site. To save space, the actual code in the new methods has
been elided.
ms_site <- R6::R6Class("ms_site", inherit=ms_object,
public=list(
initialize=function(token, tenant=NULL, properties=NULL)
{
self$type <- "site"
private$api_type <- "sites"
super$initialize(token, tenant, properties)
},
list_drives=function() {}, # ...
get_drive=function(drive_id=NULL) {}, # ...
list_subsites=function() {}, # ...
get_list=function(list_name=NULL, list_id=NULL) {}, # ...
print=function(...)
{
cat("<Sharepoint site '", self$properties$displayName, "'>\n", sep="")
cat(" directory id:", self$properties$id, "\n")
cat(" web link:", self$properties$webUrl, "\n")
cat(" description:", self$properties$description, "\n")
cat("---\n")
cat(format_public_methods(self))
invisible(self)
}
))
Note the following:
The initialize()
method of your class should take 3
arguments: the OAuth2 token for authenticating with Graph, the name of
the AAD tenant, and the list of properties for this object as obtained
from the Graph endpoint. It should set 2 fields: self$type
contains a human-readable name for this type of object, and
private$api_type
contains the object type as it appears in
the URL of a Graph API request. It should then call the superclass
method to complete the initialisation. initialize()
itself
should not contact the Graph endpoint; it should merely create and
populate the R6 object given the response from a previous
request.
The print()
method is optional and should display
any properties that can help identify this object to a human
reader.
You can read the code of the existing classes such as
az_user
, az_app
etc to see how to call the
API. The do_operation()
method should suffice for any
regular communication with the Graph endpoint.
register_graph_class
Having defined your new class, call register_graph_class
so that AzureGraph becomes aware of it and can automatically use it to
populate object lists. If you are writing a new package, the
register_graph_class
call should go in your package’s
.onLoad
startup function. For example, registering the
ms_site
SharePoint class looks like this.
.onLoad <- function(libname, pkgname)
{
register_graph_class("site", ms_site,
function(props) grepl("sharepoint", props$id, fixed=TRUE))
# ... other startup code ...
}
register_graph_class
takes 3 arguments:
Finally, so that people can use the same workflow with your class as
with AzureGraph-supplied classes, you can add getter and setter methods
to ms_graph
and any other classes for which it’s
appropriate. Again, if you’re writing a package, this should happen in
the .onLoad
function.
In the case of ms_site
, it’s appropriate to add a getter
method not just to ms_graph
, but also the
ms_group
class. This is because SharePoint sites have
associated user groups, hence it’s useful to be able to retrieve a site
given the object for a group. The relevant code in the
.onLoad
function looks like this (slightly simplified):
.onLoad <- function(libname, pkgname)
{
# ...
ms_graph$set("public", "get_sharepoint_site", overwrite=TRUE,
function(site_url=NULL, site_id=NULL)
{
op <- if(is.null(site_url) && !is.null(site_id))
file.path("sites", site_id)
else if(!is.null(site_url) && is.null(site_id))
{
site_url <- httr::parse_url(site_url)
file.path("sites", paste0(site_url$hostname, ":"), site_url$path)
}
else stop("Must supply either site ID or URL")
ms_site$new(self$token, self$tenant, self$call_graph_endpoint(op))
})
az_group$set("public", "get_sharepoint_site", overwrite=TRUE,
function()
{
res <- self$do_operation("sites/root")
ms_site$new(self$token, self$tenant, res)
})
# ...
}
Once this is done, the object for a SharePoint site can be instantiated as follows: