Skip to content

Commit

Permalink
Merge branch 'main' into feature/add-facet-selection
Browse files Browse the repository at this point in the history
# Conflicts:
#	APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs
  • Loading branch information
Joshua Folkerts committed Mar 26, 2024
2 parents 16d9a62 + 65b219e commit 32b0462
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public abstract class BaseTypeQueryBuilder : ITypeQueryBuilder
protected IQuery _parent = null;
public virtual IQuery Parent
{
get => _parent;
get => _parent;
set
{
if (_parent.IsNull())
Expand Down Expand Up @@ -55,14 +55,11 @@ public virtual BaseTypeQueryBuilder Field(string propertyName)
if (!propertyName.IsNullOrEmpty())
{
string clonedPropName = ConvertNestedFieldToString.ConvertNestedFieldForQuery(propertyName);
if (graphObject.SelectItems.IsNullOrEmpty())
{
graphObject.SelectItems = $"{clonedPropName}";
}
else
{
graphObject.SelectItems += $" {clonedPropName}";
}
graphObject.SelectItems.Append(
graphObject.SelectItems.Length == 0 ?
$"{clonedPropName}" :
$" {clonedPropName}"
);
}

return this;
Expand All @@ -73,9 +70,11 @@ public virtual BaseTypeQueryBuilder Link(BaseTypeQueryBuilder link)
string linkItems = link.GetQuery()?.Query ?? string.Empty;
if (!linkItems.IsNullOrEmpty())
{
graphObject.SelectItems += graphObject.SelectItems.IsNullOrEmpty() ?
graphObject.SelectItems.Append(
graphObject.SelectItems.Length == 0 ?
$"_link{{{linkItems}}}" :
$" _link{{{linkItems}}}";
$" _link{{{linkItems}}}"
);
}
return this;
}
Expand All @@ -86,9 +85,11 @@ public virtual BaseTypeQueryBuilder Children(BaseTypeQueryBuilder children)
string childrenItems = children.GetQuery()?.Query ?? string.Empty;
if (!childrenItems.IsNullOrEmpty())
{
graphObject.SelectItems += graphObject.SelectItems.IsNullOrEmpty() ?
graphObject.SelectItems.Append(
graphObject.SelectItems.Length == 0 ?
$"_children{{{childrenItems}}}" :
$" _children{{{childrenItems}}}";
$" _children{{{childrenItems}}}"
);
}

return this;
Expand All @@ -105,9 +106,39 @@ public virtual BaseTypeQueryBuilder Fragments(params FragmentBuilder[] fragments
protected virtual BaseTypeQueryBuilder Fragment(FragmentBuilder fragment)
{
fragment.ValidateNotNullArgument("fragment");
graphObject.SelectItems += graphObject.SelectItems.IsNullOrEmpty() ? $"...{fragment.GetName()}" : $" ...{fragment.GetName()}";
Parent?.AddFragment(fragment);
graphObject.SelectItems.Append(
graphObject.SelectItems.Length == 0 ?
$"...{fragment.GetName()}" :
$" ...{fragment.GetName()}"
);

if (Parent != null)
{
Parent.AddFragment(fragment);
var children = GetAllChildren(fragment);
foreach (var childFragment in children)
{
Parent.AddFragment(childFragment);
}
}
return this;
}
private IEnumerable<FragmentBuilder> GetAllChildren(FragmentBuilder fragment)
{
if (fragment.HasChildren)
{
foreach (var child in fragment.ChildrenFragments)
{
yield return child;
if (child.HasChildren)
{
foreach (var item in GetAllChildren(child))
{
yield return item;
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class ContentGraphQuery
public string Autocomplete { get; set; } = string.Empty;
public string Cursor { get; set; } = string.Empty;
public string TypeName { get; set; } = string.Empty;
public string SelectItems { get; set; } = string.Empty;
public StringBuilder SelectItems { get; set; } = new StringBuilder();
public string Facets { get; set; } = string.Empty;
public string Total { get; set; } = string.Empty;
public override string ToString()
Expand All @@ -27,7 +27,7 @@ public override string ToString()
stringBuilder.Append(Filter);
}
stringBuilder.Append('{');
stringBuilder.Append(SelectItems);//mandatory property
stringBuilder.Append(SelectItems);
if (!Facets.IsNullOrEmpty())
{
stringBuilder.Append(' ');
Expand Down
17 changes: 17 additions & 0 deletions APIs/src/EpiServer.ContentGraph/Api/Querying/FragmentBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public FragmentBuilder<T> Link<TLink>(TypeQueryBuilder<TLink> link)
base.Link(link);
return this;
}
[Obsolete("Obsoleted. Use Link instead")]
public FragmentBuilder<T> Children<TChildren>(TypeQueryBuilder<TChildren> children)
{
base.Children(children);
Expand All @@ -48,6 +49,9 @@ public override GraphQLRequest GetQuery()

public class FragmentBuilder : BaseTypeQueryBuilder
{
private List<FragmentBuilder> _childrenFragments;
public IEnumerable<FragmentBuilder> ChildrenFragments => _childrenFragments;
public bool HasChildren => _childrenFragments != null && _childrenFragments.Any();
public FragmentBuilder() : base()
{
_query.OperationName = "sampleFragment";
Expand All @@ -64,5 +68,18 @@ public string GetName()
{
return _query.OperationName;
}
public override FragmentBuilder Fragments(params FragmentBuilder[] fragments)
{
if (fragments.IsNotNull() && fragments.Length > 0)
{
if (_childrenFragments.IsNull())
{
_childrenFragments = new List<FragmentBuilder>();
}
base.Fragments(fragments);
_childrenFragments.AddRange(fragments);
}
return this;
}
}
}
29 changes: 20 additions & 9 deletions APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ public TypeQueryBuilder<T> Field(Expression<Func<T, object>> fieldSelector)
return this;
}

public TypeQueryBuilder<T> Field(Expression<Func<T, object>> fieldSelector, string alias)
{
fieldSelector.ValidateNotNullArgument("fieldSelector");
var propertyName = fieldSelector.GetFieldPath();
Field($"{alias}:{propertyName}");
return this;
}

public TypeQueryBuilder<T> Fields(params Expression<Func<T, object>>[] fieldSelectors)
{
fieldSelectors.ValidateNotNullArgument("fieldSelectors");
Expand All @@ -89,9 +97,10 @@ public TypeQueryBuilder<T> Fields(params Expression<Func<T, object>>[] fieldSele
public TypeQueryBuilder<T> AsType<TSub>(string propertyName) where TSub : T
{
string subTypeName = typeof(TSub).Name;
graphObject.SelectItems = graphObject.SelectItems.IsNullOrEmpty() ?
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
$"... on {subTypeName}{{{ConvertNestedFieldToString.ConvertNestedFieldForQuery(propertyName)}}}" :
$"{graphObject.SelectItems} ... on {subTypeName}{{{ConvertNestedFieldToString.ConvertNestedFieldForQuery(propertyName)}}}";
$" ... on {subTypeName}{{{ConvertNestedFieldToString.ConvertNestedFieldForQuery(propertyName)}}}"
);
return this;
}

Expand All @@ -112,9 +121,10 @@ public TypeQueryBuilder<T> AsType<TSub>(params Expression<Func<TSub, object>>[]
propertyName += ConvertNestedFieldToString.ConvertNestedFieldForQuery(fieldSelector.GetFieldPath());
}
string subTypeName = typeof(TSub).Name;
graphObject.SelectItems = graphObject.SelectItems.IsNullOrEmpty() ?
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
$"... on {subTypeName}{{{propertyName}}}" :
$"{graphObject.SelectItems} ... on {subTypeName}{{{propertyName}}}";
$" ... on {subTypeName}{{{propertyName}}}"
);
return this;
}

Expand All @@ -127,9 +137,10 @@ public TypeQueryBuilder<T> AsType<TSub>(SubTypeQueryBuilder<TSub> subTypeQuery)
subTypeQuery.ValidateNotNullArgument("subTypeQuery");
subTypeQuery.Parent = this.Parent;
string subTypeName = typeof(TSub).Name;
graphObject.SelectItems = graphObject.SelectItems.IsNullOrEmpty() ?
graphObject.SelectItems.Append(graphObject.SelectItems.Length == 0 ?
$"... on {subTypeName}{subTypeQuery.GetQuery().Query}" :
$"{graphObject.SelectItems} ... on {subTypeName}{subTypeQuery.GetQuery().Query}";
$" ... on {subTypeName}{subTypeQuery.GetQuery().Query}"
);
return this;
}

Expand Down Expand Up @@ -675,7 +686,7 @@ public override GraphQueryBuilder ToQuery()
{
if (!_compiled)
{
if (graphObject.SelectItems.IsNullOrEmpty() && graphObject.Total.IsNullOrEmpty() && graphObject.Facets.IsNullOrEmpty() && graphObject.Autocomplete.IsNullOrEmpty())
if (graphObject.SelectItems?.Length == 0 && graphObject.Total.IsNullOrEmpty() && graphObject.Facets.IsNullOrEmpty() && graphObject.Autocomplete.IsNullOrEmpty())
{
throw new ArgumentNullException("You must select at least one of the values [Field(s), Facet(s), Total, Autocomplete(s)]");
}
Expand All @@ -692,9 +703,9 @@ public override GraphQueryBuilder ToQuery()
{
graphObject.Filter = graphObject.Filter.IsNullOrEmpty() ? graphObject.Ids : $"{graphObject.Filter},{graphObject.Ids}";
}
if (!graphObject.SelectItems.IsNullOrEmpty())
if (graphObject.SelectItems.Length > 0)
{
graphObject.SelectItems = $"items{{{graphObject.SelectItems}}}";
graphObject.SelectItems = new System.Text.StringBuilder($"items{{{graphObject.SelectItems}}}");
}

if (!graphObject.WhereClause.IsNullOrEmpty())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,69 @@ public void add_fragment_to_a_type_query_should_generate_correct_string()
Assert.Contains(expectedFragmment, graphQueryBuilder.GetQuery().Query);
Assert.Contains(expectedFields, graphQueryBuilder.GetQuery().Query);
}
[Fact]
public void multiple_fragments_should_generate_correct_query()
{
const string expectedMainQuery = "query FragmentTest {FragmentObject{items{Name ...FirstFragment ...SecondFragment}}}";
const string expectedFistFragment = "fragment FirstFragment on PromoObject {ProviderName}";
const string expectedSecondFragmment = "fragment SecondFragment on FragmentObject {ViewingTime PromoImage{Url}}";
const string expectedFullQuery = $"{expectedMainQuery}\n{expectedFistFragment}\n{expectedSecondFragmment}";

var firstFragment = new FragmentBuilder<PromoObject>("FirstFragment");
firstFragment.Fields(x => x.ProviderName);

var secondFragment = new FragmentBuilder<FragmentObject>("SecondFragment");
secondFragment.Fields(x => x.ViewingTime, x=> x.PromoImage.Url);

GraphQueryBuilder graphQueryBuilder = new GraphQueryBuilder();
graphQueryBuilder
.OperationName("FragmentTest")
.ForType<FragmentObject>()
.Field(x => x.Name)
.Fragments(firstFragment, secondFragment)
.ToQuery()
.BuildQueries();

Assert.Equal(graphQueryBuilder.GetFragments().First().GetName(), "FirstFragment");
Assert.Equal(graphQueryBuilder.GetFragments().First().GetQuery().Query, expectedFistFragment);

Assert.Equal(graphQueryBuilder.GetFragments().Last().GetName(), "SecondFragment");
Assert.Equal(graphQueryBuilder.GetFragments().Last().GetQuery().Query, expectedSecondFragmment);

Assert.Equal(expectedFullQuery, graphQueryBuilder.GetQuery().Query);
}
[Fact]
public void nested_fragments_should_generate_correct_query()
{
const string expectedMainQuery = "query FragmentTest {FragmentObject{items{Name ...SecondFragment}}}";
const string expectedFistFragment = "fragment FirstFragment on PromoObject {Expanded{Property1}}";
const string expectedSecondFragmment = "fragment SecondFragment on FragmentObject {PromoText PromoImage{Url} ...FirstFragment}";
const string expectedFullQuery = $"{expectedMainQuery}\n{expectedSecondFragmment}\n{expectedFistFragment}";

var firstFragment = new FragmentBuilder<PromoObject>("FirstFragment");
firstFragment.Fields(x => x.Expanded.Property1);

var secondFragment = new FragmentBuilder<FragmentObject>("SecondFragment");
secondFragment.Fields(x => x.PromoText, x=> x.PromoImage.Url);
secondFragment.Fragments(firstFragment);

GraphQueryBuilder graphQueryBuilder = new GraphQueryBuilder();
graphQueryBuilder
.OperationName("FragmentTest")
.ForType<FragmentObject>()
.Field(x => x.Name)
.Fragments(secondFragment)
.ToQuery()
.BuildQueries();

//expect children in secondary fragment
Assert.Equal(graphQueryBuilder.GetFragments().First().GetName(), "SecondFragment");
Assert.True(graphQueryBuilder.GetFragments().First().HasChildren);
Assert.Equal(graphQueryBuilder.GetFragments().First().ChildrenFragments.First().GetQuery().Query, expectedFistFragment);
//expect secondary fragment
Assert.Equal(graphQueryBuilder.GetFragments().First().GetQuery().Query, expectedSecondFragmment);
//expect full query
Assert.Equal(expectedFullQuery, graphQueryBuilder.GetQuery().Query);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ namespace EPiServer.ContentGraph.UnitTests
{
public class GenerateQueryTests
{
TypeQueryBuilder<RequestTypeObject> typeQueryBuilder;
private TypeQueryBuilder<RequestTypeObject> typeQueryBuilder;

public GenerateQueryTests()
{
typeQueryBuilder = new TypeQueryBuilder<RequestTypeObject>();
}

[Fact]
public void SelectNoneOfFieldsShouldThrowException()
{
typeQueryBuilder.Limit(100).Skip(0);
Assert.Throws<ArgumentNullException>(() => typeQueryBuilder.ToQuery());
}

[Fact]
public void SelectFields()
{
Expand All @@ -33,6 +36,20 @@ public void SelectFields()
Assert.Contains(expectedFields, query.GetQuery().Query);
query = query.BuildQueries();
}

[Fact]
public void SelectFieldWithAlias()
{
string expectedFields = @"{items{property1:Property1 property2:Property2}}";
typeQueryBuilder.Field(x => x.Property1, "property1").Field(x => x.Property2, "property2");
GraphQueryBuilder query = typeQueryBuilder.ToQuery();

Assert.NotNull(query.GetQuery());
//check selected fields
Assert.Contains(expectedFields, query.GetQuery().Query);
query = query.BuildQueries();
}

[Fact]
public void SelectNestedFields()
{
Expand All @@ -49,7 +66,7 @@ public void SelectNestedFields()
}

[Theory]
[InlineData(0,100)]
[InlineData(0, 100)]
[InlineData(0, int.MaxValue)]
public void QueryWithPaging(int skip, int limit)
{
Expand Down Expand Up @@ -86,6 +103,7 @@ public void FacetsQuery()
Assert.Contains(expectedFields, query.GetQuery().Query);
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
}

[Fact]
public void FacetsQueryWithFilter()
{
Expand Down Expand Up @@ -129,6 +147,7 @@ public void AutocompleteQuery()
Assert.Contains(expectedAutoComplete, query.GetQuery().Query);
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets} {expectedAutoComplete}}}", query.GetQuery().Query);
}

[Fact]
public void SubtypeQueryTests()
{
Expand All @@ -150,6 +169,7 @@ public void SubtypeQueryTests()
Assert.Contains(expectedFields, query.GetQuery().Query);
Assert.Equal($"RequestTypeObject{{{expectedFields} {expectedFacets}}}", query.GetQuery().Query);
}

[Fact]
public void LinkQueryTests()
{
Expand All @@ -158,8 +178,8 @@ public void LinkQueryTests()
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);
.Where(x => x.SubProperty, new StringFilterOperators().Match("test"))
.Facet(x => x.Property3);

typeQueryBuilder
.Field(x => x.Property1)
Expand Down Expand Up @@ -195,7 +215,7 @@ public void Multiple_types_query()
.ToQuery()
.BuildQueries();
}

var query = graphQueryBuilder.GetQuery();
Assert.Contains(expectedQuery1, query.Query);
Assert.Contains(expectedQuery2, query.Query);
Expand Down
Loading

0 comments on commit 32b0462

Please sign in to comment.