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: