Skip to content

Commit 25be48a

Browse files
authored
Merge pull request #18 from windows-toolkit/michael-hawker/graph-presenter
Initial GraphPresenter prototype for #17
2 parents d5c0517 + c2dd9ea commit 25be48a

File tree

11 files changed

+453
-39
lines changed

11 files changed

+453
-39
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Threading;
9+
using Microsoft.Graph;
10+
using Microsoft.Toolkit.Uwp.Helpers;
11+
using Newtonsoft.Json.Linq;
12+
using Windows.UI.Xaml;
13+
using Windows.UI.Xaml.Controls;
14+
15+
namespace Microsoft.Toolkit.Graph.Controls
16+
{
17+
/// <summary>
18+
/// Specialized <see cref="ContentPresenter"/> to fetch and display data from the Microsoft Graph.
19+
/// </summary>
20+
public class GraphPresenter : ContentPresenter
21+
{
22+
/// <summary>
23+
/// Gets or sets a <see cref="IBaseRequestBuilder"/> to be used to make a request to the graph. The results will be automatically populated to the <see cref="ContentPresenter.Content"/> property. Use a <see cref="ContentPresenter.ContentTemplate"/> to change the presentation of the data.
24+
/// </summary>
25+
public IBaseRequestBuilder RequestBuilder
26+
{
27+
get { return (IBaseRequestBuilder)GetValue(RequestBuilderProperty); }
28+
set { SetValue(RequestBuilderProperty, value); }
29+
}
30+
31+
/// <summary>
32+
/// Identifies the <see cref="RequestBuilder"/> dependency property.
33+
/// </summary>
34+
/// <returns>
35+
/// The identifier for the <see cref="RequestBuilder"/> dependency property.
36+
/// </returns>
37+
public static readonly DependencyProperty RequestBuilderProperty =
38+
DependencyProperty.Register(nameof(RequestBuilder), typeof(IBaseRequestBuilder), typeof(GraphPresenter), new PropertyMetadata(null));
39+
40+
/// <summary>
41+
/// Gets or sets the <see cref="Type"/> of item returned by the <see cref="RequestBuilder"/>.
42+
/// Set to the base item type and use the <see cref="IsCollection"/> property to indicate if a collection is expected back.
43+
/// </summary>
44+
public Type ResponseType { get; set; }
45+
46+
/// <summary>
47+
/// Gets or sets a value indicating whether the returned data from the <see cref="RequestBuilder"/> is a collection.
48+
/// </summary>
49+
public bool IsCollection { get; set; }
50+
51+
/// <summary>
52+
/// Gets or sets list of <see cref="QueryOption"/> representing <see cref="Microsoft.Graph.QueryOption"/> values to pass into the request built by <see cref="RequestBuilder"/>.
53+
/// </summary>
54+
public List<QueryOption> QueryOptions { get; set; } = new List<QueryOption>();
55+
56+
/// <summary>
57+
/// Gets or sets a string to indicate a sorting order for the <see cref="RequestBuilder"/>. This is a helper to add this specific request option to the <see cref="QueryOptions"/>.
58+
/// </summary>
59+
public string OrderBy { get; set; }
60+
61+
/// <summary>
62+
/// Initializes a new instance of the <see cref="GraphPresenter"/> class.
63+
/// </summary>
64+
public GraphPresenter()
65+
{
66+
Loaded += GraphPresenter_Loaded;
67+
}
68+
69+
private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e)
70+
{
71+
// Note: some interfaces from the Graph SDK don't implement IBaseRequestBuilder properly, see https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/722
72+
if (RequestBuilder != null)
73+
{
74+
var request = new BaseRequest(
75+
RequestBuilder.RequestUrl,
76+
RequestBuilder.Client); // TODO: Do we need separate Options here?
77+
request.Method = "GET";
78+
request.QueryOptions = QueryOptions?.Select(option => (Microsoft.Graph.QueryOption)option)?.ToList() ?? new List<Microsoft.Graph.QueryOption>();
79+
80+
// Handle Special QueryOptions
81+
if (!string.IsNullOrWhiteSpace(OrderBy))
82+
{
83+
request.QueryOptions.Add(new Microsoft.Graph.QueryOption("$orderby", OrderBy));
84+
}
85+
86+
try
87+
{
88+
var response = await request.SendAsync<object>(null, CancellationToken.None).ConfigureAwait(false) as JObject;
89+
90+
//// TODO: Deal with paging?
91+
92+
var values = response["value"];
93+
object data = null;
94+
95+
if (IsCollection)
96+
{
97+
data = values.ToObject(Array.CreateInstance(ResponseType, 0).GetType());
98+
}
99+
else
100+
{
101+
data = values.ToObject(ResponseType);
102+
}
103+
104+
_ = DispatcherHelper.ExecuteOnUIThreadAsync(() => Content = data);
105+
}
106+
catch
107+
{
108+
// TODO: We should figure out what we want to do for Loading/Error states here.
109+
}
110+
}
111+
}
112+
}
113+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
using Windows.UI.Xaml;
11+
12+
namespace Microsoft.Toolkit.Graph.Controls
13+
{
14+
/// <summary>
15+
/// XAML Proxy for <see cref="Microsoft.Graph.QueryOption"/>.
16+
/// </summary>
17+
public sealed class QueryOption
18+
{
19+
/// <inheritdoc cref="Microsoft.Graph.Option.Name"/>
20+
public string Name { get; set; }
21+
22+
/// <inheritdoc cref="Microsoft.Graph.Option.Value"/>
23+
public string Value { get; set; }
24+
25+
/// <summary>
26+
/// Implicit conversion for <see cref="QueryOption"/> to <see cref="Microsoft.Graph.QueryOption"/>.
27+
/// </summary>
28+
/// <param name="option">query option to convert.</param>
29+
public static implicit operator Microsoft.Graph.QueryOption(QueryOption option)
30+
{
31+
return new Microsoft.Graph.QueryOption(option.Name, option.Value);
32+
}
33+
}
34+
}

