Skip to content

Commit c3ca20c

Browse files
Merge pull request #50 from garethj-msft/master
Add linq-based select and expand overloads to requests. This looks good.
2 parents eaf4723 + b29f6da commit c3ca20c

File tree

304 files changed

+9692
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

304 files changed

+9692
-2
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// ------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3+
// ------------------------------------------------------------------------------
4+
5+
namespace Microsoft.Graph
6+
{
7+
using System;
8+
using System.Linq;
9+
using System.Linq.Expressions;
10+
using System.Reflection;
11+
12+
/// <summary>
13+
/// Helper class to extract $select or $expand parameters from strongly-typed expressions.
14+
/// </summary>
15+
public static class ExpressionExtractHelper
16+
{
17+
/// <summary>
18+
/// Extract referenced members of the type T from the given expression as a list of strings
19+
/// </summary>
20+
/// <param name="expression">The expression to search</param>
21+
/// <param name="error">Message about what's wrong with the expression if return value is null</param>
22+
/// <returns>A comma-separated list of strings or null</returns>
23+
public static string ExtractMembers<T>(Expression<Func<T, object>> expression, out string error)
24+
{
25+
error = null;
26+
if (expression == null)
27+
{
28+
throw new ArgumentNullException(nameof(expression));
29+
}
30+
31+
// Search s => s.Foo
32+
var memberExpression = expression.Body as MemberExpression;
33+
if (memberExpression != null)
34+
{
35+
return ProcessSimpleMemberExpression<T>(memberExpression, ref error);
36+
}
37+
38+
// Search s => s.BarFromBaseType
39+
// Property base type expressions introduce an intermediate conversion operator.
40+
var convertExpression = expression.Body as UnaryExpression;
41+
if (convertExpression?.NodeType == ExpressionType.Convert)
42+
{
43+
memberExpression = convertExpression.Operand as MemberExpression;
44+
if (memberExpression != null)
45+
{
46+
return ProcessSimpleMemberExpression<T>(memberExpression, ref error);
47+
}
48+
}
49+
50+
// Search s => new { [Foo = ]s.Foo, [bar = ]s.Bar }
51+
// We'd prefer not to support the variant with named anonymous type members, but the expression trees don't differentiate,
52+
// between implicit and explicit naming, so there's no way to throw an error.
53+
var newExpression = expression.Body as NewExpression;
54+
if (newExpression != null)
55+
{
56+
if (newExpression.Arguments == null || newExpression.Arguments.Count == 0)
57+
{
58+
error = "Lambda expression must provide initializer for new anonymous type.";
59+
return null;
60+
}
61+
if (newExpression.Arguments.Any(a =>
62+
{
63+
var memberArgument = a as MemberExpression;
64+
return memberArgument == null ||
65+
!(memberArgument.Expression is ParameterExpression) ||
66+
!memberArgument.Member.DeclaringType.GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo());
67+
}))
68+
{
69+
error = $"Anonymous type in lambda expression may only be initialized with direct members of type {typeof(T).Name}";
70+
return null;
71+
}
72+
73+
// Search only for direct members of the lambda's parameter
74+
// Should already be validated above, but doesn't hurt to be sure.
75+
var members = from m in newExpression.Arguments.OfType<MemberExpression>()
76+
where m.Expression is ParameterExpression && m.Member.DeclaringType.GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())
77+
select GetMemberWireName(m.Member);
78+
return string.Join(",", members);
79+
}
80+
error = "Unrecognized lambda expression.";
81+
return null;
82+
}
83+
84+
private static string ProcessSimpleMemberExpression<T>(MemberExpression memberExpression, ref string error)
85+
{
86+
if (!memberExpression.Member.DeclaringType.GetTypeInfo().IsAssignableFrom(typeof (T).GetTypeInfo()))
87+
{
88+
error = $"Anonymous type in lambda expression may only be initialized with direct members of type {typeof (T).Name}";
89+
return null;
90+
}
91+
return GetMemberWireName(memberExpression.Member);
92+
}
93+
94+
private static string GetMemberWireName(MemberInfo member)
95+
{
96+
var jsonProperty = member.GetCustomAttribute<Newtonsoft.Json.JsonPropertyAttribute>();
97+
if (jsonProperty != null && !string.IsNullOrWhiteSpace(jsonProperty.PropertyName))
98+
{
99+
return jsonProperty.PropertyName;
100+
}
101+
return StringHelper.ConvertIdentifierToLowerCamelCase(member.Name);
102+
}
103+
}
104+
}

