List Contacts in ContactFolder - microsoft-graph-api

I am trying to get a list of Contacts from a ContactFolder using Microsoft Graph API.
The code I am using is as follows:
GraphServiceClient graphClient = new GraphServiceClient (new CustomAuthentication ());
// build the request
var request = graphClient.Me.ContactFolders.Request();
// get all contact folders
var folders = await request.GetAsync();
// get the first folder
var folder = folders.FirstOrDefault();
// get all contacts in that folder (Contacts is always null)
var contacts = folder.Contacts.ToList();
On the last line, the Contacts collection is null even though there are contacts in that folder when viewed through Outlook.
I tried to call folder.Contacts.GetAsync() however that method does not appear to be available:
Any help would be appreciated.

First you're requesting FirstOrDefault but this isn't a method exposed by IUserContactFoldersCollectionPage. You need to address the items within folders by index:
folders[0]
folders[1]
folders[etc]
You're also not populating the Contacts object. Keep in mind that Microsoft Graph is a REST API. It is provides a collection of stateless HTTP methods so it doesn't automatically hydrate your object model. You need to specifically request the data for Contacts:
.Contacts.Request().GetAsync();
Also note that ContactFolders will only return results if there are multiple folders. The default Contacts folder is never returned. If a user has no additional folders, this will return an empty result.
With this in mind, you would retrieve the Contacts like this:
GraphServiceClient graphClient = new GraphServiceClient(new CustomAuthentication());
// get all contact folders
var folders = await graphClient
.Me
.ContactFolders
.Request()
.GetAsync();
if (folders.Count > 0)
{
// Get contacts from that first folder
var folderContacts = await graphClient
.Me
.ContactFolders[folders[0].Id]
.Contacts
.Request()
.GetAsync();
}
else
{
// This user only has the default contacts folder
var defaultContacts = await graphClient
.Me
.Contacts
.Request()
.GetAsync();
}

Related

How to mock Microsoft Graph APIs GraphServiceClient using moq

My project is using GraphServiceClient to get the user Group Names using the below code.This is also using Microsoft.Identity.Web package so GraphServiceClient is injected through constructor.
var group = await graphClient.Me.TransitiveMemberOf
.Request()
.GetAsync();
The group variable is then used to get the DisplayName of the group.
I want to unit test the above code using NUnit and Moq.
var mockAuthProvider = new Mock<IAuthenticationProvider>();
var mockHttpProvider = new Mock<IHttpProvider>();
var mockGraphClient = new Mock<GraphServiceClient>(mockAuthProvider.Object, mockHttpProvider.Object);
mockGraphClient.Setup(c => c.Me.TransitiveMemberOf.Request().GetAsync(CancellationToken.None)).ReturnsAsync(???);
The ReturnAsync will return IUserTransitiveMemberOfCollectionWithReferencesPage, but how can I give a default value for it so I can test the rest of the method which actually gets the displayName
Thanks in advance.
Create a new instance of UserTransitiveMemberOfCollectionWithReferencesPage and add a new Group item to the current page.
UserTransitiveMemberOfCollectionWithReferencesPage page = new
UserTransitiveMemberOfCollectionWithReferencesPage
{
AdditionalData = new Dictionary<string, object>(),
};
page.Add(new Group { DisplayName = "MyName" });
Return page in ReturnsAsync method
mockGraphClient.Setup(c => c.Me.TransitiveMemberOf.Request().GetAsync(CancellationToken.None))
.ReturnsAsync(() => page);

Microsoft.Graph SDK connects to user's OneDrive but Items returns NULL

I'm writing a utility in C# using the Microsoft.Graph SDK that connects and reads a user's OneDrive. I have created an App Registration, granted the application Files.Read.All permissions, and given Admin consent per the documentation.
I am able to connect to the Graph and the metadata for the OneDrive:
List<string> scopes = new List<string>();
scopes.Add("https://graph.microsoft.com/.default");
var authenticationProvider = new MsalAuthenticationProvider(cca, scopes.ToArray());
GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider);
var drive = await graphClient.Users[userId].Drive
.Request()
.GetAsync();
It appears to correctly connect to the OneDrive, as evidenced by the properties that do return correct data like Quota, Owner, etc.
The issue is that the Items object is null, so I can't read any of the drive items:
I tried using the returned drive Id to access the drive directly, but received the same result:
var driveById = await graphClient.Drives[drive.Id]
.Request()
.GetAsync();
The few examples I found don't indicate any additional Request options or missing permissions. So how do I access the OneDrive Items?
The solution for this issue was presented in the comments, so I'm writing it up here for completeness.
ORIGINAL CODE:
var rootDrive = await GraphClient.Users[UserId].Drive.Request().GetAsync();
This returns the metadata of the user's OneDrive, but does not capture the Children. We will need this information later, however, so the final solution uses both this reference and the updated code.
UPDATED CODE:
To do that, you need to reference the drive's Root and its Children:
var driveItems = await GraphClient.Users[UserId].Drive
.Root
.Children
.Request()
.GetAsync();
Doing so returns an IDriveItemChildrenCollectionPage:
PROCESS THE CHILDREN:
For small samples, a standard foreach will work fine, but for larger collections you will need to implement a PageIterator (which I have not done yet). To get the children of this driveItem, you will need the drive Id of the root element as well as the current driveItem.Id:
var children = await GraphClient.Drives[rootDriveId].Items[item.Id].Children.Request().GetAsync()
Putting it altogether, it looks something like this:
public async Task ListOneDrive()
{
var rootDrive = await GraphClient.Users[UserId].Drive.Request().GetAsync();
var driveItems = await GraphClient.Users[UserId].Drive
.Root
.Children
.Request()
.GetAsync();
foreach (var item in driveItems)
{
await ListDriveItem(rootDrive.Id, item);
}
}
public async Task ListDriveItem(string rootDriveId, DriveItem item, int indentLevel = 1)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < indentLevel; i++)
{
sb.Append($" ");
}
if (item.Folder != null)
{
Console.WriteLine($"{sb.ToString()}> {item.Name}/");
var children = await GraphClient.Drives[rootDriveId].Items[item.Id].Children.Request().GetAsync();
foreach (var child in children)
{
await (ListDriveItem(rootDriveId, child, indentLevel + 1));
}
}
else if (item.File != null)
{
Console.WriteLine($"{sb.ToString()} {item.Name}");
}
}
This example is from a Console app that uses a recursive call to drill down through all the folders. Both methods really should have a PageIterator as mentioned above.

