Skip to content

Commit 5054232

Browse files
FIND-13094: Add recursive directive
- Add Inline() and Recursive() on FragmentBuilder - Add FragmentExtension for build multiple recursion on FragmentBuilder - Obsolete AsType method, replacing with InlineFragment method
1 parent dd86b65 commit 5054232

File tree

7 files changed

+486
-31
lines changed

7 files changed

+486
-31
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace EPiServer.ContentGraph.Api.Querying
2+
{
3+
public class DeligateRecursiveBuilder : Recursion
4+
{
5+
private Func<string, Recursion> filterDelegate;
6+
public DeligateRecursiveBuilder(Func<string, Recursion> filterDelegate = null)
7+
{
8+
this.filterDelegate = filterDelegate;
9+
}
10+
public Recursion GetReturnType(string fieldName)
11+
{
12+
return filterDelegate(fieldName);
13+
}
14+
}
15+
}

APIs/src/EpiServer.ContentGraph/Api/Querying/FragmentBuilder.cs

Lines changed: 143 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,67 @@
1-
using EPiServer.ContentGraph.Helpers;
1+
using EPiServer.ContentGraph.ExpressionHelper;
2+
using EPiServer.ContentGraph.Extensions;
3+
using EPiServer.ContentGraph.Helpers;
24
using EPiServer.ContentGraph.Helpers.Reflection;
35
using GraphQL.Transport;
46
using System;
57
using System.Linq.Expressions;
8+
using System.Text;
69
using System.Text.RegularExpressions;
710

