GraphServiceClient expand function - microsoft-graph-api

I am working with the .NET Microsoft.Graph library in order to sync users and groups. The following query returns all 269 groups:
var msGraphGroup =
await graphServiceClient.Groups
.Request()
.Top(999)
.GetAsync();
Since the groups are nested, I need to get also the members:
var msGraphGroup_ =
await graphServiceClient.Groups
.Request()
.Top(999)
.Expand("members")
.GetAsync();
This query returns the groups with the members, but just 100 items. Why not all 269?
I wrote also the following:
var msGraphGroup =
await graphServiceClient.Groups
.Request()
.Expand("members")
.GetAsync();
List<Microsoft.Graph.Group> all = new List<Microsoft.Graph.Group>();
while (msGraphGroup.Count > 0)
{
all.AddRange(msGraphGroup.CurrentPage);
if (msGraphGroup.NextPageRequest == null)
{
break;
}
msGraphGroup = await msGraphGroup.NextPageRequest.GetAsync();
}
Is there a way to get all groups with all members in one single query?

As per the documentation on Graph API Throttling Guide, expand operator is quite expensive and that is why it is limited. Also it does not support select so you will end up fetching all user info in the expand.
As of now you have to query the members of each group separately. There is a suggestion here to use the batch API to reduce calls.
Note: Your question is similar to Retrieve all Users from all Groups? and Microsoft Graph cannot select on expand statement for /groups. Check them out for more insight.

Related

Graph API .net SDK - Filter Me.MemberOf based on displayName of groups

I am trying to filter out the groups of a user he is a member of based on some words that the group name may contain.
I am using this code -
var groups = _graphServiceClient.Me.MemberOf.Request().Filter($"displayName -eq 'abhi'").GetAsync().Result;
but I am getting the error that filter request is invalid.
Any help with this is always appreciated, thanks.
Based on aad advanced queries here your request is supported when the request headers contains ConsistencyLevel = eventual and count = true
To get it to work here is the sample code:
List<Option> options = new List<Option>();
options.Add(new HeaderOption("ConsistencyLevel", "eventual"));
options.Add(new QueryOption("$filter", $"displayName eq 'abhi'"));
options.Add(new QueryOption("$count", "true"));
var groups = await graphServiceClient.Me
.MemberOf
.Request(options)
.GetAsync();

Microsoft Graph API SDK for SharePoint to query a file