Microsoft.Toolkit.Graph.Controls/Microsoft.Toolkit.Graph.Controls.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<PackageTags>UWP Toolkit Windows Controls MSAL Microsoft Graph AadLogin ProfileCard Person PeoplePicker Login</PackageTags>
1616
<SignAssembly>false</SignAssembly>
1717
<GenerateLibraryLayout>true</GenerateLibraryLayout>
18+
<LangVersion>8.0</LangVersion>
1819
<Configurations>Debug;Release;CI</Configurations>
1920
<Platforms>AnyCPU;ARM;ARM64;x64;x86</Platforms>
2021
</PropertyGroup>
@@ -34,8 +35,8 @@
3435
</ItemGroup>
3536

3637
<ItemGroup>
37-
<PackageReference Include="Microsoft.Graph.Beta" Version="0.18.0-preview" />
38-
<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.4" />
38+
<PackageReference Include="Microsoft.Graph.Beta" Version="0.19.0-preview" />
39+
<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.5" />
3940
</ItemGroup>
4041

4142
<ItemGroup>

Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System;
56
using System.IO;
67
using System.Threading.Tasks;
78
using Microsoft.Graph;

Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
</PropertyGroup>
2222

2323
<ItemGroup>
24-
<PackageReference Include="Microsoft.Graph.Beta" Version="0.18.0-preview" />
25-
<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.4" />
24+
<PackageReference Include="Microsoft.Graph.Beta" Version="0.19.0-preview" />
25+
<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.5" />
2626
<PackageReference Include="Microsoft.Toolkit" Version="6.1.0" />
2727
</ItemGroup>
2828
</Project>

Microsoft.Toolkit.Graph/Providers/MockProvider.cs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ namespace Microsoft.Toolkit.Graph.Providers
2121
/// </summary>
2222
public class MockProvider : IProvider
2323
{
24+
private const string GRAPH_PROXY_URL = "https://proxy.apisandbox.msdn.microsoft.com/svc?url=";
25+
2426
private ProviderState _state = ProviderState.Loading;
2527

2628
/// <inheritdoc/>
@@ -42,22 +44,15 @@ private set
4244

4345
/// <inheritdoc/>
4446
public GraphServiceClient Graph => new GraphServiceClient(
45-
"https://proxy.apisandbox.msdn.microsoft.com/svc?url=" + HttpUtility.HtmlEncode("https://graph.microsoft.com/beta/"),
46-
new DelegateAuthenticationProvider((requestMessage) =>
47-
{
48-
//// Temporary Workaround for https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/59
49-
//// ------------------------
50-
var requestUri = requestMessage.RequestUri.ToString();
51-
var index = requestUri.IndexOf("&");
52-
if (index >= 0)
53-
{
54-
requestMessage.RequestUri = new Uri(requestUri.Remove(index, 1).Insert(index, "?"));
55-
}
56-
57-
//// End Workaround
58-
59-
return this.AuthenticateRequestAsync(requestMessage);
60-
}));
47+
new DelegateAuthenticationProvider((requestMessage) =>
48+
{
49+
var requestUri = requestMessage.RequestUri.ToString();
50+
51+
// Prepend Proxy Service URI to our request
52+
requestMessage.RequestUri = new Uri(GRAPH_PROXY_URL + Uri.EscapeDataString(requestUri));
53+
54+
return this.AuthenticateRequestAsync(requestMessage);
55+
}));
6156

6257
/// <inheritdoc/>
6358
public event EventHandler<StateChangedEventArgs> StateChanged;

Microsoft.Toolkit.Graph/Providers/ProviderManager.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.ComponentModel;
7+
using Microsoft.Graph;
8+
using Microsoft.Identity.Client;
9+
using Microsoft.Toolkit.Helpers;
610

711
namespace Microsoft.Toolkit.Graph.Providers
812
{
@@ -14,7 +18,7 @@ namespace Microsoft.Toolkit.Graph.Providers
1418
/// ProviderManager.Instance.GlobalProvider = await MsalProvider.CreateAsync(...);
1519
/// </code>
1620
/// </example>
17-
public class ProviderManager
21+
public class ProviderManager : INotifyPropertyChanged
1822
{
1923
/// <summary>
2024
/// Gets the name of the toolkit client to identify self in Graph calls.
@@ -31,6 +35,9 @@ public class ProviderManager
3135
/// </summary>
3236
public event EventHandler<ProviderUpdatedEventArgs> ProviderUpdated;
3337

38+
/// <inheritdoc/>
39+
public event PropertyChangedEventHandler PropertyChanged;
40+
3441
private IProvider _provider;
3542

3643
/// <summary>
@@ -58,6 +65,8 @@ public IProvider GlobalProvider
5865
}
5966

6067
ProviderUpdated?.Invoke(this, new ProviderUpdatedEventArgs(ProviderManagerChangedState.ProviderChanged));
68+
69+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(GlobalProvider)));
6170
}
6271
}
6372

SampleTest/Assets/FileIcon.png

2.77 KB
Loading

0 commit comments

Comments
 (0)