Skip to content

Commit

Permalink
FIND-12462: Support link type for Link query
Browse files Browse the repository at this point in the history
- New class LinkQueryBuilder use for build a link query
- Add unit test
  • Loading branch information
ManhOptimizely committed Apr 2, 2024
1 parent 18a3fa7 commit 7100f9d
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using EPiServer.ContentGraph.Helpers;
using EPiServer.ContentGraph.Helpers.Text;
using GraphQL.Transport;
using System;

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

return this;
}
public virtual BaseTypeQueryBuilder Link(BaseTypeQueryBuilder link)
public virtual BaseTypeQueryBuilder Link(ITypeQueryBuilder link)
{
link.ValidateNotNullArgument("link");
string linkItems = link.GetQuery()?.Query ?? string.Empty;
if (!linkItems.IsNullOrEmpty())
var linkQueryBuilder = link as ILinkQueryBuilder;
if (linkQueryBuilder is null)
{
throw new ArgumentException("The argument [link] is not type of [LinkQueryBuilder]");
}
string linkQuery = linkQueryBuilder.GetQuery()?.Query ?? string.Empty;
if (!linkQuery.IsNullOrEmpty())
{
if (!linkQueryBuilder.GetLinkType().IsNullOrEmpty())
{
graphObject.SelectItems.Append(
graphObject.SelectItems.Length == 0 ?
$"_link(type:{linkQueryBuilder.GetLinkType()})" :
$" _link(type:{linkQueryBuilder.GetLinkType()})"
);
}
graphObject.SelectItems.Append(
graphObject.SelectItems.Length == 0 ?
$"_link{{{linkItems}}}" :
$" _link{{{linkItems}}}"
$"{{{linkQuery}}}" :
$" {{{linkQuery}}}"
);
}
return this;
}
public virtual BaseTypeQueryBuilder Link(ITypeQueryBuilder link, string alias)
{
link.ValidateNotNullArgument("link");
var linkQueryBuilder = link as ILinkQueryBuilder;
if (linkQueryBuilder is null)
{
throw new ArgumentException("The argument [link] is not type of [LinkQueryBuilder]");
}
if (!alias.IsValidName(50))
{
throw new ArgumentException($"Alias name {alias} is not valid");
}
string linkQuery = linkQueryBuilder.GetQuery()?.Query ?? string.Empty;
if (!linkQuery.IsNullOrEmpty())
{
if (!linkQueryBuilder.GetLinkType().IsNullOrEmpty())
{
if (string.IsNullOrEmpty(alias))
{
graphObject.SelectItems.Append(
graphObject.SelectItems.Length == 0 ?
$"_link(type:{linkQueryBuilder.GetLinkType()})" :
$" _link(type:{linkQueryBuilder.GetLinkType()})"
);
}
else
{
graphObject.SelectItems.Append(
graphObject.SelectItems.Length == 0 ?
$"{alias}:_link(type:{linkQueryBuilder.GetLinkType()})" :
$" {alias}:_link(type:{linkQueryBuilder.GetLinkType()})"
);
}

}
graphObject.SelectItems.Append(
graphObject.SelectItems.Length == 0 ?
$"{{{linkQuery}}}" :
$" {{{linkQuery}}}"
);

}
return this;
}
[Obsolete("Use Link method instead")]
public virtual BaseTypeQueryBuilder Children(BaseTypeQueryBuilder children)
public virtual BaseTypeQueryBuilder Children(ITypeQueryBuilder children)
{
children.ValidateNotNullArgument("children");
string childrenItems = children.GetQuery()?.Query ?? string.Empty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Linq;
using System.IO;
using System.Collections.Generic;
using EPiServer.ContentGraph.Helpers.Text;

namespace EPiServer.ContentGraph.Api.Querying
{
Expand Down Expand Up @@ -113,8 +114,7 @@ public IEnumerable<FragmentBuilder> GetFragments()
/// <returns></returns>
public GraphQueryBuilder OperationName(string op)
{
Regex reg = new Regex(@"^[a-zA-Z_]\w*$");
if (reg.IsMatch(op))
if (op.IsValidName())
{
_query.OperationName = op;
}
Expand Down
37 changes: 37 additions & 0 deletions APIs/src/EpiServer.ContentGraph/Api/Querying/LinkQueryBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EPiServer.ContentGraph.Api.Querying
{
public interface ILinkQueryBuilder : ITypeQueryBuilder
{
string GetLinkType();
}

public class LinkQueryBuilder<T> : TypeQueryBuilder<T>, ILinkQueryBuilder
{
private string _type;
public LinkQueryBuilder() : base()
{
}
public LinkQueryBuilder(string linkType) : base()
{
_type = linkType;
}
public LinkQueryBuilder<T> WithLinkType(string linkType)
{
if (_type == null)
{
_type = linkType;
}
return this;
}
public string GetLinkType()
{
return _type;
}
}
}
12 changes: 12 additions & 0 deletions APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ public TypeQueryBuilder<T> Link<TLink>(TypeQueryBuilder<TLink> link)
base.Link(link);
return this;
}
/// <summary>
/// Link with simple alias
/// </summary>
/// <param name="link"></param>
/// <param name="alias">Length should lte 50, can not start with numbers</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public TypeQueryBuilder<T> Link<TLink>(TypeQueryBuilder<TLink> link, string alias)
{
base.Link(link, alias);
return this;
}
[Obsolete("Use Link method instead")]
public TypeQueryBuilder<T> Children<TChildren>(TypeQueryBuilder<TChildren> children)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,5 +264,10 @@ public static bool IsWildcardPrefix(this string value)

return false;
}
public static bool IsValidName(this string name, int length=25)
{
Regex reg = new Regex(@"^[a-zA-Z_]\w*$");
return reg.IsMatch(name) && name.Length <= length;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using EpiServer.ContentGraph.UnitTests.QueryTypeObjects;
using EPiServer.ContentGraph.Api.Filters;
using EPiServer.ContentGraph.Api.Querying;
using Xunit;

namespace EpiServer.ContentGraph.UnitTests
{
public class GenerateLinkQueryTests
{
private TypeQueryBuilder<RequestTypeObject> typeQueryBuilder;
public GenerateLinkQueryTests()
{
typeQueryBuilder = new TypeQueryBuilder<RequestTypeObject>();
}

[Fact]
public void LinkQueryTests()
{
string childQuery = "SubTypeObject(where:{SubProperty:{match: \"test\"}}){items{SubProperty} facets{Property3{name count}}}";
string expectedFields = $"items{{Property1 Property2 _link(type:CUSTOMERREFERENCES) {{{childQuery}}}}}";
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
var linkQuery = new LinkQueryBuilder<SubTypeObject>("CUSTOMERREFERENCES")
.Field(x => x.SubProperty)
.Where(x => x.SubProperty, new StringFilterOperators().Match("test"))
.Facet(x => x.Property3);

typeQueryBuilder
.Field(x => x.Property1)
.Field(x => x.Property2)
.Link(linkQuery)
.Facet(x => x.Property3.NestedProperty);
GraphQueryBuilder query = typeQueryBuilder.ToQuery();

Assert.NotNull(query.GetQuery());
Assert.Contains(expectedFacets, query.GetQuery().Query);
Assert.Contains(expectedFields, query.GetQuery().Query);
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
}

[Fact]
public void multiple_links_query_should_generate_correct_query()
{
string expectedLink1 = "SubTypeObject(where:{SubProperty:{match: \"test1\"}}){items{SubProperty} facets{Property3{name count}}}";
string expectedLink2 = "SubTypeObject(where:{Property1:{match: \"test2\"}}){items{Property1} facets{Property3{name count}}}";
string expectedFields = $"items{{Property1 Property2 _link(type:CUSTOMERREFERENCES) {{{expectedLink1}}} _link(type:DEFAULT) {{{expectedLink2}}}}}";
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
var linkQuery1 = new LinkQueryBuilder<SubTypeObject>("CUSTOMERREFERENCES")
.Field(x => x.SubProperty)
.Where(x => x.SubProperty, new StringFilterOperators().Match("test1"))
.Facet(x => x.Property3);

var linkQuery2 = new LinkQueryBuilder<SubTypeObject>("DEFAULT")
.Field(x => x.Property1)
.Where(x => x.Property1, new StringFilterOperators().Match("test2"))
.Facet(x => x.Property3);

typeQueryBuilder
.Field(x => x.Property1)
.Field(x => x.Property2)
.Link(linkQuery1)
.Link(linkQuery2)
.Facet(x => x.Property3.NestedProperty);
GraphQueryBuilder query = typeQueryBuilder.ToQuery();

Assert.Equal(linkQuery1.GetQuery().Query, expectedLink1);
Assert.Equal(linkQuery2.GetQuery().Query, expectedLink2);

Assert.Contains(expectedFacets, query.GetQuery().Query);
Assert.Contains(expectedFields, query.GetQuery().Query);
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
}

[Fact]
public void nested_links_query_should_generate_nested_link_query()
{
string expectedLink1 = "SubTypeObject(where:{SubProperty:{match: \"test1\"}}){items{SubProperty} facets{Property3{name count}}}";
string expectedLink2 = "SubTypeObject(where:{Property1:{match: \"test2\"}}){items{Property1 _link(type:CUSTOMERREFERENCES) {" + expectedLink1 + "}} facets{Property3{name count}}}";
string expectedFields = $"items{{Property1 Property2 _link(type:DEFAULT) {{{expectedLink2}}}}}";
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
var linkQuery1 = new LinkQueryBuilder<SubTypeObject>()
.WithLinkType("CUSTOMERREFERENCES")
.Field(x => x.SubProperty)
.Where(x => x.SubProperty, new StringFilterOperators().Match("test1"))
.Facet(x => x.Property3);

var linkQuery2 = new LinkQueryBuilder<SubTypeObject>("DEFAULT")
.Field(x => x.Property1)
.Where(x => x.Property1, new StringFilterOperators().Match("test2"))
.Facet(x => x.Property3)
.Link(linkQuery1);

typeQueryBuilder
.Field(x => x.Property1)
.Field(x => x.Property2)
.Link(linkQuery2)
.Facet(x => x.Property3.NestedProperty);
GraphQueryBuilder query = typeQueryBuilder.ToQuery();

Assert.Equal(linkQuery1.GetQuery().Query, expectedLink1);
Assert.Equal(linkQuery2.GetQuery().Query, expectedLink2);

Assert.Contains(expectedFacets, query.GetQuery().Query);
Assert.Contains(expectedFields, query.GetQuery().Query);
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
}
[Fact]
public void nested_link_query_with_aliases()
{
string expectedLink1 = "SubTypeObject(where:{SubProperty:{match: \"test1\"}}){items{SubProperty} facets{Property3{name count}}}";
string expectedLink2 = "SubTypeObject(where:{Property1:{match: \"test2\"}}){items{Property1 mylink1:_link(type:CUSTOMERREFERENCES) {" + expectedLink1 + "}} facets{Property3{name count}}}";
string expectedFields = $"items{{Property1 Property2 mylink2:_link(type:DEFAULT) {{{expectedLink2}}}}}";
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
var linkQuery1 = new LinkQueryBuilder<SubTypeObject>()
.WithLinkType("CUSTOMERREFERENCES")
.Field(x => x.SubProperty)
.Where(x => x.SubProperty, new StringFilterOperators().Match("test1"))
.Facet(x => x.Property3);

var linkQuery2 = new LinkQueryBuilder<SubTypeObject>("DEFAULT")
.Field(x => x.Property1)
.Where(x => x.Property1, new StringFilterOperators().Match("test2"))
.Facet(x => x.Property3)
.Link(linkQuery1, "mylink1");

typeQueryBuilder
.Field(x => x.Property1)
.Field(x => x.Property2)
.Link(linkQuery2, "mylink2")
.Facet(x => x.Property3.NestedProperty);
GraphQueryBuilder query = typeQueryBuilder.ToQuery();

Assert.Equal(linkQuery1.GetQuery().Query, expectedLink1);
Assert.Equal(linkQuery2.GetQuery().Query, expectedLink2);

Assert.Contains(expectedFacets, query.GetQuery().Query);
Assert.Contains(expectedFields, query.GetQuery().Query);
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,30 +170,6 @@ public void SubtypeQueryTests()
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
}

[Fact]
public void LinkQueryTests()
{
string childQuery = "SubTypeObject(where:{SubProperty:{match: \"test\"}}){items{SubProperty} facets{Property3{name count}}}";
string expectedFields = $"items{{Property1 Property2 _link{{{childQuery}}}}}";
string expectedFacets = @"facets{Property3{NestedProperty{name count}}}";
TypeQueryBuilder<SubTypeObject> linkQuery = new TypeQueryBuilder<SubTypeObject>()
.Field(x => x.SubProperty)
.Where(x => x.SubProperty, new StringFilterOperators().Match("test"))
.Facet(x => x.Property3);

typeQueryBuilder
.Field(x => x.Property1)
.Field(x => x.Property2)
.Link(linkQuery)
.Facet(x => x.Property3.NestedProperty);
GraphQueryBuilder query = typeQueryBuilder.ToQuery();

Assert.NotNull(query.GetQuery());
Assert.Contains(expectedFacets, query.GetQuery().Query);
Assert.Contains(expectedFields, query.GetQuery().Query);
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
}

[Fact]
public void Multiple_types_query()
{
Expand Down

0 comments on commit 7100f9d

Please sign in to comment.