Exception Azure AD B2C Setting Custom Attribute With GraphServiceClient

Following directions from here: https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-custom-attributes?pivots=b2c-user-flow
I am able to create and get users fine:
// This works!
var graphServiceClient = new GraphServiceClient(
$"https://graph.microsoft.com/beta",
AuthenticationProvider);
var user = await graphClient.Users[userId].Request().GetAsync();
if (user.AdditionalData == null)
user.AdditionalData = new Dictionary<string, object>();
user.AdditionalData[$"extension_xxxxxxx_Apps] = "TestValue";
// this does not work!
result = await graphClient.Users[user.Id].Request().UpdateAsync(msGraphUser);
For xxxxxxx I tried both the Client ID and Object Id from the b2c-extensions-app in my tenant.
Exception:
Microsoft.Graph.ServiceException: 'Code: Request_BadRequest
Message: The following extension properties are not available: extension_xxxxxxx_Apps.
what am I missing? How can I set a custom attribute from GraphServiceClient?
Thank you
Try creating a "new" user rather getting the existing one. When you call UpdateAsync, B2C will only set the properties that you provide (it won't overwrite the other props with null). This may or may not help, but the thing is, we're doing the same thing you do above except with a "new" User, and it works for us.
User b2cUser = await this.GetOurUser(userId);
var additionalData = new Dictionary<string, object>();
additionalData["extension_id-of-extensions-app-here_Foo"] = "Ice Cream Cone";
var updatedB2cUser = new User
{
AdditionalData = additionalData
};
await this.GraphServiceClient.Users[b2cUser.Id].Request().UpdateAsync(updatedB2cUser);
In practice, we include additional props such as Identities, because we use B2C as an identity provider...so the above is some pared-down code from our B2C wrapper showing just the "custom property" part.
Update
Actually, you may just need to remove the hyphens from your extension-app ID and double-check which one you're using.

Error When creating a GraphServiceClient in Example App from MS

I'm trying to get the MS example application to work for the Graph Beta Webhooks API and it's currently crashing because I've had to to modify some of the example code to remove some obsolete code and I'm not sure what to replace it with.
This is the function:
public static GraphServiceClient GetAuthenticatedClient(string userId, string redirect)
{
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (request) =>
{
var tokenCache = new SampleTokenCache(userId);
// Obsolete code
//var cca = new ConfidentialClientApplication(Startup.ClientId, redirect, new ClientCredential(Startup.ClientSecret), tokenCache.GetMsalCacheInstance(), null);
// New code
var cca2 = ConfidentialClientApplicationBuilder.Create(Startup.ClientId).WithClientSecret(Startup.ClientSecret).WithRedirectUri(redirect).Build();
// Question - how do I pass the tokenCache in here as the userTokenCache ?
// ERROR - With the new code this returns zero accounts presuambly because I haven't passed in a userTokenCache
var accounts = await cca2.GetAccountsAsync();
// Obsolete code
//var authResult = await cca.AcquireTokenSilentAsync(Startup.Scopes, accounts.First());
// New code
var authResult2 = await cca2.AcquireTokenSilent(Startup.Scopes, accounts.First()).ExecuteAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult2.AccessToken);
}));
return graphClient;
}
If I use the ConfidentialClientApplicationBuilder then the GetAccountsAsync() returns an empty collection and I think it's because I haven't passed the tokenCache into the builder. Does anyone know how to fix this code or has anyone got the example App working ?
This is the link to the example App:
https://learn.microsoft.com/en-us/samples/microsoftgraph/aspnet-webhooks-rest-sample/microsoft-graph-aspnet-webhooks/
Thanks
Ed James
You need to login as user so that an account is added to the token cache which is available calling the tokenCache.GetMsalCacheInstance() method.

How do I access Events in a calendar other than my own using Graph API and C#

I know how to accomplish this using MS Graph Explorer. However, I need to get this done in C# code.
The code below is from Microsoft. I would like to be able to query/update a random user (yes, I have permissions to these user calendars).
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
// Get the Graph client from the provider
var graphClient = ProviderManager.Instance.GlobalProvider.Graph;
try
{
// Get the events
var events = await graphClient.Me.Events.Request()
.Select("subject,organizer,start,end")
.OrderBy("createdDateTime DESC")
.GetAsync();
EventList.ItemsSource = events.CurrentPage.ToList();
}
catch (Microsoft.Graph.ServiceException ex)
{
ShowNotification($"Exception getting events: {ex.Message}");
}
base.OnNavigatedTo(e);
}
To get the events of other users than yourself use this statement:
var events = await graphClient.Users[{userID}].Events.Request()
.Select("subject,organizer,start,end")
.OrderBy("createdDateTime DESC")
.GetAsync();

Resources