From f0188058d403ed3c3b51d8eb4a52fce8f586ad62 Mon Sep 17 00:00:00 2001 From: Hong Ooi Date: Sun, 25 Apr 2021 08:56:49 +1000 Subject: [PATCH] Add filter arg to all listing methods (#22) * add filter args to all list_* methods * document * filtering working again --- NEWS.md | 2 +- R/az_app.r | 12 +++++++--- R/az_dir_role.R | 12 +++++++--- R/az_group.R | 20 ++++++++++++----- R/az_object.R | 18 ++++++++++----- R/az_user.R | 38 +++++++++++++++++++++----------- R/ms_object.R | 5 +++++ man/az_app.Rd | 11 ++++++++-- man/az_directory_role.Rd | 9 +++++++- man/az_group.Rd | 11 ++++++++-- man/az_object.Rd | 11 ++++++++-- man/az_user.Rd | 17 ++++++++++----- man/ms_object.Rd | 7 ++++++ tests/testthat/test06_filter.R | 40 ++++++++++++++++++++++++++++++++++ 14 files changed, 170 insertions(+), 43 deletions(-) create mode 100644 tests/testthat/test06_filter.R diff --git a/NEWS.md b/NEWS.md index 43fc77d..cf54abf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,7 +8,7 @@ - The `ms_graph$get_user()` method can now get a user by email or display name. - Fix a bug in retrieving a paged list of values as a data frame, when `n` (the maximum number of rows) is supplied. - New `ms_graph$get_aad_object()` method to retrieve an Azure Active Directory object by ID. Mostly intended for use with the `list_object_memberships()` and `list_group_memberships()` methods, which return only IDs and not full object information. -- All `list_*` methods now have an `n` argument to set a cap on the number of results; the default value is `n=Inf`. If this is set to NULL, the `ms_graph_pager` iterator object is returned instead to allow manual iteration over the results. +- All `list_*` R6 methods now have `filter` and `n` arguments to filter the result set and cap the number of results. The default values are `filter=NULL` and `n=Inf`. If `n=NULL`, the `ms_graph_pager` iterator object is returned instead to allow manual iteration over the results. - Export the `find_class_generator()` function. - New "Batching and paging" vignette describing these APIs. diff --git a/R/az_app.r b/R/az_app.r index 2eb91d8..3350f7d 100644 --- a/R/az_app.r +++ b/R/az_app.r @@ -15,7 +15,7 @@ #' - `update(...)`: Update the app data in Azure Active Directory. For what properties can be updated, consult the REST API documentation link below. #' - `do_operation(...)`: Carry out an arbitrary operation on the app. #' - `sync_fields()`: Synchronise the R object with the app data in Azure Active Directory. -#' - `list_owners(type=c("user", "group", "application", "servicePrincipal"), n=Inf)`: Return a list of all owners of this app. Specify the `type` argument to filter the result for specific object type(s). `n` is the number of results to return; set this to NULL to return the `ms_graph_pager` iterator object for the result set. +#' - `list_owners(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)`: Return a list of all owners of this app. Specify the `type` argument to limit the result to specific object type(s). #' - `create_service_principal(...)`: Create a service principal for this app, by default in the current tenant. #' - `get_service_principal()`: Get the service principal for this app. #' - `delete_service_principal(confirm=TRUE)`: Delete the service principal for this app. By default, ask for confirmation first. @@ -30,6 +30,10 @@ #' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview), #' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta) #' +#' @section List methods: +#' All `list_*` methods have `filter` and `n` arguments to limit the number of results. The former should be an [OData expression](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter) as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are `filter=NULL` and `n=Inf`. If `n=NULL`, the `ms_graph_pager` iterator object is returned instead to allow manual iteration over the results. +#' +#' Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. #' @seealso #' [ms_graph], [az_service_principal], [az_user], [az_group], [az_object] #' @@ -157,9 +161,11 @@ public=list( self$update(keyCredentials=creds[-idx]) }, - list_owners=function(type=c("user", "group", "application", "servicePrincipal"), n=Inf) + list_owners=function(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf) { - pager <- self$get_list_pager(self$do_operation("owners"), type_filter=type) + opts <- list(`$filter`=filter, `$count`=if(!is.null(filter)) "true") + hdrs <- if(!is.null(filter)) httr::add_headers(consistencyLevel="eventual") + pager <- self$get_list_pager(self$do_operation("owners", options=opts, hdrs, type_filter=type)) extract_list_values(pager, n) }, diff --git a/R/az_dir_role.R b/R/az_dir_role.R index 0d33618..c19c284 100644 --- a/R/az_dir_role.R +++ b/R/az_dir_role.R @@ -14,11 +14,15 @@ #' - `update(...)`: Update the item's properties in Microsoft Graph. #' - `do_operation(...)`: Carry out an arbitrary operation on the item. #' - `sync_fields()`: Synchronise the R object with the item metadata in Microsoft Graph. -#' - `list_members(n=Inf)`: Return a list of all members of this group. `n` is the number of results to return; set this to NULL to return the `ms_graph_pager` iterator object for the result set. +#' - `list_members(filter=NULL, n=Inf)`: Return a list of all members of this group. #' #' @section Initialization: #' Currently support for directory roles is limited. Objects of this class should not be initialized directly. #' +#' @section List methods: +#' All `list_*` methods have `filter` and `n` arguments to limit the number of results. The former should be an [OData expression](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter) as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are `filter=NULL` and `n=Inf`. If `n=NULL`, the `ms_graph_pager` iterator object is returned instead to allow manual iteration over the results. +#' +#' Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. #' @seealso #' [ms_graph], [az_user] #' @@ -38,9 +42,11 @@ public=list( super$initialize(token, tenant, properties) }, - list_members=function(n=Inf) + list_members=function(filter=NULL, n=Inf) { - pager <- self$get_list_pager(self$do_operation("members")) + opts <- list(`$filter`=filter, `$count`=if(!is.null(filter)) "true") + hdrs <- if(!is.null(filter)) httr::add_headers(consistencyLevel="eventual") + pager <- self$get_list_pager(self$do_operation("members", options=opts, hdrs)) extract_list_values(pager, n) }, diff --git a/R/az_group.R b/R/az_group.R index 4a8d4f7..ea4a43a 100644 --- a/R/az_group.R +++ b/R/az_group.R @@ -14,12 +14,16 @@ #' - `update(...)`: Update the group information in Azure Active Directory. #' - `do_operation(...)`: Carry out an arbitrary operation on the group. #' - `sync_fields()`: Synchronise the R object with the app data in Azure Active Directory. -#' - `list_members(type=c("user", "group", "application", "servicePrincipal"), n=Inf)`: Return a list of all members of this group. Specify the `type` argument to filter the result for specific object type(s). `n` is the number of results to return; set this to NULL to return the `ms_graph_pager` iterator object for the result set. -#' - `list_owners(type=c("user", "group", "application", "servicePrincipal"), n=Inf)`: Return a list of all owners of this group. Specify the `type` argument to filter the result for specific object type(s). +#' - `list_members(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)`: Return a list of all members of this group. Specify the `type` argument to limit the result to specific object type(s). +#' - `list_owners(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)`: Return a list of all owners of this group. Specify the `type` argument to limit the result to specific object type(s). #' #' @section Initialization: #' Creating new objects of this class should be done via the `create_group` and `get_group` methods of the [ms_graph] and [az_app] classes. Calling the `new()` method for this class only constructs the R object; it does not call the Microsoft Graph API to create the actual group. #' +#' @section List methods: +#' All `list_*` methods have `filter` and `n` arguments to limit the number of results. The former should be an [OData expression](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter) as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are `filter=NULL` and `n=Inf`. If `n=NULL`, the `ms_graph_pager` iterator object is returned instead to allow manual iteration over the results. +#' +#' Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. #' @seealso #' [ms_graph], [az_app], [az_user], [az_object] #' @@ -59,15 +63,19 @@ public=list( super$initialize(token, tenant, properties) }, - list_members=function(type=c("user", "group", "application", "servicePrincipal"), n=Inf) + list_members=function(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf) { - pager <- self$get_list_pager(self$do_operation("members"), type_filter=type) + opts <- list(`$filter`=filter, `$count`=if(!is.null(filter)) "true") + hdrs <- if(!is.null(filter)) httr::add_headers(consistencyLevel="eventual") + pager <- self$get_list_pager(self$do_operation("members", options=opts, hdrs), type_filter=type) extract_list_values(pager, n) }, - list_owners=function(type=c("user", "group", "application", "servicePrincipal"), n=Inf) + list_owners=function(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf) { - pager <- self$get_list_pager(self$do_operation("owners"), type_filter=type) + opts <- list(`$filter`=filter, `$count`=if(!is.null(filter)) "true") + hdrs <- if(!is.null(filter)) httr::add_headers(consistencyLevel="eventual") + pager <- self$get_list_pager(self$do_operation("owners", options=opts, hdrs), type_filter=type) extract_list_values(pager, n) }, diff --git a/R/az_object.R b/R/az_object.R index b59b4cb..581a038 100644 --- a/R/az_object.R +++ b/R/az_object.R @@ -14,12 +14,16 @@ #' - `update(...)`: Update the object information in Azure Active Directory. #' - `do_operation(...)`: Carry out an arbitrary operation on the object. #' - `sync_fields()`: Synchronise the R object with the data in Azure Active Directory. -#' - `list_group_memberships(security_only=FALSE, n=Inf)`: Return the IDs of all groups this object is a member of. If `security_only` is TRUE, only security group IDs are returned. `n` is the number of results to return; set this to NULL to return the `ms_graph_pager` iterator object for the result set. -#' - `list_object_memberships(security_only=FALSE, n=Inf)`: Return the IDs of all groups, administrative units and directory roles this object is a member of. +#' - `list_group_memberships(security_only=FALSE, filter=NULL, n=Inf)`: Return the IDs of all groups this object is a member of. If `security_only` is TRUE, only security group IDs are returned. +#' - `list_object_memberships(security_only=FALSE, filter=NULL, n=Inf)`: Return the IDs of all groups, administrative units and directory roles this object is a member of. #' #' @section Initialization: #' Objects of this class should not be created directly. Instead, create an object of the appropriate subclass: [az_app], [az_service_principal], [az_user], [az_group]. #' +#' @section List methods: +#' All `list_*` methods have `filter` and `n` arguments to limit the number of results. The former should be an [OData expression](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter) as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are `filter=NULL` and `n=Inf`. If `n=NULL`, the `ms_graph_pager` iterator object is returned instead to allow manual iteration over the results. +#' +#' Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. #' @seealso #' [ms_graph], [az_app], [az_service_principal], [az_user], [az_group] #' @@ -32,18 +36,22 @@ az_object <- R6::R6Class("az_object", inherit=ms_object, public=list( - list_object_memberships=function(security_only=FALSE, n=Inf) + list_object_memberships=function(security_only=FALSE, filter=NULL, n=Inf) { + if(!is.null(filter)) + stop("This method doesn't support filtering", call.=FALSE) body <- list(securityEnabledOnly=security_only) pager <- self$get_list_pager(self$do_operation("getMemberObjects", body=body, http_verb="POST"), generate_objects=FALSE) unlist(extract_list_values(pager, n)) }, - list_group_memberships=function(security_only=FALSE, n=Inf) + list_group_memberships=function(security_only=FALSE, filter=NULL, n=Inf) { + if(!is.null(filter)) + stop("This method doesn't support filtering", call.=FALSE) body <- list(securityEnabledOnly=security_only) - pager <- self$get_list_pager(self$do_operation("getMemberGroups", body=body, http_verb="POST"), + pager <- self$get_list_pager(self$do_operation("getMemberGroups", body=body, http_verb="POST"), generate_objects=FALSE) unlist(extract_list_values(pager, n)) }, diff --git a/R/az_user.R b/R/az_user.R index 61b01bb..c9f144a 100644 --- a/R/az_user.R +++ b/R/az_user.R @@ -14,16 +14,20 @@ #' - `update(...)`: Update the user information in Azure Active Directory. #' - `do_operation(...)`: Carry out an arbitrary operation on the user account. #' - `sync_fields()`: Synchronise the R object with the app data in Azure Active Directory. -#' - `list_direct_memberships(n=Inf)`: List the groups and directory roles this user is a direct member of. `n` is the number of results to return; set this to NULL to return the `ms_graph_pager` iterator object for the result set. -#' - `list_owned_objects(type=c("user", "group", "application", "servicePrincipal"), n=Inf)`: List directory objects (groups/apps/service principals) owned by this user. Specify the `type` argument to filter the result for specific object type(s). -#' - `list_created_objects(type=c("user", "group", "application", "servicePrincipal"), n=Inf)`: List directory objects (groups/apps/service principals) created by this user. Specify the `type` argument to filter the result for specific object type(s). -#' - `list_owned_devices(n=Inf)`: List the devices owned by this user. -#' - `list_registered_devices(n=Inf)`: List the devices registered by this user. +#' - `list_direct_memberships(filter=NULL, n=Inf)`: List the groups and directory roles this user is a direct member of. +#' - `list_owned_objects(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)`: List directory objects (groups/apps/service principals) owned by this user. Specify the `type` argument to limit the result to specific object type(s). +#' - `list_created_objects(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)`: List directory objects (groups/apps/service principals) created by this user. Specify the `type` argument to limit the result to specific object type(s). +#' - `list_owned_devices(filter=NULL, n=Inf)`: List the devices owned by this user. +#' - `list_registered_devices(filter=NULL, n=Inf)`: List the devices registered by this user. #' - `reset_password(password=NULL, force_password_change=TRUE)`: Resets a user password. By default the new password will be randomly generated, and must be changed at next login. #' #' @section Initialization: #' Creating new objects of this class should be done via the `create_user` and `get_user` methods of the [ms_graph] and [az_app] classes. Calling the `new()` method for this class only constructs the R object; it does not call the Microsoft Graph API to create the actual user account. #' +#' @section List methods: +#' All `list_*` methods have `filter` and `n` arguments to limit the number of results. The former should be an [OData expression](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter) as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are `filter=NULL` and `n=Inf`. If `n=NULL`, the `ms_graph_pager` iterator object is returned instead to allow manual iteration over the results. +#' +#' Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. #' @seealso #' [ms_graph], [az_app], [az_group], [az_device], [az_object] #' @@ -93,27 +97,35 @@ public=list( password }, - list_owned_objects=function(type=c("user", "group", "application", "servicePrincipal"), n=Inf) + list_owned_objects=function(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf) { - pager <- self$get_list_pager(self$do_operation("ownedObjects"), type_filter=type) + opts <- list(`$filter`=filter, `$count`=if(!is.null(filter)) "true") + hdrs <- if(!is.null(filter)) httr::add_headers(consistencyLevel="eventual") + pager <- self$get_list_pager(self$do_operation("ownedObjects", options=opts, hdrs), type_filter=type) extract_list_values(pager, n) }, - list_created_objects=function(type=c("user", "group", "application", "servicePrincipal"), n=Inf) + list_created_objects=function(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf) { - pager <- self$get_list_pager(self$do_operation("createdObjects"), type_filter=type) + opts <- list(`$filter`=filter, `$count`=if(!is.null(filter)) "true") + hdrs <- if(!is.null(filter)) httr::add_headers(consistencyLevel="eventual") + pager <- self$get_list_pager(self$do_operation("createdObjects", options=opts, hdrs), type_filter=type) extract_list_values(pager, n) }, - list_owned_devices=function(n=Inf) + list_owned_devices=function(filter=NULL, n=Inf) { - pager <- self$get_list_pager(self$do_operation("ownedDevices")) + opts <- list(`$filter`=filter, `$count`=if(!is.null(filter)) "true") + hdrs <- if(!is.null(filter)) httr::add_headers(consistencyLevel="eventual") + pager <- self$get_list_pager(self$do_operation("ownedDevices", options=opts, hdrs)) extract_list_values(pager, n) }, - list_direct_memberships=function(n=Inf) + list_direct_memberships=function(filter=NULL, n=Inf) { - pager <- self$get_list_pager(self$do_operation("memberOf")) + opts <- list(`$filter`=filter, `$count`=if(!is.null(filter)) "true") + hdrs <- if(!is.null(filter)) httr::add_headers(consistencyLevel="eventual") + pager <- self$get_list_pager(self$do_operation("memberOf", options=opts, hdrs)) extract_list_values(pager, n) }, diff --git a/R/ms_object.R b/R/ms_object.R index 8323506..6a4c63e 100644 --- a/R/ms_object.R +++ b/R/ms_object.R @@ -19,6 +19,11 @@ #' @section Initialization: #' Objects of this class should not be created directly. Instead, create an object of the appropriate subclass. #' +#' @section List methods: +#' All `list_*` methods have `filter` and `n` arguments to limit the number of results. The former should be an [OData expression](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter) as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are `filter=NULL` and `n=Inf`. If `n=NULL`, the `ms_graph_pager` iterator object is returned instead to allow manual iteration over the results. +#' +#' Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. +#' #' @section Paged results: #' Microsoft Graph returns lists in pages, with each page containing a subset of objects and a link to the next page. AzureGraph provides an iterator-based API that lets you access each page individually, or collect them all into a single object. #' diff --git a/man/az_app.Rd b/man/az_app.Rd index 07c3cf9..7c5897e 100644 --- a/man/az_app.Rd +++ b/man/az_app.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/az_app.r +% Please edit documentation in R/az_app.R \docType{class} \name{az_app} \alias{az_app} @@ -29,7 +29,7 @@ Base class representing an AAD app. \item \code{update(...)}: Update the app data in Azure Active Directory. For what properties can be updated, consult the REST API documentation link below. \item \code{do_operation(...)}: Carry out an arbitrary operation on the app. \item \code{sync_fields()}: Synchronise the R object with the app data in Azure Active Directory. -\item \code{list_owners(type=c("user", "group", "application", "servicePrincipal"), n=Inf)}: Return a list of all owners of this app. Specify the \code{type} argument to filter the result for specific object type(s). \code{n} is the number of results to return; set this to NULL to return the \code{ms_graph_pager} iterator object for the result set. +\item \code{list_owners(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)}: Return a list of all owners of this app. Specify the \code{type} argument to limit the result to specific object type(s). \item \code{create_service_principal(...)}: Create a service principal for this app, by default in the current tenant. \item \code{get_service_principal()}: Get the service principal for this app. \item \code{delete_service_principal(confirm=TRUE)}: Delete the service principal for this app. By default, ask for confirmation first. @@ -48,6 +48,13 @@ Creating new objects of this class should be done via the \code{create_app} and \href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta}{REST API reference} } +\section{List methods}{ + +All \verb{list_*} methods have \code{filter} and \code{n} arguments to limit the number of results. The former should be an \href{https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter}{OData expression} as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are \code{filter=NULL} and \code{n=Inf}. If \code{n=NULL}, the \code{ms_graph_pager} iterator object is returned instead to allow manual iteration over the results. + +Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. +} + \examples{ \dontrun{ diff --git a/man/az_directory_role.Rd b/man/az_directory_role.Rd index 56ba9d7..cdcce44 100644 --- a/man/az_directory_role.Rd +++ b/man/az_directory_role.Rd @@ -28,7 +28,7 @@ Class representing a role in Azure Active Directory. \item \code{update(...)}: Update the item's properties in Microsoft Graph. \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{list_members(n=Inf)}: Return a list of all members of this group. \code{n} is the number of results to return; set this to NULL to return the \code{ms_graph_pager} iterator object for the result set. +\item \code{list_members(filter=NULL, n=Inf)}: Return a list of all members of this group. } } @@ -37,6 +37,13 @@ Class representing a role in Azure Active Directory. Currently support for directory roles is limited. Objects of this class should not be initialized directly. } +\section{List methods}{ + +All \verb{list_*} methods have \code{filter} and \code{n} arguments to limit the number of results. The former should be an \href{https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter}{OData expression} as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are \code{filter=NULL} and \code{n=Inf}. If \code{n=NULL}, the \code{ms_graph_pager} iterator object is returned instead to allow manual iteration over the results. + +Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. +} + \seealso{ \link{ms_graph}, \link{az_user} diff --git a/man/az_group.Rd b/man/az_group.Rd index 2e01b42..3feb769 100644 --- a/man/az_group.Rd +++ b/man/az_group.Rd @@ -28,8 +28,8 @@ Class representing an AAD group. \item \code{update(...)}: Update the group information in Azure Active Directory. \item \code{do_operation(...)}: Carry out an arbitrary operation on the group. \item \code{sync_fields()}: Synchronise the R object with the app data in Azure Active Directory. -\item \code{list_members(type=c("user", "group", "application", "servicePrincipal"), n=Inf)}: Return a list of all members of this group. Specify the \code{type} argument to filter the result for specific object type(s). \code{n} is the number of results to return; set this to NULL to return the \code{ms_graph_pager} iterator object for the result set. -\item \code{list_owners(type=c("user", "group", "application", "servicePrincipal"), n=Inf)}: Return a list of all owners of this group. Specify the \code{type} argument to filter the result for specific object type(s). +\item \code{list_members(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)}: Return a list of all members of this group. Specify the \code{type} argument to limit the result to specific object type(s). +\item \code{list_owners(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)}: Return a list of all owners of this group. Specify the \code{type} argument to limit the result to specific object type(s). } } @@ -38,6 +38,13 @@ Class representing an AAD group. Creating new objects of this class should be done via the \code{create_group} and \code{get_group} methods of the \link{ms_graph} and \link{az_app} classes. Calling the \code{new()} method for this class only constructs the R object; it does not call the Microsoft Graph API to create the actual group. } +\section{List methods}{ + +All \verb{list_*} methods have \code{filter} and \code{n} arguments to limit the number of results. The former should be an \href{https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter}{OData expression} as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are \code{filter=NULL} and \code{n=Inf}. If \code{n=NULL}, the \code{ms_graph_pager} iterator object is returned instead to allow manual iteration over the results. + +Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. +} + \examples{ \dontrun{ diff --git a/man/az_object.Rd b/man/az_object.Rd index abf8e29..a78e964 100644 --- a/man/az_object.Rd +++ b/man/az_object.Rd @@ -28,8 +28,8 @@ Base class representing an Azure Active Directory object in Microsoft Graph. \item \code{update(...)}: Update the object information in Azure Active Directory. \item \code{do_operation(...)}: Carry out an arbitrary operation on the object. \item \code{sync_fields()}: Synchronise the R object with the data in Azure Active Directory. -\item \code{list_group_memberships(security_only=FALSE, n=Inf)}: Return the IDs of all groups this object is a member of. If \code{security_only} is TRUE, only security group IDs are returned. \code{n} is the number of results to return; set this to NULL to return the \code{ms_graph_pager} iterator object for the result set. -\item \code{list_object_memberships(security_only=FALSE, n=Inf)}: Return the IDs of all groups, administrative units and directory roles this object is a member of. +\item \code{list_group_memberships(security_only=FALSE, filter=NULL, n=Inf)}: Return the IDs of all groups this object is a member of. If \code{security_only} is TRUE, only security group IDs are returned. +\item \code{list_object_memberships(security_only=FALSE, filter=NULL, n=Inf)}: Return the IDs of all groups, administrative units and directory roles this object is a member of. } } @@ -38,6 +38,13 @@ Base class representing an Azure Active Directory object in Microsoft Graph. Objects of this class should not be created directly. Instead, create an object of the appropriate subclass: \link{az_app}, \link{az_service_principal}, \link{az_user}, \link{az_group}. } +\section{List methods}{ + +All \verb{list_*} methods have \code{filter} and \code{n} arguments to limit the number of results. The former should be an \href{https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter}{OData expression} as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are \code{filter=NULL} and \code{n=Inf}. If \code{n=NULL}, the \code{ms_graph_pager} iterator object is returned instead to allow manual iteration over the results. + +Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. +} + \seealso{ \link{ms_graph}, \link{az_app}, \link{az_service_principal}, \link{az_user}, \link{az_group} diff --git a/man/az_user.Rd b/man/az_user.Rd index b3b3b02..01a1258 100644 --- a/man/az_user.Rd +++ b/man/az_user.Rd @@ -28,11 +28,11 @@ Class representing an AAD user account. \item \code{update(...)}: Update the user information in Azure Active Directory. \item \code{do_operation(...)}: Carry out an arbitrary operation on the user account. \item \code{sync_fields()}: Synchronise the R object with the app data in Azure Active Directory. -\item \code{list_direct_memberships(n=Inf)}: List the groups and directory roles this user is a direct member of. \code{n} is the number of results to return; set this to NULL to return the \code{ms_graph_pager} iterator object for the result set. -\item \code{list_owned_objects(type=c("user", "group", "application", "servicePrincipal"), n=Inf)}: List directory objects (groups/apps/service principals) owned by this user. Specify the \code{type} argument to filter the result for specific object type(s). -\item \code{list_created_objects(type=c("user", "group", "application", "servicePrincipal"), n=Inf)}: List directory objects (groups/apps/service principals) created by this user. Specify the \code{type} argument to filter the result for specific object type(s). -\item \code{list_owned_devices(n=Inf)}: List the devices owned by this user. -\item \code{list_registered_devices(n=Inf)}: List the devices registered by this user. +\item \code{list_direct_memberships(filter=NULL, n=Inf)}: List the groups and directory roles this user is a direct member of. +\item \code{list_owned_objects(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)}: List directory objects (groups/apps/service principals) owned by this user. Specify the \code{type} argument to limit the result to specific object type(s). +\item \code{list_created_objects(type=c("user", "group", "application", "servicePrincipal"), filter=NULL, n=Inf)}: List directory objects (groups/apps/service principals) created by this user. Specify the \code{type} argument to limit the result to specific object type(s). +\item \code{list_owned_devices(filter=NULL, n=Inf)}: List the devices owned by this user. +\item \code{list_registered_devices(filter=NULL, n=Inf)}: List the devices registered by this user. \item \code{reset_password(password=NULL, force_password_change=TRUE)}: Resets a user password. By default the new password will be randomly generated, and must be changed at next login. } } @@ -42,6 +42,13 @@ Class representing an AAD user account. Creating new objects of this class should be done via the \code{create_user} and \code{get_user} methods of the \link{ms_graph} and \link{az_app} classes. Calling the \code{new()} method for this class only constructs the R object; it does not call the Microsoft Graph API to create the actual user account. } +\section{List methods}{ + +All \verb{list_*} methods have \code{filter} and \code{n} arguments to limit the number of results. The former should be an \href{https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter}{OData expression} as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are \code{filter=NULL} and \code{n=Inf}. If \code{n=NULL}, the \code{ms_graph_pager} iterator object is returned instead to allow manual iteration over the results. + +Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. +} + \examples{ \dontrun{ diff --git a/man/ms_object.Rd b/man/ms_object.Rd index a82ef1d..f73527b 100644 --- a/man/ms_object.Rd +++ b/man/ms_object.Rd @@ -37,6 +37,13 @@ Base class representing a object in Microsoft Graph. All other Graph object clas Objects of this class should not be created directly. Instead, create an object of the appropriate subclass. } +\section{List methods}{ + +All \verb{list_*} methods have \code{filter} and \code{n} arguments to limit the number of results. The former should be an \href{https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter}{OData expression} as a string to filter the result set on. The latter should be a number setting the maximum number of (filtered) results to return. The default values are \code{filter=NULL} and \code{n=Inf}. If \code{n=NULL}, the \code{ms_graph_pager} iterator object is returned instead to allow manual iteration over the results. + +Support in the underlying Graph API for OData queries is patchy. Not all endpoints that return lists of objects support filtering, and if they do, they may not allow all of the defined operators. If your filtering expression results in an error, you can carry out the operation without filtering and then filter the results on the client side. +} + \section{Paged results}{ Microsoft Graph returns lists in pages, with each page containing a subset of objects and a link to the next page. AzureGraph provides an iterator-based API that lets you access each page individually, or collect them all into a single object. diff --git a/tests/testthat/test06_filter.R b/tests/testthat/test06_filter.R new file mode 100644 index 0000000..3454e10 --- /dev/null +++ b/tests/testthat/test06_filter.R @@ -0,0 +1,40 @@ +context("List filtering") + +tenant <- Sys.getenv("AZ_TEST_TENANT_ID") +user <- Sys.getenv("AZ_TEST_USERPRINCIPALNAME") + +if(tenant == "" || user == "") + skip("Paging tests skipped: login credentials not set") + +if(!interactive()) + skip("Paging tests skipped: must be in interactive session") + +scopes <- c("https://graph.microsoft.com/.default", "openid", "offline_access") +token <- AzureAuth::get_azure_token(scopes, tenant, .az_cli_app_id, version=2) +gr <- ms_graph$new(token=token) +me <- gr$get_user(user) + + +test_that("Filtering works", +{ + id <- me$list_group_memberships()[1] + grp <- gr$get_aad_object(id) + expect_true(inherits(grp, "az_group") && !is.null(grp$properties$displayName)) + filtexpr1 <- sprintf("displayName eq '%s'", grp$properties$displayName) + + expect_error(me$list_group_memberships(filter=filtexpr1)) + + lst1 <- me$list_direct_memberships(filter=filtexpr1) + expect_is(lst1, "list") + expect_true(length(lst1) == 1 && + inherits(lst1[[1]], "az_group") && + lst1[[1]]$properties$displayName == grp$properties$displayName) + + filtexpr2 <- sprintf("userPrincipalName eq '%s'", user) + lst2 <- grp$list_members(filter=filtexpr2) + expect_is(lst2, "list") + expect_true(length(lst2) == 1 && + inherits(lst2[[1]], "az_user") && + lst2[[1]]$properties$userPrincipalName == user) +}) +