From 0be53f1666fedab12d00ed75ebf04141011ca57d Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Tue, 3 Aug 2021 12:18:47 -0700 Subject: [PATCH] Updated Graph usage pattern in RoamingSettings helpers and updated descriptions to csproj files. --- ...ommunityToolkit.Authentication.Msal.csproj | 9 +- ...CommunityToolkit.Authentication.Uwp.csproj | 6 +- .../CommunityToolkit.Authentication.csproj | 11 ++- .../CommunityToolkit.Graph.Uwp.csproj | 26 +++--- .../Controls/GraphPresenter/GraphPresenter.cs | 10 +-- .../Controls/PeoplePicker/PeoplePicker.cs | 2 +- .../CommunityToolkit.Graph.csproj | 14 +++- .../GraphExtensions.OneDrive.cs} | 47 +++++++---- .../GraphExtensions.UserExtensions.cs} | 83 ++++++------------- .../Extensions/GraphExtensions.Users.cs | 18 ++-- .../IRemoteSettingsStorageHelper.cs | 35 -------- .../RoamingSettings/OneDriveStorageHelper.cs | 41 +++++---- .../UserExtensionStorageHelper.cs | 32 +++++-- 13 files changed, 161 insertions(+), 173 deletions(-) rename CommunityToolkit.Graph/{Helpers/RoamingSettings/OneDriveDataSource.cs => Extensions/GraphExtensions.OneDrive.cs} (56%) rename CommunityToolkit.Graph/{Helpers/RoamingSettings/UserExtensionDataSource.cs => Extensions/GraphExtensions.UserExtensions.cs} (65%) delete mode 100644 CommunityToolkit.Graph/Helpers/RoamingSettings/IRemoteSettingsStorageHelper.cs diff --git a/CommunityToolkit.Authentication.Msal/CommunityToolkit.Authentication.Msal.csproj b/CommunityToolkit.Authentication.Msal/CommunityToolkit.Authentication.Msal.csproj index 3024215..c5157df 100644 --- a/CommunityToolkit.Authentication.Msal/CommunityToolkit.Authentication.Msal.csproj +++ b/CommunityToolkit.Authentication.Msal/CommunityToolkit.Authentication.Msal.csproj @@ -4,8 +4,10 @@ Windows Community Toolkit .NET Standard Auth Services - This package includes .NET Standard authentication helpers such as: - - MsalProvider: + This library provides an authentication provider based on the native Windows dialogues. It is part of the Windows Community Toolkit. + + Classes: + - MsalProvider: An authentication provider based on MSAL for .NET. Community Toolkit Provider Authentication Auth Msal @@ -18,7 +20,4 @@ - - - diff --git a/CommunityToolkit.Authentication.Uwp/CommunityToolkit.Authentication.Uwp.csproj b/CommunityToolkit.Authentication.Uwp/CommunityToolkit.Authentication.Uwp.csproj index 01d1e55..5c68901 100644 --- a/CommunityToolkit.Authentication.Uwp/CommunityToolkit.Authentication.Uwp.csproj +++ b/CommunityToolkit.Authentication.Uwp/CommunityToolkit.Authentication.Uwp.csproj @@ -4,12 +4,12 @@ uap10.0.17134 Windows Community Toolkit Graph Uwp Authentication Provider - This library provides an authentication provider based on the native Windows dialogues. It is part of the Windows Community Toolkit. + This library provides an authentication provider based on the native Windows dialogues. Classes: - - WindowsProvider: + - WindowsProvider: An authentication provider based on the native AccountsSettingsPane in Windows. - UWP Toolkit Windows Microsoft Graph AadLogin Authentication Login + UWP Community Toolkit Provider Authentication Auth Windows 9.0 diff --git a/CommunityToolkit.Authentication/CommunityToolkit.Authentication.csproj b/CommunityToolkit.Authentication/CommunityToolkit.Authentication.csproj index e9a1003..1c8f624 100644 --- a/CommunityToolkit.Authentication/CommunityToolkit.Authentication.csproj +++ b/CommunityToolkit.Authentication/CommunityToolkit.Authentication.csproj @@ -5,12 +5,11 @@ Windows Community Toolkit .NET Standard Auth Services This package includes .NET Standard authentication helpers such as: - - BaseProvider: - - IProvider: - - MockProvider: - - ProviderManager: - - ProviderState: - - ProviderStateChangedEventArgs: + + - BaseProvider: A base construct for building Graph Providers on top of. + - IProvider: Authentication provider interface to expose more states around the authentication process for Graph controls and helpers. + - ProviderManager: Shared provider manager used by controls and helpers to authenticate and call the Microsoft Graph. + - ProviderState: Represents the current authentication state of the session for a given IProvider. Community Toolkit Provider Authentication Auth diff --git a/CommunityToolkit.Graph.Uwp/CommunityToolkit.Graph.Uwp.csproj b/CommunityToolkit.Graph.Uwp/CommunityToolkit.Graph.Uwp.csproj index 97f8bac..506adb7 100644 --- a/CommunityToolkit.Graph.Uwp/CommunityToolkit.Graph.Uwp.csproj +++ b/CommunityToolkit.Graph.Uwp/CommunityToolkit.Graph.Uwp.csproj @@ -6,13 +6,19 @@ This library provides Microsoft Graph UWP XAML controls. It is part of the Windows Community Toolkit. - Classes: - - GraphPresenter: - - LoginButton: The Login Control leverages MSAL libraries to support the sign-in processes for Microsoft Graph and beyond. + Controls: + - GraphPresenter: A specialized ContentPresenter for fetching and displaying data from Microsoft Graph. + - LoginButton: The Login Control leverages the global authentication provider to support the sign-in processes for Microsoft Graph and beyond. - PersonView: The PersonView control displays a user photo and can display their name and e-mail. - - PeoplePicker: The PeoplePicker Control is a simple control that allows for selection of one or more users. + - PeoplePicker: The PeoplePicker Control is a simple control that allows for selection users. + + Extensions: + - FrameworkElement.IsVisibleWhen(ProviderState): Extension on FrameworkElement for toggling visibility in response to changes in the global authentcation provider. + + Triggers: + - ProviderStateTrigger: StateTrigger for reacting to changes in the global authentcation provider. - UWP Toolkit Windows Controls MSAL Microsoft Graph AadLogin ProfileCard Person PeoplePicker Login + UWP Community Toolkit Windows Controls Microsoft Graph Login Person PeoplePicker Presenter 9.0 @@ -22,8 +28,8 @@ + - @@ -35,14 +41,6 @@ - - - - - - - - diff --git a/CommunityToolkit.Graph.Uwp/Controls/GraphPresenter/GraphPresenter.cs b/CommunityToolkit.Graph.Uwp/Controls/GraphPresenter/GraphPresenter.cs index 97ffd48..ed8edb7 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/GraphPresenter/GraphPresenter.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/GraphPresenter/GraphPresenter.cs @@ -74,11 +74,11 @@ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) // Note: some interfaces from the Graph SDK don't implement IBaseRequestBuilder properly, see https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/722 if (RequestBuilder != null) { - var request = new BaseRequest( - RequestBuilder.RequestUrl, - RequestBuilder.Client); // TODO: Do we need separate Options here? - request.Method = HttpMethods.GET; - request.QueryOptions = QueryOptions?.Select(option => (Microsoft.Graph.QueryOption)option)?.ToList() ?? new List(); + var request = new BaseRequest(RequestBuilder.RequestUrl, RequestBuilder.Client) // TODO: Do we need separate Options here? + { + Method = HttpMethods.GET, + QueryOptions = QueryOptions?.Select(option => (Microsoft.Graph.QueryOption)option)?.ToList() ?? new List(), + }; // Handle Special QueryOptions if (!string.IsNullOrWhiteSpace(OrderBy)) diff --git a/CommunityToolkit.Graph.Uwp/Controls/PeoplePicker/PeoplePicker.cs b/CommunityToolkit.Graph.Uwp/Controls/PeoplePicker/PeoplePicker.cs index 337605e..d3f1cb9 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/PeoplePicker/PeoplePicker.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/PeoplePicker/PeoplePicker.cs @@ -22,7 +22,7 @@ namespace CommunityToolkit.Graph.Uwp.Controls /// public partial class PeoplePicker : TokenizingTextBox { - private DispatcherQueueTimer _typeTimer = null; + private readonly DispatcherQueueTimer _typeTimer = null; /// /// Initializes a new instance of the class. diff --git a/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj b/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj index 3af7782..a45af4e 100644 --- a/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj +++ b/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj @@ -5,11 +5,17 @@ Windows Community Toolkit .NET Standard Graph Services - This package includes .NET Standard code helpers such as: - - GraphExtensions: Helpers for common tasks related to the Microsoft Graph used by the Microsoft.Toolkit.Graph.Controls. - - ProviderExtensions: Extension on IProvider for accessing a pre-configured GraphServiceClient instance. + This package includes .NET Standard code helpers such as: + + Extensions: + - GraphExtensions: Helpers for common tasks related to the Microsoft Graph in context of the available controls and helpers. + - ProviderExtensions: Extension on IProvider for accessing a pre-configured GraphServiceClient instance. + + Helpers: + - OneDriveStorageHelper: A helper for interacting with data stored via files and folders in OneDrive. + - UserExtensionStorageHelper: A helper for interacting with open extensions on the Graph User to store data in key/value pairs. - Windows Community Toolkit Graph Provider Extensions + Windows Community Toolkit Microsoft Graph Provider Extensions Helpers Roaming Settings 9.0 diff --git a/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveDataSource.cs b/CommunityToolkit.Graph/Extensions/GraphExtensions.OneDrive.cs similarity index 56% rename from CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveDataSource.cs rename to CommunityToolkit.Graph/Extensions/GraphExtensions.OneDrive.cs index 4f4c686..ee5c93e 100644 --- a/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveDataSource.cs +++ b/CommunityToolkit.Graph/Extensions/GraphExtensions.OneDrive.cs @@ -7,31 +7,27 @@ using System.IO; using System.Text; using System.Threading.Tasks; -using CommunityToolkit.Authentication; -using CommunityToolkit.Graph.Extensions; using Microsoft.Graph; using Microsoft.Toolkit.Helpers; -namespace CommunityToolkit.Graph.Helpers.RoamingSettings +namespace CommunityToolkit.Graph.Extensions { /// - /// Helpers for interacting with files in the special OneDrive AppRoot folder. + /// OneDrive focused extension methods to the Graph SDK used by the controls and helpers. /// - internal static class OneDriveDataSource + public static partial class GraphExtensions { - private static GraphServiceClient Graph => ProviderManager.Instance.GlobalProvider?.GetClient(); - /// /// Updates or create a new file on the remote with the provided content. /// /// The type of object to save. /// A representing the asynchronous operation. - public static async Task SetFileAsync(string userId, string itemPath, T fileContents, IObjectSerializer serializer) + public static async Task SetFileAsync(this GraphServiceClient graph, string userId, string itemPath, T fileContents, IObjectSerializer serializer) { var json = serializer.Serialize(fileContents) as string; using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); - return await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(itemPath).Content.Request().PutAsync(stream); + return await graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(itemPath).Content.Request().PutAsync(stream); } /// @@ -39,9 +35,9 @@ public static async Task SetFileAsync(string userId, string itemPa /// /// The type of object to return. /// A representing the asynchronous operation. - public static async Task GetFileAsync(string userId, string itemPath, IObjectSerializer serializer) + public static async Task GetFileAsync(this GraphServiceClient graph, string userId, string itemPath, IObjectSerializer serializer) { - Stream stream = await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(itemPath).Content.Request().GetAsync(); + Stream stream = await graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(itemPath).Content.Request().GetAsync(); string streamContents = new StreamReader(stream).ReadToEnd(); @@ -52,12 +48,20 @@ public static async Task GetFileAsync(string userId, string itemPath, IObj /// Delete the file from the remote. /// /// A representing the asynchronous operation. - public static async Task DeleteItemAsync(string userId, string itemPath) + public static async Task DeleteItemAsync(this GraphServiceClient graph, string userId, string itemPath) { - await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(itemPath).Request().DeleteAsync(); + await graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(itemPath).Request().DeleteAsync(); } - public static async Task CreateFolderAsync(string userId, string folderName, string path = null) + /// + /// Ensure a folder exists by name. + /// + /// Instance of the . + /// The id of the target Graph user. + /// The name of the new folder. + /// The path to create the new folder in. + /// A representing the asynchronous operation. + public static async Task CreateFolderAsync(this GraphServiceClient graph, string userId, string folderName, string path = null) { var folderDriveItem = new DriveItem() { @@ -67,17 +71,24 @@ public static async Task CreateFolderAsync(string userId, string folderName, str if (path != null) { - await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(path).Children.Request().AddAsync(folderDriveItem); + await graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(path).Children.Request().AddAsync(folderDriveItem); } else { - await Graph.Users[userId].Drive.Special.AppRoot.Children.Request().AddAsync(folderDriveItem); + await graph.Users[userId].Drive.Special.AppRoot.Children.Request().AddAsync(folderDriveItem); } } - public static async Task> ReadFolderAsync(string userId, string folderPath) + /// + /// Retrieve a list of directory items with names and types. + /// + /// Instance of the . + /// The id of the target Graph user. + /// The path to create the new folder in. + /// A with the directory listings. + public static async Task> ReadFolderAsync(this GraphServiceClient graph, string userId, string folderPath) { - IDriveItemChildrenCollectionPage folderContents = await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(folderPath).Children.Request().GetAsync(); + IDriveItemChildrenCollectionPage folderContents = await graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(folderPath).Children.Request().GetAsync(); var results = new List<(DirectoryItemType, string)>(); foreach (var item in folderContents) diff --git a/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionDataSource.cs b/CommunityToolkit.Graph/Extensions/GraphExtensions.UserExtensions.cs similarity index 65% rename from CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionDataSource.cs rename to CommunityToolkit.Graph/Extensions/GraphExtensions.UserExtensions.cs index 2a035b6..a0a5d06 100644 --- a/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionDataSource.cs +++ b/CommunityToolkit.Graph/Extensions/GraphExtensions.UserExtensions.cs @@ -4,28 +4,28 @@ using System; using System.Collections.Generic; +using System.IO; using System.Net.Http; +using System.Text; using System.Threading.Tasks; -using CommunityToolkit.Authentication; -using CommunityToolkit.Graph.Extensions; using Microsoft.Graph; +using Microsoft.Toolkit.Helpers; -namespace CommunityToolkit.Graph.Helpers.RoamingSettings +namespace CommunityToolkit.Graph.Extensions { /// - /// Manages Graph interaction with open extensions on the user. + /// UserExtensions focused extension methods to the Graph SDK used by the controls and helpers. /// - internal static class UserExtensionDataSource + public static partial class GraphExtensions { - private static GraphServiceClient Graph => ProviderManager.Instance.GlobalProvider?.GetClient(); - /// /// Retrieve an extension object for a user. /// + /// Instance of the . /// The user to access. /// The extension to retrieve. /// The extension result. - public static async Task GetExtension(string userId, string extensionId) + public static async Task GetExtension(this GraphServiceClient graph, string userId, string extensionId) { if (string.IsNullOrWhiteSpace(extensionId)) { @@ -37,33 +37,35 @@ public static async Task GetExtension(string userId, string extension throw new ArgumentNullException(nameof(userId)); } - var extension = await Graph.Users[userId].Extensions[extensionId].Request().GetAsync(); + var extension = await graph.Users[userId].Extensions[extensionId].Request().GetAsync(); return extension; } /// /// Get all extension objects for a user. /// + /// Instance of the . /// The user to access. /// All extension results. - public static async Task> GetAllExtensions(string userId) + public static async Task> GetAllExtensions(this GraphServiceClient graph, string userId) { if (string.IsNullOrWhiteSpace(userId)) { throw new ArgumentNullException(nameof(userId)); } - var extensions = await Graph.Users[userId].Extensions.Request().GetAsync(); + var extensions = await graph.Users[userId].Extensions.Request().GetAsync(); return extensions; } /// /// Create a new extension object on a user. /// + /// Instance of the . /// The user to access. /// The id of the new extension. /// The newly created extension. - public static async Task CreateExtension(string userId, string extensionId) + public static async Task CreateExtension(this GraphServiceClient graph, string userId, string extensionId) { if (string.IsNullOrWhiteSpace(extensionId)) { @@ -78,13 +80,13 @@ public static async Task CreateExtension(string userId, string extens try { // Try to see if the extension already exists. - return await GetExtension(userId, extensionId); + return await graph.GetExtension(userId, extensionId); } catch { } - string requestUrl = Graph.Users[userId].Extensions.Request().RequestUrl; + string requestUrl = graph.Users[userId].Extensions.Request().RequestUrl; string json = "{" + "\"@odata.type\": \"microsoft.graph.openTypeExtension\"," + @@ -93,13 +95,13 @@ public static async Task CreateExtension(string userId, string extens HttpRequestMessage hrm = new (HttpMethod.Post, requestUrl); hrm.Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json"); - await Graph.AuthenticationProvider.AuthenticateRequestAsync(hrm); - HttpResponseMessage response = await Graph.HttpProvider.SendAsync(hrm); + await graph.AuthenticationProvider.AuthenticateRequestAsync(hrm); + HttpResponseMessage response = await graph.HttpProvider.SendAsync(hrm); if (response.IsSuccessStatusCode) { // Deserialize into Extension object. var content = await response.Content.ReadAsStringAsync(); - var extension = Graph.HttpProvider.Serializer.DeserializeObject(content); + var extension = graph.HttpProvider.Serializer.DeserializeObject(content); return extension; } @@ -109,10 +111,11 @@ public static async Task CreateExtension(string userId, string extens /// /// Delete a user extension by id. /// + /// Instance of the . /// The user to access. /// The id of the extension to delete. /// A task. - public static async Task DeleteExtension(string userId, string extensionId) + public static async Task DeleteExtension(this GraphServiceClient graph, string userId, string extensionId) { if (string.IsNullOrWhiteSpace(extensionId)) { @@ -126,7 +129,7 @@ public static async Task DeleteExtension(string userId, string extensionId) try { - await GetExtension(userId, extensionId); + await graph.GetExtension(userId, extensionId); } catch { @@ -134,51 +137,19 @@ public static async Task DeleteExtension(string userId, string extensionId) return; } - await Graph.Users[userId].Extensions[extensionId].Request().DeleteAsync(); - } - - /// - /// Get a value from an extension by key. - /// - /// The type of object to return. - /// The target extension. - /// The key for the desired value. - /// The value for the provided key. - public static T GetValue(this Extension extension, string key) - { - return (T)GetValue(extension, key); - } - - /// - /// Get a value from a user extension by key. - /// - /// The target extension. - /// The key for the desired value. - /// The value for the provided key. - public static object GetValue(this Extension extension, string key) - { - if (string.IsNullOrWhiteSpace(key)) - { - throw new ArgumentNullException(nameof(key)); - } - - if (extension.AdditionalData.ContainsKey(key)) - { - return extension.AdditionalData[key]; - } - - return null; + await graph.Users[userId].Extensions[extensionId].Request().DeleteAsync(); } /// /// Sets a user extension value at the specified key. /// + /// Instance of the . /// The user to access. /// The id of the target extension. /// The key. /// The value to set. /// A task. - public static async Task SetValue(string userId, string extensionId, string key, object value) + public static async Task SetValue(this GraphServiceClient graph, string userId, string extensionId, string key, object value) { if (string.IsNullOrWhiteSpace(userId)) { @@ -198,7 +169,7 @@ public static async Task SetValue(string userId, string extensionId, string key, var extensionToUpdate = (Extension)Activator.CreateInstance(typeof(Extension), true); extensionToUpdate.AdditionalData = new Dictionary() { { key, value } }; - await Graph.Users[userId].Extensions[extensionId].Request().UpdateAsync(extensionToUpdate); + await graph.Users[userId].Extensions[extensionId].Request().UpdateAsync(extensionToUpdate); } } -} \ No newline at end of file +} diff --git a/CommunityToolkit.Graph/Extensions/GraphExtensions.Users.cs b/CommunityToolkit.Graph/Extensions/GraphExtensions.Users.cs index 6a641ca..98d82af 100644 --- a/CommunityToolkit.Graph/Extensions/GraphExtensions.Users.cs +++ b/CommunityToolkit.Graph/Extensions/GraphExtensions.Users.cs @@ -20,7 +20,11 @@ public static partial class GraphExtensions /// A representing the asynchronous operation. public static async Task GetMeAsync(this GraphServiceClient graph) { - return await graph.Me.Request().GetAsync(); + return await graph + .Me + .Request() + .WithScopes(new string[] { "user.read" }) + .GetAsync(); } /// @@ -31,7 +35,11 @@ public static async Task GetMeAsync(this GraphServiceClient graph) /// A representing the asynchronous operation. public static async Task GetUserAsync(this GraphServiceClient graph, string userId) { - return await graph.Users[userId].Request().GetAsync(); + return await graph + .Users[userId] + .Request() + .WithScopes(new string[] { "user.read" }) + .GetAsync(); } /// @@ -46,7 +54,7 @@ public static async Task FindUserAsync(this Gr .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" }) + .WithScopes(new string[] { "user.readbasic.all" }) .GetAsync(); } @@ -63,7 +71,7 @@ public static async Task GetUserPhoto(this GraphServiceClient graph, str .Photo .Content .Request() - ////.WithScopes(new string[] { "user.readbasic.all" }) + .WithScopes(new string[] { "user.readbasic.all" }) .GetAsync(); } @@ -79,7 +87,7 @@ public static async Task GetMyPhotoAsync(this GraphServiceClient graph) .Photo .Content .Request() - ////.WithScopes(new string[] { "user.read" }) + .WithScopes(new string[] { "user.read" }) .GetAsync(); } } diff --git a/CommunityToolkit.Graph/Helpers/RoamingSettings/IRemoteSettingsStorageHelper.cs b/CommunityToolkit.Graph/Helpers/RoamingSettings/IRemoteSettingsStorageHelper.cs deleted file mode 100644 index 66601d0..0000000 --- a/CommunityToolkit.Graph/Helpers/RoamingSettings/IRemoteSettingsStorageHelper.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Toolkit.Helpers; - -namespace CommunityToolkit.Graph.Helpers.RoamingSettings -{ - /// - /// Describes a remote settings storage location with basic sync support. - /// - /// The type of keys to use for accessing values. - public interface IRemoteSettingsStorageHelper : ISettingsStorageHelper - { - /// - /// Gets or sets an event that fires whenever a sync request has completed. - /// - EventHandler SyncCompleted { get; set; } - - /// - /// Gets or sets a value an event that fires whenever a remote sync request has failed. - /// - EventHandler SyncFailed { get; set; } - - /// - /// Update the remote extension to match the local cache and retrieve any new keys. Any existing remote values are replaced. - /// - /// The freshly synced user extension. - Task Sync(); - } -} diff --git a/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveStorageHelper.cs b/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveStorageHelper.cs index 90e4259..c9b56fd 100644 --- a/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveStorageHelper.cs +++ b/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveStorageHelper.cs @@ -9,12 +9,13 @@ using System.Threading.Tasks; using CommunityToolkit.Authentication; using CommunityToolkit.Graph.Extensions; +using Microsoft.Graph; using Microsoft.Toolkit.Helpers; namespace CommunityToolkit.Graph.Helpers.RoamingSettings { /// - /// A base class for easily building roaming settings helper implementations. + /// An IFileStorageHelper implementation for interacting with data stored via files and folders in OneDrive. /// public class OneDriveStorageHelper : IFileStorageHelper { @@ -35,13 +36,8 @@ public class OneDriveStorageHelper : IFileStorageHelper /// A new instance of the configured for the current Graph user. public static async Task CreateForCurrentUserAsync(IObjectSerializer objectSerializer = null) { - var provider = ProviderManager.Instance.GlobalProvider; - if (provider == null || provider.State != ProviderState.SignedIn) - { - throw new InvalidOperationException($"The {nameof(ProviderManager.GlobalProvider)} must be set and signed in to create a new {nameof(OneDriveStorageHelper)} for the current user."); - } - - var me = await provider.GetClient().Me.Request().GetAsync(); + var graph = GetGraphClient(); + var me = await graph.GetMeAsync(); var userId = me.Id; return new OneDriveStorageHelper(userId, objectSerializer); @@ -61,25 +57,29 @@ public OneDriveStorageHelper(string userId, IObjectSerializer objectSerializer = /// public async Task ReadFileAsync(string filePath, T @default = default) { - return await OneDriveDataSource.GetFileAsync(UserId, filePath, Serializer) ?? @default; + var graph = ProviderManager.Instance.GlobalProvider.GetClient(); + return await graph.GetFileAsync(UserId, filePath, Serializer) ?? @default; } /// public Task> ReadFolderAsync(string folderPath) { - return OneDriveDataSource.ReadFolderAsync(UserId, folderPath); + var graph = GetGraphClient(); + return graph.ReadFolderAsync(UserId, folderPath); } /// public async Task CreateFileAsync(string filePath, T value) { - await OneDriveDataSource.SetFileAsync(UserId, filePath, value, Serializer); + var graph = GetGraphClient(); + await graph.SetFileAsync(UserId, filePath, value, Serializer); } /// public Task CreateFolderAsync(string folderName) { - return OneDriveDataSource.CreateFolderAsync(UserId, folderName); + var graph = GetGraphClient(); + return graph.CreateFolderAsync(UserId, folderName); } /// @@ -90,13 +90,26 @@ public Task CreateFolderAsync(string folderName) /// A task. public Task CreateFolderAsync(string folderName, string folderPath) { - return OneDriveDataSource.CreateFolderAsync(UserId, folderName, folderPath); + var graph = GetGraphClient(); + return graph.CreateFolderAsync(UserId, folderName, folderPath); } /// public Task DeleteItemAsync(string itemPath) { - return OneDriveDataSource.DeleteItemAsync(UserId, itemPath); + var graph = GetGraphClient(); + return graph.DeleteItemAsync(UserId, itemPath); + } + + private static GraphServiceClient GetGraphClient() + { + var provider = ProviderManager.Instance.GlobalProvider; + if (provider == null || provider.State != ProviderState.SignedIn) + { + throw new InvalidOperationException($"The {nameof(ProviderManager.GlobalProvider)} must be set and signed in to perform this action."); + } + + return provider.GetClient(); } } } diff --git a/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionStorageHelper.cs b/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionStorageHelper.cs index ee150ae..8c22835 100644 --- a/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionStorageHelper.cs +++ b/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionStorageHelper.cs @@ -17,9 +17,9 @@ namespace CommunityToolkit.Graph.Helpers.RoamingSettings { /// - /// An IObjectStorageHelper implementation using open extensions on the Graph User for storing key/value pairs. + /// An ISettingsStorageHelper implementation using open extensions on the Graph User for storing key/value pairs. /// - public class UserExtensionStorageHelper : IRemoteSettingsStorageHelper + public class UserExtensionStorageHelper : ISettingsStorageHelper { private static readonly IList ReservedKeys = new List { "responseHeaders", "statusCode", "@odata.context" }; private static readonly SemaphoreSlim SyncLock = new (1); @@ -30,7 +30,7 @@ public class UserExtensionStorageHelper : IRemoteSettingsStorageHelper public EventHandler SyncCompleted { get; set; } /// - /// gets or sets an event that fires whenever a remote sync request has failed. + /// Gets or sets an event that fires whenever a remote sync request has failed. /// public EventHandler SyncFailed { get; set; } @@ -82,6 +82,21 @@ public static async Task CreateForCurrentUserAsync(s return new UserExtensionStorageHelper(extensionId, userId, objectSerializer); } + /// + /// Retrieve an instance of the GraphServiceClient, or throws an exception if not signed in. + /// + /// A instance. + protected static GraphServiceClient GetGraphClient() + { + var provider = ProviderManager.Instance.GlobalProvider; + if (provider == null || provider.State != ProviderState.SignedIn) + { + throw new InvalidOperationException($"The {nameof(ProviderManager.GlobalProvider)} must be set and signed in to perform this action."); + } + + return provider.GetClient(); + } + /// /// An indexer for easily accessing key values. /// @@ -156,14 +171,16 @@ public virtual async Task Sync() try { + var graph = GetGraphClient(); + IDictionary remoteData = null; // Check if the extension should be cleared. if (_cleared) { // Delete and re-create the remote extension. - await UserExtensionDataSource.DeleteExtension(UserId, ExtensionId); - Extension extension = await UserExtensionDataSource.CreateExtension(UserId, ExtensionId); + await graph.DeleteExtension(UserId, ExtensionId); + Extension extension = await graph.CreateExtension(UserId, ExtensionId); remoteData = extension.AdditionalData; _cleared = false; @@ -171,7 +188,7 @@ public virtual async Task Sync() else { // Get the remote extension. - Extension extension = await UserExtensionDataSource.GetExtension(UserId, ExtensionId); + Extension extension = await graph.GetExtension(UserId, ExtensionId); remoteData = extension.AdditionalData; } @@ -208,9 +225,10 @@ public virtual async Task Sync() SyncCompleted?.Invoke(this, new EventArgs()); } - catch + catch (Exception e) { SyncFailed?.Invoke(this, new EventArgs()); + throw e; } finally {