src/Microsoft.Graph.Core/Helpers/StringHelper.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,19 @@ public static string ConvertTypeToLowerCamelCase(string typeString)
4141

4242
return typeString;
4343
}
44+
45+
/// <summary>
46+
/// Converts the identifier string to lower camel case.
47+
/// </summary>
48+
/// <param name="identifierString">The identifier string.</param>
49+
/// <returns>The converted string.</returns>
50+
public static string ConvertIdentifierToLowerCamelCase(string identifierString)
51+
{
52+
if (!string.IsNullOrEmpty(identifierString))
53+
{
54+
return string.Concat(identifierString.Substring(0, 1).ToLowerInvariant(), identifierString.Substring(1));
55+
}
56+
return identifierString;
57+
}
4458
}
4559
}

src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<Compile Include="Exceptions\ErrorConstants.cs" />
4747
<Compile Include="Exceptions\ErrorResponse.cs" />
4848
<Compile Include="Exceptions\ServiceException.cs" />
49+
<Compile Include="Helpers\ExtractSelectHelper.cs" />
4950
<Compile Include="Helpers\StringHelper.cs" />
5051
<Compile Include="Helpers\UrlHelper.cs" />
5152
<Compile Include="Models\AsyncOperationStatus.cs" />

src/Microsoft.Graph/Requests/Generated/AttachmentRequest.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace Microsoft.Graph
1212
using System.IO;
1313
using System.Net.Http;
1414
using System.Threading;
15+
using System.Linq.Expressions;
1516

1617
/// <summary>
1718
/// The type AttachmentRequest.
@@ -135,6 +136,30 @@ public IAttachmentRequest Expand(string value)
135136
return this;
136137
}
137138

139+
/// <summary>
140+
/// Adds the specified expand value to the request.
141+
/// </summary>
142+
/// <param name="expandExpression">The expression from which to calculate the expand value.</param>
143+
/// <returns>The request object to send.</returns>
144+
public IAttachmentRequest Expand(Expression<Func<Attachment, object>> expandExpression)
145+
{
146+
if (expandExpression == null)
147+
{
148+
throw new ArgumentNullException(nameof(expandExpression));
149+
}
150+
string error;
151+
string value = ExpressionExtractHelper.ExtractMembers(expandExpression, out error);
152+
if (value == null)
153+
{
154+
throw new ArgumentException(error, nameof(expandExpression));
155+
}
156+
else
157+
{
158+
this.QueryOptions.Add(new QueryOption("$expand", value));
159+
}
160+
return this;
161+
}
162+
138163
/// <summary>
139164
/// Adds the specified select value to the request.
140165
/// </summary>
@@ -146,6 +171,30 @@ public IAttachmentRequest Select(string value)
146171
return this;
147172
}
148173

174+
/// <summary>
175+
/// Adds the specified select value to the request.
176+
/// </summary>
177+
/// <param name="selectExpression">The expression from which to calculate the select value.</param>
178+
/// <returns>The request object to send.</returns>
179+
public IAttachmentRequest Select(Expression<Func<Attachment, object>> selectExpression)
180+
{
181+
if (selectExpression == null)
182+
{
183+
throw new ArgumentNullException(nameof(selectExpression));
184+
}
185+
string error;
186+
string value = ExpressionExtractHelper.ExtractMembers(selectExpression, out error);
187+
if (value == null)
188+
{
189+
throw new ArgumentException(error, nameof(selectExpression));
190+
}
191+
else
192+
{
193+
this.QueryOptions.Add(new QueryOption("$select", value));
194+
}
195+
return this;
196+
}
197+
149198
/// <summary>
150199
/// Initializes any collection properties after deserialization, like next requests for paging.
151200
/// </summary>

src/Microsoft.Graph/Requests/Generated/CalendarCalendarViewCollectionRequest.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace Microsoft.Graph
1111
using System.Collections.Generic;
1212
using System.Net.Http;
1313
using System.Threading;
14+
using System.Linq.Expressions;
1415

1516
/// <summary>
1617
/// The type CalendarCalendarViewCollectionRequest.
@@ -109,6 +110,30 @@ public ICalendarCalendarViewCollectionRequest Expand(string value)
109110
return this;
110111
}
111112

