Extending AzureGraph

Hong Ooi

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.

Extend the ms_object base class

AzureGraph 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:

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 the class with 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:

Add getter and setter methods

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:

library(AzureGraph)
library(Microsoft365R)

gr <- get_graph_login()

# directly from the Graph client
mysite1 <- gr$get_sharepoint_site("https://mytenant.sharepoint.com/sites/my-site-name")

# or via a group
mygroup <- gr$get_group("my-group-guid")
mysite2 <- mygroup$get_sharepoint_site()