From 762709ee89f30b60c88a77ed4004f13a3458e932 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Tue, 13 Apr 2021 13:04:48 -0700 Subject: [PATCH 1/3] Updated graph references to v1, not beta. Add graph extension for talking to beta endpoint, but using v1 types --- .../CommunityToolkit.Net.Graph.csproj | 2 +- .../Extensions/GraphExtensions.People.cs | 40 ++++++ .../Extensions/GraphExtensions.Users.cs | 126 ++++++++++++++++++ .../Extensions/GraphExtensions.cs | 86 +----------- .../Extensions/ProviderExtensions.cs | 20 +++ ...CommunityToolkit.Uwp.Graph.Controls.csproj | 2 +- .../Controls/GraphPresenter/GraphPresenter.cs | 7 +- .../Controls/LoginButton/LoginButton.cs | 2 +- .../Controls/PersonView/PersonView.cs | 10 +- .../Controls/PersonView/PersonView.xaml | 2 +- 10 files changed, 203 insertions(+), 94 deletions(-) create mode 100644 CommunityToolkit.Net.Graph/Extensions/GraphExtensions.People.cs create mode 100644 CommunityToolkit.Net.Graph/Extensions/GraphExtensions.Users.cs diff --git a/CommunityToolkit.Net.Graph/CommunityToolkit.Net.Graph.csproj b/CommunityToolkit.Net.Graph/CommunityToolkit.Net.Graph.csproj index 98339a9..3a60504 100644 --- a/CommunityToolkit.Net.Graph/CommunityToolkit.Net.Graph.csproj +++ b/CommunityToolkit.Net.Graph/CommunityToolkit.Net.Graph.csproj @@ -18,7 +18,7 @@ - + diff --git a/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.People.cs b/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.People.cs new file mode 100644 index 0000000..ccc990d --- /dev/null +++ b/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.People.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.Graph; + +namespace CommunityToolkit.Net.Graph.Extensions +{ + /// + /// People focused extension methods to the Graph SDK used by the controls and helpers. + /// + public static partial class GraphExtensions + { + /// + /// Shortcut to perform a person query. + /// + /// Instance of the . + /// User to search for. + /// collection of . + public static async Task FindPersonAsync(this GraphServiceClient graph, string query) + { + try + { + return await graph + .Me + .People + .Request() + .Search(query) + .WithScopes(new string[] { "people.read" }) + .GetAsync(); + } + catch + { + } + + return new UserPeopleCollectionPage(); + } + } +} diff --git a/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.Users.cs b/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.Users.cs new file mode 100644 index 0000000..ddee7f5 --- /dev/null +++ b/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.Users.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Threading.Tasks; +using Microsoft.Graph; + +namespace CommunityToolkit.Net.Graph.Extensions +{ + /// + /// User focused extension methods to the Graph SDK used by the controls and helpers. + /// + public static partial class GraphExtensions + { + /// + /// Retrieve the curernt user. + /// + /// Instance of the . + /// A representing the asynchronous operation. + public static async Task GetMeAsync(this GraphServiceClient graph) + { + try + { + return await graph.Me.Request().GetAsync(); + } + catch + { + } + + return null; + } + + /// + /// Retrieve a user by id. + /// + /// Instance of the . + /// The is of the user to retrieve. + /// A representing the asynchronous operation. + public static async Task GetUserAsync(this GraphServiceClient graph, string userId) + { + try + { + return await graph.Users[userId].Request().GetAsync(); + } + catch + { + } + + return null; + } + + /// + /// Shortcut to perform a user query. + /// + /// Instance of the . + /// User to search for. + /// collection of . + public static async Task FindUserAsync(this GraphServiceClient graph, string query) + { + try + { + return await graph + .Users + .Request() + .Filter($"startswith(displayName, '{query}') or startswith(givenName, '{query}') or startswith(surname, '{query}') or startswith(mail, '{query}') or startswith(userPrincipalName, '{query}')") + .WithScopes(new string[] { "user.readbasic.all" }) + .GetAsync(); + } + catch + { + } + + return new GraphServiceUsersCollectionPage(); + } + + /// + /// Helper to get the photo of a particular user. + /// + /// Instance of the . + /// UserID. + /// Stream with user photo or null. + public static async Task GetUserPhoto(this GraphServiceClient graph, string userId) + { + try + { + return await graph + .Users[userId] + .Photo + .Content + .Request() + .WithScopes(new string[] { "user.readbasic.all" }) + .GetAsync(); + } + catch + { + } + + return null; + } + + /// + /// Get the photo of the current user. + /// + /// Instance of the . + /// Stream with user photo or null. + public static async Task GetMyPhotoAsync(this GraphServiceClient graph) + { + try + { + return await graph + .Me + .Photo + .Content + .Request() + .WithScopes(new string[] { "user.read" }) + .GetAsync(); + } + catch + { + } + + return null; + } + } +} diff --git a/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.cs b/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.cs index 77d8637..c14d22d 100644 --- a/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.cs +++ b/CommunityToolkit.Net.Graph/Extensions/GraphExtensions.cs @@ -2,16 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.IO; -using System.Threading.Tasks; using Microsoft.Graph; namespace CommunityToolkit.Net.Graph.Extensions { /// - /// Extension methods to the Graph SDK used by the Microsoft.Toolkit.Graph.Controls. + /// Extension methods to the Graph SDK used by the controls and helpers. /// - public static class GraphExtensions + public static partial class GraphExtensions { /// /// Simple method to convert a to a with basic common properties like , , , , and intact. @@ -28,9 +26,9 @@ public static Person ToPerson(this User user) // Standard User Info DisplayName = user.DisplayName, - EmailAddresses = new RankedEmailAddress[] + ScoredEmailAddresses = new ScoredEmailAddress[] { - new RankedEmailAddress() + new ScoredEmailAddress() { Address = user.Mail ?? user.UserPrincipalName, }, @@ -41,85 +39,11 @@ public static Person ToPerson(this User user) // Company Information CompanyName = user.CompanyName, Department = user.Department, - Title = user.JobTitle, + JobTitle = user.JobTitle, OfficeLocation = user.OfficeLocation, }; } - /// - /// Shortcut to perform a person query. - /// - /// Instance of the . - /// User to search for. - /// collection of . - public static async Task FindPersonAsync(this GraphServiceClient graph, string query) - { - try - { - return await graph - .Me - .People - .Request() - .Search(query) - .WithScopes(new string[] { "people.read" }) - .GetAsync(); - } - catch - { - } - - return new UserPeopleCollectionPage(); - } - - /// - /// Shortcut to perform a user query. - /// - /// Instance of the . - /// User to search for. - /// collection of . - public static async Task FindUserAsync(this GraphServiceClient graph, string query) - { - try - { - return await graph - .Users - .Request() - .Filter($"startswith(displayName, '{query}') or startswith(givenName, '{query}') or startswith(surname, '{query}') or startswith(mail, '{query}') or startswith(userPrincipalName, '{query}')") - .WithScopes(new string[] { "user.readbasic.all" }) - .GetAsync(); - } - catch - { - } - - return new GraphServiceUsersCollectionPage(); - } - - /// - /// Helper to get the photo of a particular user. - /// - /// Instance of the . - /// UserID. - /// Stream with user photo or null. - public static async Task GetUserPhoto(this GraphServiceClient graph, string userid) - { - try - { - return await graph - .Users[userid] - .Photo - .Content - .Request() - .WithScopes(new string[] { "user.readbasic.all" }) - .GetAsync(); - } - catch - { - } - - return null; - } - /// /// Extension to provider Searching on OData Requests. /// diff --git a/CommunityToolkit.Net.Graph/Extensions/ProviderExtensions.cs b/CommunityToolkit.Net.Graph/Extensions/ProviderExtensions.cs index 5af1a2b..2826a81 100644 --- a/CommunityToolkit.Net.Graph/Extensions/ProviderExtensions.cs +++ b/CommunityToolkit.Net.Graph/Extensions/ProviderExtensions.cs @@ -13,6 +13,7 @@ namespace CommunityToolkit.Net.Graph.Extensions public static class ProviderExtensions { private static GraphServiceClient _client; + private static GraphServiceClient _betaClient; static ProviderExtensions() { @@ -46,5 +47,24 @@ public static GraphServiceClient Graph(this IProvider provider) return _client; } + + /// + /// Lazily gets a GraphServiceClient instance based on the current GlobalProvider, but configured for the beta endpoint. + /// The beta client instance is cleared whenever the GlobalProvider changes. + /// + /// The provider for authenticating Graph calls. + /// A GraphServiceClient instance configured for the beta endpoint. + public static GraphServiceClient BetaGraph(this IProvider provider) + { + if (_betaClient == null && provider?.State == ProviderState.SignedIn) + { + _betaClient = new GraphServiceClient("https://graph.microsoft.com/beta", new DelegateAuthenticationProvider(async (requestMessage) => + { + await provider.AuthenticateRequestAsync(requestMessage); + })); + } + + return _betaClient; + } } } diff --git a/CommunityToolkit.Uwp.Graph.Controls/CommunityToolkit.Uwp.Graph.Controls.csproj b/CommunityToolkit.Uwp.Graph.Controls/CommunityToolkit.Uwp.Graph.Controls.csproj index 4c79a34..ecff297 100644 --- a/CommunityToolkit.Uwp.Graph.Controls/CommunityToolkit.Uwp.Graph.Controls.csproj +++ b/CommunityToolkit.Uwp.Graph.Controls/CommunityToolkit.Uwp.Graph.Controls.csproj @@ -26,6 +26,7 @@ + @@ -35,7 +36,6 @@ - diff --git a/CommunityToolkit.Uwp.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs b/CommunityToolkit.Uwp.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs index be65053..5d91578 100644 --- a/CommunityToolkit.Uwp.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs +++ b/CommunityToolkit.Uwp.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs @@ -8,7 +8,6 @@ using System.Threading; using Microsoft.Graph; using Microsoft.Toolkit.Uwp; -using Newtonsoft.Json.Linq; using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -88,7 +87,7 @@ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) try { - var response = await request.SendAsync(null, CancellationToken.None).ConfigureAwait(false) as JObject; + var response = await request.SendAsync(null, CancellationToken.None).ConfigureAwait(false) as IDictionary; //// TODO: Deal with paging? @@ -97,11 +96,11 @@ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) if (IsCollection) { - data = values.ToObject(Array.CreateInstance(ResponseType, 0).GetType()); + data = Convert.ChangeType(values, Array.CreateInstance(ResponseType, 0).GetType()); } else { - data = values.ToObject(ResponseType); + data = Convert.ChangeType(values, ResponseType); } _ = dispatcherQueue.EnqueueAsync(() => Content = data); diff --git a/CommunityToolkit.Uwp.Graph.Controls/Controls/LoginButton/LoginButton.cs b/CommunityToolkit.Uwp.Graph.Controls/Controls/LoginButton/LoginButton.cs index ebe5f79..5b220e3 100644 --- a/CommunityToolkit.Uwp.Graph.Controls/Controls/LoginButton/LoginButton.cs +++ b/CommunityToolkit.Uwp.Graph.Controls/Controls/LoginButton/LoginButton.cs @@ -115,7 +115,7 @@ private async void LoadData() { // https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/master/src/components/mgt-login/mgt-login.ts#L139 // TODO: Batch with photo request later? https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/29 - UserDetails = await provider.Graph().Me.Request().GetAsync(); + UserDetails = await provider.Graph().GetMeAsync(); } catch (Exception e) { diff --git a/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.cs b/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.cs index 4e1199e..fdd3e42 100644 --- a/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.cs +++ b/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.cs @@ -138,7 +138,7 @@ private async void LoadData() // TODO: Batch when API easier https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/29 try { - user = await provider.Graph().Users[UserId].Request().GetAsync(); + user = await provider.Graph().GetUserAsync(UserId); } catch { @@ -147,7 +147,7 @@ private async void LoadData() try { // TODO: Move to LoadImage based on previous call? - await DecodeStreamAsync(await provider.Graph().Users[UserId].Photo.Content.Request().GetAsync()); + await DecodeStreamAsync(await provider.Graph().GetUserPhoto(UserId)); _photoId = UserId; } catch @@ -158,7 +158,7 @@ private async void LoadData() { try { - user = await provider.Graph().Me.Request().GetAsync(); + user = await provider.Graph().GetMeAsync(); } catch { @@ -166,7 +166,7 @@ private async void LoadData() try { - await DecodeStreamAsync(await provider.Graph().Me.Photo.Content.Request().GetAsync()); + await DecodeStreamAsync(await provider.Graph().GetMyPhotoAsync()); _photoId = user.Id; } catch @@ -203,7 +203,7 @@ private async Task LoadImageAsync(Person person) await DecodeStreamAsync(await graph.GetUserPhoto(person.UserPrincipalName)); _photoId = person.Id; // TODO: Only set on success for photo? } - else if (!string.IsNullOrWhiteSpace(person.EmailAddresses.First().Address)) + else if (!string.IsNullOrWhiteSpace(person.ScoredEmailAddresses.First().Address)) { // TODO https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/master/src/components/mgt-person/mgt-person.ts#L174 } diff --git a/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.xaml b/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.xaml index 260d054..5f8bda1 100644 --- a/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.xaml +++ b/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.xaml @@ -65,7 +65,7 @@ Visibility="{Binding ShowName, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BoolToVisibilityConverter}}" /> From 3e0ef581a8c8d0816d67fc95dde4795b0539c8d8 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Tue, 13 Apr 2021 13:13:55 -0700 Subject: [PATCH 2/3] Updated personView to use beta endpoint for photo retrieval --- .../Controls/PersonView/PersonView.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.cs b/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.cs index fdd3e42..f4f387b 100644 --- a/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.cs +++ b/CommunityToolkit.Uwp.Graph.Controls/Controls/PersonView/PersonView.cs @@ -27,8 +27,6 @@ public partial class PersonView : Control /// public const string PersonQueryMe = "me"; - private static readonly string[] RequiredScopes = new string[] { "user.readbasic.all" }; - private string _photoId = null; private string _defaultImageSource = "ms-appx:///Microsoft.Toolkit.Graph.Controls/Assets/person.png"; @@ -147,7 +145,7 @@ private async void LoadData() try { // TODO: Move to LoadImage based on previous call? - await DecodeStreamAsync(await provider.Graph().GetUserPhoto(UserId)); + await DecodeStreamAsync(await provider.BetaGraph().GetUserPhoto(UserId)); _photoId = UserId; } catch @@ -166,7 +164,7 @@ private async void LoadData() try { - await DecodeStreamAsync(await provider.Graph().GetMyPhotoAsync()); + await DecodeStreamAsync(await provider.BetaGraph().GetMyPhotoAsync()); _photoId = user.Id; } catch @@ -196,7 +194,7 @@ private async Task LoadImageAsync(Person person) try { // TODO: Better guarding - var graph = ProviderManager.Instance.GlobalProvider.Graph(); + var graph = ProviderManager.Instance.GlobalProvider.BetaGraph(); if (!string.IsNullOrWhiteSpace(person.UserPrincipalName)) { From 50abfcb232331074ef11ae9df9d38619b40c3411 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Tue, 13 Apr 2021 13:42:25 -0700 Subject: [PATCH 3/3] Undoing changes to GraphPresenter --- .../Controls/GraphPresenter/GraphPresenter.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CommunityToolkit.Uwp.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs b/CommunityToolkit.Uwp.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs index 5d91578..2f0d08d 100644 --- a/CommunityToolkit.Uwp.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs +++ b/CommunityToolkit.Uwp.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs @@ -8,6 +8,7 @@ using System.Threading; using Microsoft.Graph; using Microsoft.Toolkit.Uwp; +using Newtonsoft.Json.Linq; using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -87,7 +88,7 @@ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) try { - var response = await request.SendAsync(null, CancellationToken.None).ConfigureAwait(false) as IDictionary; + var response = await request.SendAsync(null, CancellationToken.None).ConfigureAwait(false) as JObject; //// TODO: Deal with paging? @@ -96,11 +97,11 @@ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) if (IsCollection) { - data = Convert.ChangeType(values, Array.CreateInstance(ResponseType, 0).GetType()); + data = values.ToObject(Array.CreateInstance(ResponseType, 0).GetType()); } else { - data = Convert.ChangeType(values, ResponseType); + data = values.ToObject(ResponseType); } _ = dispatcherQueue.EnqueueAsync(() => Content = data); @@ -112,4 +113,4 @@ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) } } } -} +} \ No newline at end of file