Skip to content

Commit fc05f59

Browse files
FIND-12462: Support link type for Link query
- New class LinkQueryBuilder use for build a link query - Add unit test
1 parent 24eb9ca commit fc05f59

File tree

7 files changed

+260
-32
lines changed

7 files changed

+260
-32
lines changed

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

+64-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using EPiServer.ContentGraph.Helpers;
2+
using EPiServer.ContentGraph.Helpers.Text;
23
using GraphQL.Transport;
34
using System;
45

@@ -64,22 +65,79 @@ public virtual BaseTypeQueryBuilder Field(string propertyName)
6465

6566
return this;
6667
}
67-
public virtual BaseTypeQueryBuilder Link(BaseTypeQueryBuilder link)
68+
public virtual BaseTypeQueryBuilder Link(ITypeQueryBuilder link)
6869
{
6970
link.ValidateNotNullArgument("link");
70-
string linkItems = link.GetQuery()?.Query ?? string.Empty;
71-
if (!linkItems.IsNullOrEmpty())
71+
var linkQueryBuilder = link as ILinkQueryBuilder;
72+
if (linkQueryBuilder is null)
7273
{
74+
throw new ArgumentException("The argument [link] is not type of [LinkQueryBuilder]");
75+
}
76+
string linkQuery = linkQueryBuilder.GetQuery()?.Query ?? string.Empty;
77+
if (!linkQuery.IsNullOrEmpty())
78+
{
79+
if (!linkQueryBuilder.GetLinkType().IsNullOrEmpty())
80+
{
81+
graphObject.SelectItems.Append(
82+
graphObject.SelectItems.Length == 0 ?
83+
$"_link(type:{linkQueryBuilder.GetLinkType()})" :
84+
$" _link(type:{linkQueryBuilder.GetLinkType()})"
85+
);
86+
}
7387
graphObject.SelectItems.Append(
7488
graphObject.SelectItems.Length == 0 ?
75-
$"_link{{{linkItems}}}" :
76-
$" _link{{{linkItems}}}"
89+
$"{{{linkQuery}}}" :
90+
$" {{{linkQuery}}}"
7791
);
7892
}
7993
return this;
8094
}
95+
public virtual BaseTypeQueryBuilder Link(ITypeQueryBuilder link, string alias)
96+
{
97+
link.ValidateNotNullArgument("link");
98+
var linkQueryBuilder = link as ILinkQueryBuilder;
99+
if (linkQueryBuilder is null)
100+
{
101+
throw new ArgumentException("The argument [link] is not type of [LinkQueryBuilder]");
102+
}
103+
if (!alias.IsValidName(50))
104+
{
105+
throw new ArgumentException($"Alias name {alias} is not valid");
106+
}
107+
string linkQuery = linkQueryBuilder.GetQuery()?.Query ?? string.Empty;
108+
if (!linkQuery.IsNullOrEmpty())
109+
{
110+
if (!linkQueryBuilder.GetLinkType().IsNullOrEmpty())
111+
{
112+
if (string.IsNullOrEmpty(alias))
113+
{
114+
graphObject.SelectItems.Append(
115+
graphObject.SelectItems.Length == 0 ?
116+
$"_link(type:{linkQueryBuilder.GetLinkType()})" :
117+
$" _link(type:{linkQueryBuilder.GetLinkType()})"
118+
);
119+
}
120+
else
121+
{
122+
graphObject.SelectItems.Append(
123+
graphObject.SelectItems.Length == 0 ?
124+
$"{alias}:_link(type:{linkQueryBuilder.GetLinkType()})" :
125+
$" {alias}:_link(type:{linkQueryBuilder.GetLinkType()})"
126+
);
127+
}
128+
129+
}
130+
graphObject.SelectItems.Append(
131+
graphObject.SelectItems.Length == 0 ?
132+
$"{{{linkQuery}}}" :
133+
$" {{{linkQuery}}}"
134+
);
135+
136+
}
137+
return this;
138+
}
81139
[Obsolete("Use Link method instead")]
82-
public virtual BaseTypeQueryBuilder Children(BaseTypeQueryBuilder children)
140+
public virtual BaseTypeQueryBuilder Children(ITypeQueryBuilder children)
83141
{
84142
children.ValidateNotNullArgument("children");
85143
string childrenItems = children.GetQuery()?.Query ?? string.Empty;

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using System.Linq;
1616
using System.IO;
1717
using System.Collections.Generic;
18+
using EPiServer.ContentGraph.Helpers.Text;
1819

1920
namespace EPiServer.ContentGraph.Api.Querying
2021
{
@@ -113,8 +114,7 @@ public IEnumerable<FragmentBuilder> GetFragments()
113114
/// <returns></returns>
114115
public GraphQueryBuilder OperationName(string op)
115116
{
116-
Regex reg = new Regex(@"^[a-zA-Z_]\w*$");
117-
if (reg.IsMatch(op))
117+
if (op.IsValidName())
118118
{
119119
_query.OperationName = op;
120120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace EPiServer.ContentGraph.Api.Querying
8+
{
9+
public interface ILinkQueryBuilder : ITypeQueryBuilder
10+
{
11+
string GetLinkType();
12+
}
13+
14+
public class LinkQueryBuilder<T> : TypeQueryBuilder<T>, ILinkQueryBuilder
15+
{
16+
private string _type;
17+
public LinkQueryBuilder() : base()
18+
{
19+
}
20+
public LinkQueryBuilder(string linkType) : base()
21+
{
22+
_type = linkType;
23+
}
24+
public LinkQueryBuilder<T> WithLinkType(string linkType)
25+
{
26+
if (_type == null)
27+
{
28+
_type = linkType;
29+
}
30+
return this;
31+
}
32+
public string GetLinkType()
33+
{
34+
return _type;
35+
}
36+
}
37+
}

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

+12
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ public TypeQueryBuilder<T> Link<TLink>(TypeQueryBuilder<TLink> link)
3333
base.Link(link);
3434
return this;
3535
}
36+
/// <summary>
37+
/// Link with simple alias
38+
/// </summary>
39+
/// <param name="link"></param>
40+
/// <param name="alias">Length should lte 50, can not start with numbers</param>
41+
/// <returns></returns>
42+
/// <exception cref="ArgumentException"></exception>
43+
public TypeQueryBuilder<T> Link<TLink>(TypeQueryBuilder<TLink> link, string alias)
44+
{
45+
base.Link(link, alias);
46+
return this;
47+
}
3648
[Obsolete("Use Link method instead")]
3749
public TypeQueryBuilder<T> Children<TChildren>(TypeQueryBuilder<TChildren> children)
3850
{

APIs/src/EpiServer.ContentGraph/Helpers/Text/StringExtensions.cs

+5
Original file line numberDiff line numberDiff line change
@@ -264,5 +264,10 @@ public static bool IsWildcardPrefix(this string value)
264264

265265
return false;
266266
}
267+
public static bool IsValidName(this string name, int length=25)
268+
{
269+
Regex reg = new Regex(@"^[a-zA-Z_]\w*$");
270+
return reg.IsMatch(name) && name.Length <= length;
271+
}
267272
}
268273
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using EpiServer.ContentGraph.UnitTests.QueryTypeObjects;
2+
using EPiServer.ContentGraph.Api.Filters;
3+
using EPiServer.ContentGraph.Api.Querying;
4+
using Xunit;
5+
6+
namespace EpiServer.ContentGraph.UnitTests
7+
{
8+
public class GenerateLinkQueryTests
9+
{
10+
private TypeQueryBuilder<RequestTypeObject> typeQueryBuilder;
11+
public GenerateLinkQueryTests()
12+
{
13+
typeQueryBuilder = new TypeQueryBuilder<RequestTypeObject>();
14+
}
15+
16+
[Fact]
17+
public void LinkQueryTests()
18+
{
19+
string childQuery = "SubTypeObject(where:{SubProperty:{match: \"test\"}}){items{SubProperty} facets{Property3{name count}}}";
20+
string expectedFields = $"items{{Property1 Property2 _link(type:CUSTOMERREFERENCES) {{{childQuery}}}}}";
21+
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
22+
var linkQuery = new LinkQueryBuilder<SubTypeObject>("CUSTOMERREFERENCES")
23+
.Field(x => x.SubProperty)
24+
.Where(x => x.SubProperty, new StringFilterOperators().Match("test"))
25+
.Facet(x => x.Property3);
26+
27+
typeQueryBuilder
28+
.Field(x => x.Property1)
29+
.Field(x => x.Property2)
30+
.Link(linkQuery)
31+
.Facet(x => x.Property3.NestedProperty);
32+
GraphQueryBuilder query = typeQueryBuilder.ToQuery();
33+
34+
Assert.NotNull(query.GetQuery());
35+
Assert.Contains(expectedFacets, query.GetQuery().Query);
36+
Assert.Contains(expectedFields, query.GetQuery().Query);
37+
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
38+
}
39+
40+
[Fact]
41+
public void multiple_links_query_should_generate_correct_query()
42+
{
43+
string expectedLink1 = "SubTypeObject(where:{SubProperty:{match: \"test1\"}}){items{SubProperty} facets{Property3{name count}}}";
44+
string expectedLink2 = "SubTypeObject(where:{Property1:{match: \"test2\"}}){items{Property1} facets{Property3{name count}}}";
45+
string expectedFields = $"items{{Property1 Property2 _link(type:CUSTOMERREFERENCES) {{{expectedLink1}}} _link(type:DEFAULT) {{{expectedLink2}}}}}";
46+
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
47+
var linkQuery1 = new LinkQueryBuilder<SubTypeObject>("CUSTOMERREFERENCES")
48+
.Field(x => x.SubProperty)
49+
.Where(x => x.SubProperty, new StringFilterOperators().Match("test1"))
50+
.Facet(x => x.Property3);
51+
52+
var linkQuery2 = new LinkQueryBuilder<SubTypeObject>("DEFAULT")
53+
.Field(x => x.Property1)
54+
.Where(x => x.Property1, new StringFilterOperators().Match("test2"))
55+
.Facet(x => x.Property3);
56+
57+
typeQueryBuilder
58+
.Field(x => x.Property1)
59+
.Field(x => x.Property2)
60+
.Link(linkQuery1)
61+
.Link(linkQuery2)
62+
.Facet(x => x.Property3.NestedProperty);
63+
GraphQueryBuilder query = typeQueryBuilder.ToQuery();
64+
65+
Assert.Equal(linkQuery1.GetQuery().Query, expectedLink1);
66+
Assert.Equal(linkQuery2.GetQuery().Query, expectedLink2);
67+
68+
Assert.Contains(expectedFacets, query.GetQuery().Query);
69+
Assert.Contains(expectedFields, query.GetQuery().Query);
70+
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
71+
}
72+
73+
[Fact]
74+
public void nested_links_query_should_generate_nested_link_query()
75+
{
76+
string expectedLink1 = "SubTypeObject(where:{SubProperty:{match: \"test1\"}}){items{SubProperty} facets{Property3{name count}}}";
77+
string expectedLink2 = "SubTypeObject(where:{Property1:{match: \"test2\"}}){items{Property1 _link(type:CUSTOMERREFERENCES) {" + expectedLink1 + "}} facets{Property3{name count}}}";
78+
string expectedFields = $"items{{Property1 Property2 _link(type:DEFAULT) {{{expectedLink2}}}}}";
79+
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
80+
var linkQuery1 = new LinkQueryBuilder<SubTypeObject>()
81+
.WithLinkType("CUSTOMERREFERENCES")
82+
.Field(x => x.SubProperty)
83+
.Where(x => x.SubProperty, new StringFilterOperators().Match("test1"))
84+
.Facet(x => x.Property3);
85+
86+
var linkQuery2 = new LinkQueryBuilder<SubTypeObject>("DEFAULT")
87+
.Field(x => x.Property1)
88+
.Where(x => x.Property1, new StringFilterOperators().Match("test2"))
89+
.Facet(x => x.Property3)
90+
.Link(linkQuery1);
91+
92+
typeQueryBuilder
93+
.Field(x => x.Property1)
94+
.Field(x => x.Property2)
95+
.Link(linkQuery2)
96+
.Facet(x => x.Property3.NestedProperty);
97+
GraphQueryBuilder query = typeQueryBuilder.ToQuery();
98+
99+
Assert.Equal(linkQuery1.GetQuery().Query, expectedLink1);
100+
Assert.Equal(linkQuery2.GetQuery().Query, expectedLink2);
101+
102+
Assert.Contains(expectedFacets, query.GetQuery().Query);
103+
Assert.Contains(expectedFields, query.GetQuery().Query);
104+
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
105+
}
106+
[Fact]
107+
public void nested_link_query_with_aliases()
108+
{
109+
string expectedLink1 = "SubTypeObject(where:{SubProperty:{match: \"test1\"}}){items{SubProperty} facets{Property3{name count}}}";
110+
string expectedLink2 = "SubTypeObject(where:{Property1:{match: \"test2\"}}){items{Property1 mylink1:_link(type:CUSTOMERREFERENCES) {" + expectedLink1 + "}} facets{Property3{name count}}}";
111+
string expectedFields = $"items{{Property1 Property2 mylink2:_link(type:DEFAULT) {{{expectedLink2}}}}}";
112+
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
113+
var linkQuery1 = new LinkQueryBuilder<SubTypeObject>()
114+
.WithLinkType("CUSTOMERREFERENCES")
115+
.Field(x => x.SubProperty)
116+
.Where(x => x.SubProperty, new StringFilterOperators().Match("test1"))
117+
.Facet(x => x.Property3);
118+
119+
var linkQuery2 = new LinkQueryBuilder<SubTypeObject>("DEFAULT")
120+
.Field(x => x.Property1)
121+
.Where(x => x.Property1, new StringFilterOperators().Match("test2"))
122+
.Facet(x => x.Property3)
123+
.Link(linkQuery1, "mylink1");
124+
125+
typeQueryBuilder
126+
.Field(x => x.Property1)
127+
.Field(x => x.Property2)
128+
.Link(linkQuery2, "mylink2")
129+
.Facet(x => x.Property3.NestedProperty);
130+
GraphQueryBuilder query = typeQueryBuilder.ToQuery();
131+
132+
Assert.Equal(linkQuery1.GetQuery().Query, expectedLink1);
133+
Assert.Equal(linkQuery2.GetQuery().Query, expectedLink2);
134+
135+
Assert.Contains(expectedFacets, query.GetQuery().Query);
136+
Assert.Contains(expectedFields, query.GetQuery().Query);
137+
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
138+
}
139+
}
140+
}

APIs/src/Testing/EpiServer.ContentGraph.UnitTests/GenerateQueryTests.cs

-24
Original file line numberDiff line numberDiff line change
@@ -170,30 +170,6 @@ public void SubtypeQueryTests()
170170
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
171171
}
172172

173-
[Fact]
174-
public void LinkQueryTests()
175-
{
176-
string childQuery = "SubTypeObject(where:{SubProperty:{match: \"test\"}}){items{SubProperty} facets{Property3{name count}}}";
177-
string expectedFields = $"items{{Property1 Property2 _link{{{childQuery}}}}}";
178-
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
179-
TypeQueryBuilder<SubTypeObject> linkQuery = new TypeQueryBuilder<SubTypeObject>()
180-
.Field(x => x.SubProperty)
181-
.Where(x => x.SubProperty, new StringFilterOperators().Match("test"))
182-
.Facet(x => x.Property3);
183-
184-
typeQueryBuilder
185-
.Field(x => x.Property1)
186-
.Field(x => x.Property2)
187-
.Link(linkQuery)
188-
.Facet(x => x.Property3.NestedProperty);
189-
GraphQueryBuilder query = typeQueryBuilder.ToQuery();
190-
191-
Assert.NotNull(query.GetQuery());
192-
Assert.Contains(expectedFacets, query.GetQuery().Query);
193-
Assert.Contains(expectedFields, query.GetQuery().Query);
194-
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
195-
}
196-
197173
[Fact]
198174
public void Multiple_types_query()
199175
{

0 commit comments

Comments
 (0)