I try to use Graph API SDK to query a file in a SharePoint site
var site = await graphClient.Sites["myDomain"]
.SiteWithPath("relativePath").Request()
.GetAsync().ConfigureAwait(false);
var drive = await graphClient.Sites["myDomain]
.SiteWithPath("relativePath").Lists["mylib"].Drive
.Request().GetAsync().ConfigureAwait(false);
var file = await graphClient.Sites[site.Id]
.Drives[drive.Id].Root.ItemWithPath("/folder1").Children["myfile.txt"]
.Request().GetAsync().ConfigureAwait(false);
This is working and I get the file.
I try to combine the three steps into one,
var file = await graphClient.Sites["myDomain"]
.SiteWithPath("relativePath").Lists["mylib"].Drive
.Root.ItemWithPath("/folder1").Children["myfile.txt"]
.Request().GetAsync().ConfigureAwait(false);
But it gives Bad Request error. What's wrong? What is the best way to do this?
The navigation you are using is not accepted by Graph.
As per the get files docs, you need the site-id.
# Valid
GET /sites/mydomain.sharepoint.com:/relativePath/lists/mylib/drive
# Invalid addition to above url
GET /sites/mydomain.sharepoint.com:/relativePath/lists/mylib/drive/root:/myfile.txt:
If you don't have the site id, you can expand the list relationship in the get list drive call and use the site-id to request for the file. This will be two requests instead.
var drive = await graphServiceClient
.Sites["mydomain.sharepoint.com"]
.SiteWithPath(relativePath)
.Lists["mylib"]
.Drive
.Request()
.Expand("list")
.GetAsync()
.ConfigureAwait(false);
var file = await graphServiceClient
.Sites[drive.List.ParentReference.SiteId]
.Drives[drive.Id]
.Root.ItemWithPath("/Folder 1")
.Children["myfile.txt"]
.Request().GetAsync().ConfigureAwait(false);

When is Microsoft Graph API finished calculating in Excel?

I can see how to trigger the calculation of a spreadsheet using Microsoft Graph API here...
https://learn.microsoft.com/en-us/graph/api/workbookapplication-calculate?view=graph-rest-1.0&tabs=http
But, when I pull the results from the calculations they don't seem to be updated. However, I pull a second or third time, it is usually updated.
I assume this means that calculation hasn't finished b/c of the size of the file or complexity of the calcs.
However, being asynchronous, I'm not finding any way to check to see when the calculations have finished.
Any idea how to do this?
UPDATE 1:
This is the code I'm (now) using to create the session (per #UJJAVAL123-MSFT)
var persistChanges = false;
var wrkbk = await graphClient.Me.Drive.Items[strItemId].Workbook
.CreateSession(persistChanges)
.Request()
.PostAsync();
This will give me a value for 'id' like this...
cluster=US5&session=15.SN3PEPF000074ED1.A82.1.V24.7737Nwad4oPuVafaO%2fpAkiay14.5.en-US5.en-US26.10037ffe965d2cf2-Unlimited1.A1.N16.16.0.12904.3505114.5.en-US5.en-US1.V1.N0.1.A&usid=1b230e8f-3bd0-cdaa-2da6-47db74154075
I'm not exactly sure how/where to use this, or whether I use the entire thing (it looks like a query string) or if I'm supposed to parse and pull out one of the values...
cluster=US5
session=15.SN3PEPF000074ED1.A82.1.V24.xxxxxxxxxxxxxxxxx%2fpAkiay14.5.en-US5.en-US26.10037ffe965d2cf2-Unlimited1.A1.N16.16.0.12904.3505114.5.en-US5.en-US1.V1.N0.1.A
usid=1b230e8f-3bd0-cdaa-2da6-xxxxxxxxxxxx
and this is the code i'm using to trigger the calculation, but not sure how to connect the two...
var calculationType = "FullRebuild";
await graphClient.Me.Drive.Items[strItemId].Workbook.Application
.Calculate(calculationType)
.Request()
.PostAsync();
Also, I'm seeing that it is possible to create, refresh and close a session, but not entirely sure how to check on a specific async process inside that session.
Here is the code I'm using to check a specific range for a value, not sure where we pass the session-id here either...
var result = await graphClient.Me.Drive.Items[strItemId].Workbook.Worksheets[strSheetName]
.Range(strRangeName)
.Request()
.GetAsync();
UPDATE 2:
I can run an API call (presumably) in the same session successfully by passing the workbook-session-id (which is the ENTIRE string shown above) and I get the expected 204 No Content response. However, it is not clear from the c# Code Snippet in the Microsoft Graph Explorer how to pass the workbook-session-id in the request.
Here is the code it provides...
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
await graphClient.Me.Drive.Items["{item-id}"].Workbook.Application
.Calculate(null)
.Request()
.PostAsync();
So the question remains, how can I do a PostAsync or GetAsync and reference the workbook-session-id?
This code does NOT give me an error...
await graphClient.Me.Drive.Items[strItemId].Workbook.Application
.Calculate(calculationType)
.Request()
.Header("workbook-session-id",wrkbk.Id)
.PostAsync();
So now, the question is WHEN do I get the workbook-session-id? Do I get it when I initially open the workbook and then pass it to every call?
You should create a session and pass the session Id with each request. The presence of a
session Id in the requests ensures that you are using the Excel API in the most efficient
way possible.
Check here for API call to get a session
So after a decent amount of testing, i figured it out.
The answer is that you use the CreateSession method (https://learn.microsoft.com/en-us/graph/api/workbook-createsession?view=graph-rest-1.0&tabs=http) to get the workbook info, and you set the persistChanges setting, then you get back info about the workbook session.
Like this...
using Microsoft.Graph;
// strItemId = the id from the microsoft graph api of the item
// strUserId = the id of the user from the microsoft graph api (note: must have permissions set correctly)
public static async Task<WorkbookSessionInfo> GetWorkbookSessionId(string strItemId, string strUserId)
{
// true = you can see changes in the workbook
// false = don't update the workbook, just do calculations
var persistChanges = true;
try
{
var wrkbk = await graphClient.Users[strUserId].Drive.Items[strItemId].Workbook
.CreateSession(persistChanges)
.Request()
.PostAsync();
var result = wrkbk;
return result;
}
catch (Exception ex)
{
Console.WriteLine($"Error getting items: {ex.Message}");
return null;
}
}
And you are returned a WorkbookSessionInfo object, which includes the SessionId for use in subsequent calls. This way, it keeps all your calls in the same session!
https://learn.microsoft.com/en-us/graph/api/resources/workbooksessioninfo?view=graph-rest-1.0

OneDrive API returns files that are not available

I use the MS Graph API to upload data to OneDrive.
I have deleted all data on OneDrive, but when I use the :
var search = await graphClient.Users[user.Id].Drive.Root
.Search("")
.Request()
.GetAsync();
foreach (var item in search)
{
Console.WriteLine(item.Name);
}
I get data displayed even though my OneDrive is empty, why ?
I use:
.Net 4.7.2,
Visual Studio
The solution is:
If I use this code:
var search = await graphClient.Users[user.Id].Drive.Root
.Search("")
.Request()
.GetAsync();
He's searching through the index.
Data can also be displayed there although it is no longer available.
Therefore, data that is no longer available on OneDrive is also displayed.
Do not use the search function.

Microsoft Graph Client SDK -filter groups by name

A rather simple ask but I'm having trouble converting my basic httpClient method of querying the Graph into the SDK method. I was using the following and it works fine:
var filter = "IT";
var response = await httpClient.GetAsync($"{webOptions.GraphApiUrl}/beta/groups?$filter=startswith(displayName, '{filter}')&$select=id,displayName");
...now I'm attempting to filter using the SDK as follows:
var groups = await graphServiceClient.Groups
.Request()
.Filter($"displayName startswith {filter}")
.Select("id, displayName")
.GetAsync();
I've also tried .Filter($"startswith("displayName", {filter})) and other variants.
I'm getting an invalid filter clause error. Any ideas?
Apparently it occurs since the provided filter expression for Filter method is invalid, it could be validated like this:
var message = graphServiceClient.Groups
.Request()
.Filter($"displayName startswith '{filter}'")
.Select("id, displayName").GetHttpRequestMessage();
The generated message.RequestUri will return the following value:
https://graph.microsoft.com/v1.0/groups?$filter=displayName startswith '{filter}'&$select=id, displayName}
A valid filter expression needs to be specified like this:
.Filter($"startswith(displayName, '{filter}')")
In case if you want to switch to beta version for GraphServiceClient class, it could be specified like this:
graphServiceClient.BaseUrl = "https://graph.microsoft.com/beta";

Resources