811
namespace EPiServer.ContentGraph.Api.Querying
912
{
13+
public class Recursion
14+
{
15+
public string FieldName { get; set; }
16+
public int? RecursiveDepth { get; set; }
17+
public override string ToString()
18+
{
19+
if (RecursiveDepth.IsNull() || RecursiveDepth.Value < 0)
20+
{
21+
return ConvertNestedFieldToString.ConvertNestedFieldForQuery($"{FieldName} @recursive");
22+
}
23+
else
24+
{
25+
return ConvertNestedFieldToString.ConvertNestedFieldForQuery($"{FieldName} @recursive(depth:{RecursiveDepth.Value})");
26+
}
27+
}
28+
}
29+
public class FragmentBuilder : BaseTypeQueryBuilder
30+
{
31+
private List<FragmentBuilder> _childrenFragments;
32+
public IEnumerable<FragmentBuilder> ChildrenFragments => _childrenFragments;
33+
public bool HasChildren => _childrenFragments != null && _childrenFragments.Any();
34+
public FragmentBuilder() : base()
35+
{
36+
_query.OperationName = "sampleFragment";
37+
}
38+
public void OperationName(string name)
39+
{
40+
Regex reg = new Regex(@"^[a-zA-Z_]\w*$");
41+
if (reg.IsMatch(name))
42+
{
43+
_query.OperationName = name;
44+
}
45+
}
46+
public string GetName()
47+
{
48+
return _query.OperationName;
49+
}
50+
public override FragmentBuilder AddFragments(params FragmentBuilder[] fragments)
51+
{
52+
if (fragments.IsNotNull() && fragments.Length > 0)
53+
{
54+
if (_childrenFragments.IsNull())
55+
{
56+
_childrenFragments = new List<FragmentBuilder>();
57+
}
58+
base.AddFragments(fragments);
59+
_childrenFragments.AddRange(fragments);
60+
}
61+
return this;
62+
}
63+
}
64+
1065
public class FragmentBuilder<T> : FragmentBuilder
1166
{
1267
public FragmentBuilder() : base() { }
@@ -46,46 +101,107 @@ public FragmentBuilder<T> Children<TChildren>(TypeQueryBuilder<TChildren> childr
46101
base.Children(children);
47102
return this;
48103
}
49-
public override GraphQLRequest GetQuery()
104+
private FragmentBuilder Recursive<TSub>(params Recursion[] recursives) where TSub : T
50105
{
51-
_query.Query = $"fragment {_query.OperationName} on {typeof(T).Name} {graphObject}";
52-
return _query;
53-
}
54-
}
106+
recursives.ValidateNotNullArgument("recursives");
107+
if (!recursives.Any()) throw new ArgumentException("recursives can not be empty");
108+
string recursiveNess = string.Empty;
109+
foreach (var recursive in recursives)
110+
{
111+
recursiveNess += recursive.ToString();
112+
}
55113

56-
public class FragmentBuilder : BaseTypeQueryBuilder
57-
{
58-
private List<FragmentBuilder> _childrenFragments;
59-
public IEnumerable<FragmentBuilder> ChildrenFragments => _childrenFragments;
60-
public bool HasChildren => _childrenFragments != null && _childrenFragments.Any();
61-
public FragmentBuilder() : base()
114+
string subTypeName = typeof(TSub).Name;
115+
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
116+
$"... on {subTypeName}{{{recursiveNess}}}" :
117+
$" ... on {subTypeName}{{{recursiveNess}}}"
118+
);
119+
return this;
120+
}
121+
private FragmentBuilder<T> Recursive<TSub>(string fieldName, int? depth = null) where TSub : T
62122
{
63-
_query.OperationName = "sampleFragment";
123+
Recursive<TSub>(new Recursion() { FieldName = fieldName, RecursiveDepth = depth }); ;
124+
return this;
64125
}
65-
public void OperationName(string name)
126+
/// <summary>
127+
/// Recursive directive for a subtype
128+
/// </summary>
129+
/// <typeparam name="TSub">Subtype must inherits from type <typeparamref name="T"/></typeparam>
130+
/// <param name="fieldSelectors">Select field must inherits from <typeparamref name="T"/></param>
131+
/// <returns>FragmentBuilder</returns>
132+
public FragmentBuilder<T> Recursive<TSub>(Expression<Func<TSub, T>> fieldSelector, int? depth = null) where TSub : T
66133
{
67-
Regex reg = new Regex(@"^[a-zA-Z_]\w*$");
68-
if (reg.IsMatch(name))
69-
{
70-
_query.OperationName = name;
71-
}
134+
var fieldName = fieldSelector.GetFieldPath();
135+
Recursive<TSub>(fieldName, depth);
136+
return this;
72137
}
73-
public string GetName()
138+
/// <summary>
139+
/// Inline fragment on a subtype
140+
/// </summary>
141+
/// <typeparam name="TSub"></typeparam>
142+
/// <param name="fieldSelectors"></param>
143+
/// <returns></returns>
144+
public FragmentBuilder<T> Inline<TSub>(params string[] fields)
74145
{
75-
return _query.OperationName;
146+
fields.ValidateNotNullArgument("fields");
147+
StringBuilder propertyBuilder = new StringBuilder();
148+
foreach (var field in fields)
149+
{
150+
if (propertyBuilder.Length > 0)
151+
{
152+
propertyBuilder.Append(" ");
153+
}
154+
propertyBuilder.Append(ConvertNestedFieldToString.ConvertNestedFieldForQuery(field));
155+
}
156+
string subTypeName = typeof(TSub).Name;
157+
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
158+
$"... on {subTypeName}{{{propertyBuilder}}}" :
159+
$" ... on {subTypeName}{{{propertyBuilder}}}"
160+
);
161+
return this;
76162
}
77-
public override FragmentBuilder Fragments(params FragmentBuilder[] fragments)
163+
/// <summary>
164+
/// Inline fragment on a subtype
165+
/// </summary>
166+
/// <typeparam name="TSub"></typeparam>
167+
/// <param name="fieldSelectors"></param>
168+
/// <returns></returns>
169+
public FragmentBuilder<T> Inline<TSub>(params Expression<Func<TSub, object>>[] fieldSelectors) where TSub : T
78170
{
79-
if (fragments.IsNotNull() && fragments.Length > 0)
171+
fieldSelectors.ValidateNotNullArgument("fieldSelectors");
172+
StringBuilder propertyBuilder = new StringBuilder();
173+
foreach (var fieldSelector in fieldSelectors)
80174
{
81-
if (_childrenFragments.IsNull())
175+
if (propertyBuilder.Length > 0)
82176
{
83-
_childrenFragments = new List<FragmentBuilder>();
177+
propertyBuilder.Append(" ");
84178
}
85-
base.Fragments(fragments);
86-
_childrenFragments.AddRange(fragments);
179+
propertyBuilder.Append(ConvertNestedFieldToString.ConvertNestedFieldForQuery(fieldSelector.GetFieldPath()));
87180
}
181+
string subTypeName = typeof(TSub).Name;
182+
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
183+
$"... on {subTypeName}{{{propertyBuilder}}}" :
184+
$" ... on {subTypeName}{{{propertyBuilder}}}"
185+
);
186+
return this;
187+
}
188+
/// <summary>
189+
/// Inline fragment on a subtype
190+
/// </summary>
191+
/// <typeparam name="TSub"></typeparam>
192+
/// <param name="fieldSelectors"></param>
193+
/// <returns></returns>
194+
public FragmentBuilder<T> Inline<TSub>(params Expression<Func<TSub, Recursion>>[] fieldSelectors) where TSub : T
195+
{
196+
fieldSelectors.ValidateNotNullOrEmptyArgument("fieldSelectors");
197+
var paser = new FragmentExpressionParser();
198+
Recursive<TSub>(fieldSelectors.Select(selector => paser.GetReturnType(selector)).ToArray());
88199
return this;
89200
}
201+
public override GraphQLRequest GetQuery()
202+
{
203+
_query.Query = $"fragment {_query.OperationName} on {typeof(T).Name} {graphObject}";
204+
return _query;
205+
}
90206
}
91207
}

APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public TypeQueryBuilder<T> Fields(params Expression<Func<T, object>>[] fieldSele
103103
}
104104
return this;
105105
}
106+
[Obsolete("Obsoleted. Use InlineFragment instead")]
106107
/// <summary>
107108
/// Select fields in a subtype. The response of your query may need to convert data type. If then consider to use GetContent<TOriginal,TExpectedType> method.
108109
/// </summary>
@@ -118,6 +119,7 @@ public TypeQueryBuilder<T> AsType<TSub>(string propertyName) where TSub : T
118119
);
119120
return this;
120121
}
122+
[Obsolete("Obsoleted. Use InlineFragment instead")]
121123
/// <summary>
122124
/// Select fields in a subtype. The response of your query may need to convert data type. If then consider to use GetContent<TOriginal,TExpectedType> method.
123125
/// </summary>
@@ -144,6 +146,7 @@ public TypeQueryBuilder<T> AsType<TSub>(params Expression<Func<TSub, object>>[]
144146
);
145147
return this;
146148
}
149+
[Obsolete("Obsoleted. Use InlineFragment instead")]
147150
/// <summary>
148151
/// Select fields in a subtype. The response of your query may need to convert data type. If then consider to use GetContent<TOriginal,TExpectedType> method.
149152
/// </summary>
@@ -161,7 +164,95 @@ public TypeQueryBuilder<T> AsType<TSub>(SubTypeQueryBuilder<TSub> subTypeQuery)
161164
);
162165
return this;
163166
}
167+
public TypeQueryBuilder<T> InlineFragment<TSub>(params Expression<Func<TSub, string>>[] fieldSelectors) where TSub : T
168+
{
169+
fieldSelectors.ValidateNotNullArgument("fieldSelectors");
170+
string propertyName = string.Empty;
171+
foreach (var fieldSelector in fieldSelectors)
172+
{
173+
if (!propertyName.IsNullOrEmpty())
174+
{
175+
propertyName += " ";
176+
}
177+
propertyName += ConvertNestedFieldToString.ConvertNestedFieldForQuery(fieldSelector.GetFieldPath());
178+
179+
}
180+
string subTypeName = typeof(TSub).Name;
181+
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
182+
$"... on {subTypeName}{{{propertyName}}}" :
183+
$" ... on {subTypeName}{{{propertyName}}}"
184+
);
185+
return this;
186+
}
187+
public TypeQueryBuilder<T> InlineFragment<TSub>(params Expression<Func<TSub, int>>[] fieldSelectors) where TSub : T
188+
{
189+
fieldSelectors.ValidateNotNullArgument("fieldSelectors");
190+
string propertyName = string.Empty;
191+
foreach (var fieldSelector in fieldSelectors)
192+
{
193+
if (!propertyName.IsNullOrEmpty())
194+
{
195+
propertyName += " ";
196+
}
197+
propertyName += ConvertNestedFieldToString.ConvertNestedFieldForQuery(fieldSelector.GetFieldPath());
198+
199+
}
200+
string subTypeName = typeof(TSub).Name;
201+
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
202+
$"... on {subTypeName}{{{propertyName}}}" :
203+
$" ... on {subTypeName}{{{propertyName}}}"
204+
);
205+
return this;
206+
}
207+
public TypeQueryBuilder<T> InlineFragment<TSub>(params Expression<Func<TSub, double>>[] fieldSelectors) where TSub : T
208+
{
209+
fieldSelectors.ValidateNotNullArgument("fieldSelectors");
210+
string propertyName = string.Empty;
211+
foreach (var fieldSelector in fieldSelectors)
212+
{
213+
if (!propertyName.IsNullOrEmpty())
214+
{
215+
propertyName += " ";
216+
}
217+
propertyName += ConvertNestedFieldToString.ConvertNestedFieldForQuery(fieldSelector.GetFieldPath());
218+
219+
}
220+
string subTypeName = typeof(TSub).Name;
221+
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
222+
$"... on {subTypeName}{{{propertyName}}}" :
223+
$" ... on {subTypeName}{{{propertyName}}}"
224+
);
225+
return this;
226+
}
227+
public TypeQueryBuilder<T> InlineFragment<TSub>(params Expression<Func<TSub, IEnumerable<string>>>[] fieldSelectors) where TSub : T
228+
{
229+
fieldSelectors.ValidateNotNullArgument("fieldSelectors");
230+
string propertyName = string.Empty;
231+
foreach (var fieldSelector in fieldSelectors)
232+
{
233+
if (!propertyName.IsNullOrEmpty())
234+
{
235+
propertyName += " ";
236+
}
237+
propertyName += ConvertNestedFieldToString.ConvertNestedFieldForQuery(fieldSelector.GetFieldPath());
238+
239+
}
240+
string subTypeName = typeof(TSub).Name;
241+
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
242+
$"... on {subTypeName}{{{propertyName}}}" :
243+
$" ... on {subTypeName}{{{propertyName}}}"
244+
);
245+
return this;
246+
}
247+
public TypeQueryBuilder<T> InlineFragment<TSub>(params Expression<Func<TSub, Recursion>>[] fieldSelectors) where TSub : T
248+
{
249+
fieldSelectors.ValidateNotNullArgument("fieldSelectors");
250+
//foreach (var fieldSelector in fieldSelectors)
251+
//{
164252

253+
//}
254+
return this;
255+
}
165256
public TypeQueryBuilder<T> Autocomplete(Expression<Func<T, object>> fieldSelector, AutoCompleteOperators autocomplete)
166257
{
167258
fieldSelector.ValidateNotNullArgument("fieldSelector");

0 commit comments

Comments
 (0)