Skip to content

Commit a2dd391

Browse files
authored
Merge pull request #53 from Linq2GraphQL/rewrite-expression-parser
Rewrite expression parser
2 parents b57d38d + 5958372 commit a2dd391

File tree

82 files changed

+1067
-306
lines changed

Some content is hidden

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

82 files changed

+1067
-306
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Linq2GraphQL.Client;
2+
3+
[AttributeUsage(AttributeTargets.Parameter)]
4+
public class GraphQLArgumentAttribute(string graphQLName, string graphQLType) : Attribute
5+
{
6+
public string GraphQLType { get; private set; } = graphQLType;
7+
public string GraphQLName { get; private set; } = graphQLName;
8+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace Linq2GraphQL.Client;
4+
5+
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
6+
public class GraphQLMemberAttribute : Attribute
7+
{
8+
public GraphQLMemberAttribute(string graphQLName)
9+
{
10+
GraphQLName = graphQLName;
11+
}
12+
13+
public string GraphQLName { get; private set; }
14+
}

src/Linq2GraphQL.Client/Common/ICursorPaging.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace Linq2GraphQL.Client.Common
99
{
1010
public interface ICursorPaging
1111
{
12+
[GraphQLMember("pageInfo")]
13+
[JsonPropertyName("pageInfo")]
1214
public PageInfo PageInfo { get; set; }
1315

1416
}

src/Linq2GraphQL.Client/Common/IPageInfo.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@ namespace Linq2GraphQL.Client.Common
99
{
1010
public class PageInfo
1111
{
12+
[GraphQLMember("hasNextPage")]
1213
[JsonPropertyName("hasNextPage")]
1314
public bool HasNextPage { get; set; }
15+
16+
[GraphQLMember("hasPreviousPage")]
1417
[JsonPropertyName("hasPreviousPage")]
1518
public bool HasPreviousPage { get; set; }
19+
20+
[GraphQLMember("startCursor")]
1621
[JsonPropertyName("startCursor")]
1722
public string StartCursor { get; set; }
23+
24+
[GraphQLMember("endCursor")]
1825
[JsonPropertyName("endCursor")]
1926
public string EndCursor { get; set; }
2027

src/Linq2GraphQL.Client/Converters/CustomScalarConverter.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
namespace Linq2GraphQL.Client
77
{
88

9-
10-
11-
129
public class CustomScalarConverter<TScalar> : JsonConverter<TScalar>
1310
where TScalar : CustomScalar, new()
1411
{

src/Linq2GraphQL.Client/GraphBase.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ public GraphBase(GraphClient client, string name, OperationType operationType, L
1717

1818
public QueryNode QueryNode { get; }
1919

20+
21+
/// <summary>
22+
/// Include top node
23+
/// </summary>
24+
/// <returns></returns>
25+
public TGraph Include()
26+
{
27+
QueryNode.IncludePrimitive = true;
28+
return (TGraph)(object)this;
29+
}
30+
2031
public TGraph Include<TProperty>(Expression<Func<T, TProperty>> path)
2132
{
2233
Utilities.ParseExpression(path, QueryNode);

src/Linq2GraphQL.Client/GraphClient.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Text.Json;
22
using System.Text.Json.Serialization;
3+
using Linq2GraphQL.Client.Converters;
34
using Linq2GraphQL.Client.Schema;
45
using Microsoft.Extensions.Caching.Memory;
56
using Microsoft.Extensions.DependencyInjection;
@@ -25,7 +26,7 @@ public GraphClient(HttpClient httpClient, IOptions<GraphClientOptions> options,
2526
SerializerOptions = new()
2627
{
2728
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
28-
Converters = { }
29+
Converters = { },
2930
};
3031

3132
SubscriptionUrl = GetSubscriptionUrl();
@@ -35,6 +36,7 @@ public GraphClient(HttpClient httpClient, IOptions<GraphClientOptions> options,
3536
public SubscriptionProtocol SubscriptionProtocol => options.Value.SubscriptionProtocol;
3637
public HttpClient HttpClient { get; }
3738
public JsonSerializerOptions SerializerOptions { get; }
39+
3840

3941
public Func<GraphClient, Task<GraphQLRequest>> WSConnectionInitPayload => options.Value.WSConnectionInitPayload;
4042
private string GetSubscriptionUrl()

src/Linq2GraphQL.Client/QueryNode.cs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class QueryNode
1414

1515
public QueryNode(MemberInfo member, string name = null, List<ArgumentValue> arguments = null, bool interfaceProperty = false, bool topLevel = false)
1616
{
17-
Name = name ?? member.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name ?? member.Name.ToCamelCase();
17+
Name = name ?? member.GetCustomAttribute<GraphQLMemberAttribute>()?.GraphQLName ?? member.Name.ToCamelCase();
1818
Member = member;
1919
Arguments = arguments ?? new List<ArgumentValue>();
2020
underlyingMemberType = member.GetUnderlyingType();
@@ -62,17 +62,6 @@ private static bool MustHaveChildren(Type type)
6262
!type.IsListOfPrimitiveTypeOrString();
6363
}
6464

65-
public void SetAddPrimitiveChildren()
66-
{
67-
if (!ChildNodes.Any())
68-
{
69-
IncludePrimitive = true;
70-
}
71-
foreach (var childNode in ChildNodes)
72-
{
73-
childNode.SetAddPrimitiveChildren();
74-
}
75-
}
7665

7766
public void AddChildNode(MemberInfo member, string name = null)
7867
{
@@ -82,15 +71,15 @@ public void AddChildNode(MemberInfo member, string name = null)
8271
public int Level => Parent?.Level + 1 ?? 1;
8372
public int Leaf { get; internal set; } = 1;
8473

85-
public void AddChildNode(QueryNode childNode)
74+
public QueryNode AddChildNode(QueryNode childNode)
8675
{
8776
var currentNode = ChildNodes.FirstOrDefault(e => e.Name == childNode.Name && e.argumentHashCodeId == childNode.argumentHashCodeId);
8877
if (currentNode == null)
8978
{
9079
childNode.Parent = this;
9180
childNode.Leaf = ChildNodes.Count + 1;
9281
ChildNodes.Add(childNode);
93-
return;
82+
return childNode;
9483
}
9584
else if (childNode.IncludePrimitive)
9685
{
@@ -101,6 +90,9 @@ public void AddChildNode(QueryNode childNode)
10190
{
10291
currentNode.AddChildNode(child);
10392
}
93+
94+
return currentNode;
95+
10496
}
10597

10698
public void SetArgumentValue(string graphName, object value)
@@ -127,23 +119,25 @@ public void AddPrimitiveChildren(bool recursive, GraphQLSchema schema)
127119
var typeOrListType = underlyingMemberType.GetTypeOrListType();
128120
foreach (var propertyInfo in typeOrListType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
129121
{
130-
if (propertyInfo.GetCustomAttribute<GraphShadowPropertyAttribute>() != null)
122+
123+
if (!propertyInfo.PropertyType.IsValueTypeOrString())
131124
{
132125
continue;
133126
}
134127

135-
if (!propertyInfo.PropertyType.IsValueTypeOrString() || propertyInfo.GetCustomAttribute<GraphShadowPropertyAttribute>() != null)
128+
129+
var memberAttribute = propertyInfo.GetCustomAttribute<GraphQLMemberAttribute>();
130+
if (memberAttribute == null)
136131
{
137132
continue;
138133
}
139134

135+
140136
if (schema != null)
141137
{
142-
var name = propertyInfo.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name ??
143-
Member.Name.ToCamelCase();
144-
if (schema.TypePropertyExists(typeOrListType.Name, name))
138+
if (schema.TypePropertyExists(typeOrListType.Name, memberAttribute.GraphQLName))
145139
{
146-
AddChildNode(propertyInfo, name);
140+
AddChildNode(propertyInfo, memberAttribute.GraphQLName);
147141
}
148142
else
149143
{

src/Linq2GraphQL.Client/Utilities.cs

Lines changed: 7 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
using System.Linq.Expressions;
2-
using System.Reflection;
3-
using System.Reflection.Metadata.Ecma335;
4-
1+
using Linq2GraphQL.Client.Visitors;
2+
using System.Linq.Expressions;
53
namespace Linq2GraphQL.Client;
64

75
public static class Utilities
@@ -24,164 +22,13 @@ public static string GetArgumentsId(IEnumerable<object> objects)
2422
}
2523
}
2624

27-
private static bool IsSelectOrSelectMany(this MethodCallExpression methodCallExpression)
28-
{
29-
if (methodCallExpression.Arguments.Count != 2)
30-
{
31-
return false;
32-
}
33-
34-
;
35-
var methodName = methodCallExpression.Method.Name;
36-
return (methodName == "Select" || methodName == "SelectMany");
37-
}
38-
3925
public static void ParseExpression(Expression body, QueryNode parent)
4026
{
41-
var node = new QueryNode(parent.Member);
42-
ParseExpressionInternal(body, node);
43-
node.SetAddPrimitiveChildren();
44-
45-
foreach (var childNode in node.ChildNodes)
46-
{
47-
parent.AddChildNode(childNode);
48-
}
49-
}
50-
51-
private static void ParseExpressionInternal(Expression body, QueryNode parent)
52-
{
53-
if (body.NodeType == ExpressionType.MemberInit)
54-
{
55-
var exp = (MemberInitExpression)body;
56-
foreach (var binding in exp.Bindings.Where(e => e.BindingType == MemberBindingType.Assignment)
57-
.Cast<MemberAssignment>())
58-
{
59-
ParseExpressionInternal(binding.Expression, parent);
60-
}
61-
}
62-
63-
switch (body)
64-
{
65-
case LambdaExpression lambdaExpression:
66-
ParseExpressionInternal(lambdaExpression.Body, parent);
67-
break;
68-
69-
case MemberExpression memberExpression:
70-
var (parentNode, _) = GetMemberQueryNode(memberExpression);
71-
parent.AddChildNode(parentNode);
72-
break;
73-
74-
case MethodCallExpression methodCallExp:
75-
ParseMethodCallExpression(parent, methodCallExp);
76-
break;
77-
78-
case NewExpression newExpression:
79-
foreach (var argument in newExpression.Arguments)
80-
{
81-
ParseExpression(argument, parent);
82-
}
83-
84-
break;
85-
}
86-
}
87-
88-
private static void ParseMethodCallExpression(QueryNode parent, MethodCallExpression methodCallExp)
89-
{
90-
var graphInterfaceAttribute = methodCallExp.Method.GetCustomAttribute<GraphInterfaceAttribute>();
91-
if (graphInterfaceAttribute != null)
92-
{
93-
var queryNode = new QueryNode(methodCallExp.Method, methodCallExp.Method.Name, null, true);
94-
parent.AddChildNode(queryNode);
95-
return;
96-
}
97-
98-
var graphMethodAttribute = methodCallExp.Method.GetCustomAttribute<GraphMethodAttribute>();
99-
if (graphMethodAttribute != null)
100-
{
101-
var arguments = new List<ArgumentValue>();
102-
103-
var i = 0;
104-
foreach (var parameter in methodCallExp.Method.GetParameters())
105-
{
106-
var graphAttribute = parameter.GetCustomAttribute<GraphArgumentAttribute>();
107-
if (graphAttribute != null)
108-
{
109-
var arg = methodCallExp.Arguments[i];
110-
ConstantExpression argConstant;
111-
if (arg.NodeType == ExpressionType.Convert)
112-
{
113-
var unaryExpression = (UnaryExpression)arg;
114-
argConstant = (ConstantExpression)unaryExpression.Operand;
115-
}
116-
else
117-
{
118-
argConstant = (ConstantExpression)arg;
119-
}
120-
121-
arguments.Add(new ArgumentValue(parameter.Name, graphAttribute.GraphType,
122-
argConstant.Value));
123-
}
124-
125-
i++;
126-
}
127-
128-
var queryNode = new QueryNode(methodCallExp.Method, graphMethodAttribute.GraphName, arguments);
129-
parent.AddChildNode(queryNode);
130-
}
131-
else if (methodCallExp.IsSelectOrSelectMany())
132-
{
133-
if (methodCallExp.Arguments[0] is MemberExpression memberExpr)
134-
{
135-
var (ParentNode, LastNode) = GetMemberQueryNode(memberExpr);
136-
ParseExpressionInternal(methodCallExp.Arguments[1], LastNode);
137-
parent.AddChildNode(ParentNode);
138-
}
139-
else
140-
{
141-
ParseExpressionInternal(methodCallExp.Arguments[1], parent);
142-
}
143-
}
144-
}
145-
146-
private static (QueryNode ParentNode, QueryNode LastNode) GetMemberQueryNode(Expression expression)
147-
{
148-
var members = GetMembers(expression);
149-
if (members == null) return (null, null);
150-
151-
members.Reverse();
152-
153-
QueryNode parentNode = null;
154-
QueryNode currentNode = null;
155-
156-
foreach (var member in members)
157-
{
158-
var newNode = new QueryNode(member);
159-
if (parentNode == null)
160-
{
161-
parentNode = newNode;
162-
}
163-
else
164-
{
165-
currentNode.AddChildNode(newNode);
166-
}
167-
168-
currentNode = newNode;
169-
}
170-
171-
return (parentNode, currentNode);
27+
var parameterVisitor = new ParameterVisitor(new MemberNode(null, null));
28+
var topNode = parameterVisitor.ParseExpression(body);
29+
30+
topNode.PopulateChildQueryNodes(parent);
31+
17232
}
17333

174-
175-
private static List<MemberInfo> GetMembers(Expression expression)
176-
{
177-
var members = new List<MemberInfo>();
178-
if (expression.NodeType == ExpressionType.MemberAccess)
179-
{
180-
var memberExpression = (MemberExpression)expression;
181-
members.Add(memberExpression.Member);
182-
members.AddRange(GetMembers(memberExpression.Expression));
183-
}
184-
185-
return members;
186-
}
18734
}

0 commit comments

Comments
 (0)