113+
/// <summary>
114+
/// Adds the specified expand value to the request.
115+
/// </summary>
116+
/// <param name="expandExpression">The expression from which to calculate the expand value.</param>
117+
/// <returns>The request object to send.</returns>
118+
public ICalendarCalendarViewCollectionRequest Expand(Expression<Func<Event, object>> expandExpression)
119+
{
120+
if (expandExpression == null)
121+
{
122+
throw new ArgumentNullException(nameof(expandExpression));
123+
}
124+
string error;
125+
string value = ExpressionExtractHelper.ExtractMembers(expandExpression, out error);
126+
if (value == null)
127+
{
128+
throw new ArgumentException(error, nameof(expandExpression));
129+
}
130+
else
131+
{
132+
this.QueryOptions.Add(new QueryOption("$expand", value));
133+
}
134+
return this;
135+
}
136+
112137
/// <summary>
113138
/// Adds the specified select value to the request.
114139
/// </summary>
@@ -120,6 +145,30 @@ public ICalendarCalendarViewCollectionRequest Select(string value)
120145
return this;
121146
}
122147

148+
/// <summary>
149+
/// Adds the specified select value to the request.
150+
/// </summary>
151+
/// <param name="selectExpression">The expression from which to calculate the select value.</param>
152+
/// <returns>The request object to send.</returns>
153+
public ICalendarCalendarViewCollectionRequest Select(Expression<Func<Event, object>> selectExpression)
154+
{
155+
if (selectExpression == null)
156+
{
157+
throw new ArgumentNullException(nameof(selectExpression));
158+
}
159+
string error;
160+
string value = ExpressionExtractHelper.ExtractMembers(selectExpression, out error);
161+
if (value == null)
162+
{
163+
throw new ArgumentException(error, nameof(selectExpression));
164+
}
165+
else
166+
{
167+
this.QueryOptions.Add(new QueryOption("$select", value));
168+
}
169+
return this;
170+
}
171+
123172
/// <summary>
124173
/// Adds the specified top value to the request.
125174
/// </summary>

src/Microsoft.Graph/Requests/Generated/CalendarEventsCollectionRequest.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace Microsoft.Graph
1111
using System.Collections.Generic;
1212
using System.Net.Http;
1313
using System.Threading;
14+
using System.Linq.Expressions;
1415

1516
/// <summary>
1617
/// The type CalendarEventsCollectionRequest.
@@ -109,6 +110,30 @@ public ICalendarEventsCollectionRequest Expand(string value)
109110
return this;
110111
}
111112

113+
/// <summary>
114+
/// Adds the specified expand value to the request.
115+
/// </summary>
116+
/// <param name="expandExpression">The expression from which to calculate the expand value.</param>
117+
/// <returns>The request object to send.</returns>
118+
public ICalendarEventsCollectionRequest Expand(Expression<Func<Event, object>> expandExpression)
119+
{
120+
if (expandExpression == null)
121+
{
122+
throw new ArgumentNullException(nameof(expandExpression));
123+
}
124+
string error;
125+
string value = ExpressionExtractHelper.ExtractMembers(expandExpression, out error);
126+
if (value == null)
127+
{
128+
throw new ArgumentException(error, nameof(expandExpression));
129+
}
130+
else
131+
{
132+
this.QueryOptions.Add(new QueryOption("$expand", value));
133+
}
134+
return this;
135+
}
136+
112137
/// <summary>
113138
/// Adds the specified select value to the request.
114139
/// </summary>
@@ -120,6 +145,30 @@ public ICalendarEventsCollectionRequest Select(string value)
120145
return this;
121146
}
122147

148+
/// <summary>
149+
/// Adds the specified select value to the request.
150+
/// </summary>
151+
/// <param name="selectExpression">The expression from which to calculate the select value.</param>
152+
/// <returns>The request object to send.</returns>
153+
public ICalendarEventsCollectionRequest Select(Expression<Func<Event, object>> selectExpression)
154+
{
155+
if (selectExpression == null)
156+
{
157+
throw new ArgumentNullException(nameof(selectExpression));
158+
}
159+
string error;
160+
string value = ExpressionExtractHelper.ExtractMembers(selectExpression, out error);
161+
if (value == null)
162+
{
163+
throw new ArgumentException(error, nameof(selectExpression));
164+
}
165+
else
166+
{
167+
this.QueryOptions.Add(new QueryOption("$select", value));
168+
}
169+
return this;
170+
}
171+
123172
/// <summary>
124173
/// Adds the specified top value to the request.
125174
/// </summary>

0 commit comments

Comments
 (0)