Browse Source

GraphQL reverse references. (#592)

* GraphQL reverse references.

* Fixes.

* Fix recursion.
release/4.x
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
9c4a2736f5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollectionAll.cs
  2. 4
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollectionPublished.cs
  3. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  4. 11
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryContentsByQuery.cs
  5. 12
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs
  6. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppQueriesGraphType.cs
  7. 18
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentActions.cs
  8. 77
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs
  9. 26
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs
  10. 19
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/QueryExecutionContext.cs
  11. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs
  12. 7
      backend/src/Squidex.Domain.Apps.Entities/Q.cs
  13. 3
      backend/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs
  14. 127
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs
  15. 48
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryTests.cs
  16. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs
  17. 10
      backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AExtensions.cs

4
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollectionAll.cs

@ -64,11 +64,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
await queryScheduledItems.PrepareAsync(collection, ct);
}
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query)
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query, Guid? referenced)
{
using (Profiler.TraceMethod<MongoContentRepository>("QueryAsyncByQuery"))
{
return await queryContentsByQuery.DoAsync(app, schema, query, SearchScope.All);
return await queryContentsByQuery.DoAsync(app, schema, query, referenced, SearchScope.All);
}
}

4
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollectionPublished.cs

@ -65,11 +65,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
await queryIdsAsync.PrepareAsync(collection, ct);
}
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query)
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query, Guid? referenced)
{
using (Profiler.TraceMethod<MongoContentRepository>("QueryAsyncByQuery"))
{
return await queryContentsByQuery.DoAsync(app, schema, query, SearchScope.Published);
return await queryContentsByQuery.DoAsync(app, schema, query, referenced, SearchScope.Published);
}
}

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs

@ -54,15 +54,15 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
await collectionPublished.InitializeAsync(ct);
}
public Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query, SearchScope scope)
public Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query, Guid? referenced, SearchScope scope)
{
if (scope == SearchScope.All)
{
return collectionAll.QueryAsync(app, schema, query);
return collectionAll.QueryAsync(app, schema, query, referenced);
}
else
{
return collectionPublished.QueryAsync(app, schema, query);
return collectionPublished.QueryAsync(app, schema, query, referenced);
}
}

11
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryContentsByQuery.cs

@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
await Collection.Indexes.CreateOneAsync(indexBySchema, cancellationToken: ct);
}
public async Task<IResultList<IContentEntity>> DoAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query, SearchScope scope)
public async Task<IResultList<IContentEntity>> DoAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query, Guid? referenced, SearchScope scope)
{
Guard.NotNull(app, nameof(app));
Guard.NotNull(schema, nameof(schema));
@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
}
}
var filter = CreateFilter(schema.Id, fullTextIds, query);
var filter = CreateFilter(schema.Id, fullTextIds, query, referenced);
var contentCount = Collection.Find(filter).CountDocumentsAsync();
var contentItems = FindContentsAsync(query, filter);
@ -155,7 +155,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
return query.Sort?.All(x => x.Path.ToString() == "mt" && x.Order == SortOrder.Descending) == true;
}
private static FilterDefinition<MongoContentEntity> CreateFilter(Guid schemaId, ICollection<Guid>? ids, ClrQuery? query)
private static FilterDefinition<MongoContentEntity> CreateFilter(Guid schemaId, ICollection<Guid>? ids, ClrQuery? query, Guid? referenced)
{
var filters = new List<FilterDefinition<MongoContentEntity>>
{
@ -176,6 +176,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
filters.Add(query.Filter.BuildFilter<MongoContentEntity>());
}
if (referenced != null)
{
filters.Add(Filter.AnyEq(x => x.ReferencedIds, referenced.Value));
}
return Filter.And(filters);
}
}

12
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs

@ -56,7 +56,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
graphQLSchema.RegisterValueConverter(JsonConverter.Instance);
graphQLSchema.RegisterValueConverter(InstantConverter.Instance);
InitializeContentTypes();
InitializeContentTypes(allSchemas, pageSizeContents);
}
private void BuildSchemas(List<ISchemaEntity> allSchemas)
@ -67,11 +67,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}
private void InitializeContentTypes()
private void InitializeContentTypes(List<ISchemaEntity> allSchemas, int pageSize)
{
var i = 0;
foreach (var contentType in contentTypes.Values)
{
contentType.Initialize(this);
var schema = allSchemas[i];
contentType.Initialize(this, schema, allSchemas, pageSize);
i++;
}
foreach (var contentType in contentTypes.Values)

6
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppQueriesGraphType.cs

@ -85,12 +85,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
private void AddContentQueries(Guid schemaId, string schemaType, string schemaName, IGraphType contentType, int pageSize)
{
var resolver = ContentActions.Query.Resolver(schemaId);
var resolver = ContentActions.QueryOrReferencing.Query(schemaId);
AddField(new FieldType
{
Name = $"query{schemaType}Contents",
Arguments = ContentActions.Query.Arguments(pageSize),
Arguments = ContentActions.QueryOrReferencing.Arguments(pageSize),
ResolvedType = new ListGraphType(new NonNullGraphType(contentType)),
Resolver = resolver,
Description = $"Query {schemaName} content items."
@ -99,7 +99,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
AddField(new FieldType
{
Name = $"query{schemaType}ContentsWithTotal",
Arguments = ContentActions.Query.Arguments(pageSize),
Arguments = ContentActions.QueryOrReferencing.Arguments(pageSize),
ResolvedType = new ContentsResultGraphType(schemaType, schemaName, contentType),
Resolver = resolver,
Description = $"Query {schemaName} content items with total count."

18
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentActions.cs

@ -82,7 +82,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
});
}
public static class Query
public static class QueryOrReferencing
{
private static QueryArguments? arguments;
@ -128,7 +128,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
};
}
public static IFieldResolver Resolver(Guid schemaId)
public static IFieldResolver Query(Guid schemaId)
{
var schemaIdValue = schemaId.ToString();
@ -139,6 +139,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
return ((GraphQLExecutionContext)c.UserContext).QueryContentsAsync(schemaIdValue, query);
});
}
public static IFieldResolver Referencing(Guid schemaId)
{
var schemaIdValue = schemaId.ToString();
return new FuncFieldResolver<IContentEntity, object?>(c =>
{
var query = c.BuildODataQuery();
var contentId = c.Source.Id;
return ((GraphQLExecutionContext)c.UserContext).QueryReferencingContentsAsync(schemaIdValue, query, c.Source.Id);
});
}
}
public static class Create

77
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs

@ -5,24 +5,25 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed class ContentGraphType : ObjectGraphType<IEnrichedContentEntity>
{
private readonly ISchemaEntity schema;
private readonly string schemaType;
private readonly string schemaName;
private readonly Guid schemaId;
public ContentGraphType(ISchemaEntity schema)
{
this.schema = schema;
this.schemaId = schema.Id;
schemaType = schema.TypeName();
schemaName = schema.DisplayName();
var schemaType = schema.TypeName();
var schemaName = schema.DisplayName();
Name = $"{schemaType}";
@ -99,11 +100,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
private bool CheckType(object value)
{
return value is IContentEntity content && content.SchemaId?.Id == schema.Id;
return value is IContentEntity content && content.SchemaId?.Id == schemaId;
}
public void Initialize(IGraphModel model)
public void Initialize(IGraphModel model, ISchemaEntity schema, IEnumerable<ISchemaEntity> all, int pageSize)
{
var schemaType = schema.TypeName();
var schemaName = schema.DisplayName();
AddField(new FieldType
{
Name = "url",
@ -137,6 +141,63 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Description = $"The flat data of the {schemaName} content."
});
}
foreach (var other in all.Where(x => References(x, schema)))
{
var referencingId = other.Id;
var referencingType = other.TypeName();
var referencingName = other.DisplayName();
var contentType = model.GetContentType(referencingId);
AddReferencingQueries(referencingId, referencingType, referencingName, contentType, pageSize);
}
}
private void AddReferencingQueries(Guid referencingId, string referencingType, string referencingName, IGraphType contentType, int pageSize)
{
var resolver = ContentActions.QueryOrReferencing.Referencing(referencingId);
AddField(new FieldType
{
Name = $"referencing{referencingType}Contents",
Arguments = ContentActions.QueryOrReferencing.Arguments(pageSize),
ResolvedType = new ListGraphType(new NonNullGraphType(contentType)),
Resolver = resolver,
Description = $"Query {referencingName} content items."
});
AddField(new FieldType
{
Name = $"referencing{referencingType}ContentsWithTotal",
Arguments = ContentActions.QueryOrReferencing.Arguments(pageSize),
ResolvedType = new ContentsResultGraphType(referencingType, referencingName, contentType),
Resolver = resolver,
Description = $"Query {referencingName} content items with total count."
});
}
private static bool References(ISchemaEntity other, ISchemaEntity schema)
{
var id = schema.Id;
return other.SchemaDef.Fields.Any(x => References(x, id));
}
private static bool References(IField field, Guid id)
{
switch (field)
{
case IField<ReferencesFieldProperties> reference:
return
reference.Properties.SchemaIds == null ||
reference.Properties.SchemaIds.Count == 0 ||
reference.Properties.SchemaIds.Contains(id);
case IArrayField arrayField:
return arrayField.Fields.Any(x => References(x, id));
}
return false;
}
}
}

26
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs

@ -55,6 +55,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
{
Guard.NotNull(context, nameof(context));
if (id == default)
{
throw new DomainObjectNotFoundException(id.ToString());
}
var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
CheckPermission(context, schema);
@ -85,6 +90,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
{
Guard.NotNull(context, nameof(context));
if (query == null)
{
return EmptyContents;
}
var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
CheckPermission(context, schema);
@ -110,13 +120,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
{
Guard.NotNull(context, nameof(context));
using (Profiler.TraceMethod<ContentQueryService>())
if (ids == null || ids.Count == 0)
{
if (ids == null || ids.Count == 0)
{
return EmptyContents;
}
return EmptyContents;
}
using (Profiler.TraceMethod<ContentQueryService>())
{
var contents = await QueryCoreAsync(context, ids);
var filtered =
@ -213,7 +223,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
{
var parsedQuery = await queryParser.ParseQueryAsync(context, schema, query);
return await QueryCoreAsync(context, schema, parsedQuery);
return await QueryCoreAsync(context, schema, parsedQuery, query.Reference);
}
private async Task<IResultList<IContentEntity>> QueryByIdsAsync(Context context, ISchemaEntity schema, Q query)
@ -228,9 +238,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return contentRepository.QueryAsync(context.App, new HashSet<Guid>(ids), context.Scope());
}
private Task<IResultList<IContentEntity>> QueryCoreAsync(Context context, ISchemaEntity schema, ClrQuery query)
private Task<IResultList<IContentEntity>> QueryCoreAsync(Context context, ISchemaEntity schema, ClrQuery query, Guid? referenced)
{
return contentRepository.QueryAsync(context.App, schema, query, context.Scope());
return contentRepository.QueryAsync(context.App, schema, query, referenced, context.Scope());
}
private Task<IResultList<IContentEntity>> QueryCoreAsync(Context context, ISchemaEntity schema, HashSet<Guid> ids)

19
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/QueryExecutionContext.cs

@ -73,9 +73,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return content;
}
public virtual async Task<IResultList<IEnrichedAssetEntity>> QueryAssetsAsync(string query)
public virtual async Task<IResultList<IEnrichedAssetEntity>> QueryAssetsAsync(string odata)
{
var assets = await assetQuery.QueryAsync(context, null, Q.Empty.WithODataQuery(query));
var q = Q.Empty.WithODataQuery(odata);
var assets = await assetQuery.QueryAsync(context, null, q);
foreach (var asset in assets)
{
@ -85,9 +87,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return assets;
}
public virtual async Task<IResultList<IEnrichedContentEntity>> QueryContentsAsync(string schemaIdOrName, string query)
public virtual async Task<IResultList<IEnrichedContentEntity>> QueryContentsAsync(string schemaIdOrName, string odata)
{
var result = await contentQuery.QueryAsync(context, schemaIdOrName, Q.Empty.WithODataQuery(query));
var q = Q.Empty.WithODataQuery(odata);
var result = await contentQuery.QueryAsync(context, schemaIdOrName, q);
foreach (var content in result)
{
@ -134,5 +138,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return ids.Select(cachedContents.GetOrDefault).NotNull().ToList();
}
public Task<IResultList<IEnrichedContentEntity>> QueryReferencingContentsAsync(string schemaIdOrName, string odata, Guid reference)
{
var q = Q.Empty.WithODataQuery(odata).WithReference(reference);
return contentQuery.QueryAsync(context, schemaIdOrName, q);
}
}
}

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs

@ -22,7 +22,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Repositories
Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, HashSet<Guid> ids, SearchScope scope);
Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query, SearchScope scope);
Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, ClrQuery query, Guid? referenced, SearchScope scope);
Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> QueryIdsAsync(Guid appId, Guid schemaId, FilterNode<ClrValue> filterNode);

7
backend/src/Squidex.Domain.Apps.Entities/Q.cs

@ -20,6 +20,8 @@ namespace Squidex.Domain.Apps.Entities
public IReadOnlyList<Guid> Ids { get; private set; }
public Guid? Reference { get; private set; }
public string? ODataQuery { get; private set; }
public string? JsonQuery { get; private set; }
@ -53,6 +55,11 @@ namespace Squidex.Domain.Apps.Entities
return Clone(c => c.Ids = ids.ToList());
}
public Q WithReference(Guid? reference)
{
return Clone(c => c.Reference = reference);
}
public Q WithIds(IEnumerable<Guid> ids)
{
return Clone(c => c.Ids = ids.ToList());

3
backend/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs

@ -5,10 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

127
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs

@ -460,6 +460,133 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
AssertResult(expected, result);
}
[Fact]
public async Task Should_also_fetch_referencing_contents_when_field_is_included_in_query()
{
var contentRefId = Guid.NewGuid();
var contentRef = TestContent.CreateRef(schemaRefId1, contentRefId, "ref1-field", "ref1");
var contentId = Guid.NewGuid();
var content = TestContent.Create(schemaId, contentId, contentRefId, Guid.Empty);
var query = @"
query {
findMyRefSchema1Content(id: ""<ID>"") {
id
referencingMySchemaContents(top: 30, skip: 5) {
id
data {
myString {
de
}
}
}
}
}".Replace("<ID>", contentRefId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<IReadOnlyList<Guid>>._))
.Returns(ResultList.CreateFrom(0, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.Id.ToString(), A<Q>.That.HasOData("?$top=30&$skip=5", contentRefId)))
.Returns(ResultList.CreateFrom(1, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
var expected = new
{
data = new
{
findMyRefSchema1Content = new
{
id = contentRefId,
referencingMySchemaContents = new[]
{
new
{
id = contentId,
data = new
{
myString = new
{
de = "value"
}
}
}
}
}
}
};
AssertResult(expected, result);
}
[Fact]
public async Task Should_also_fetch_referencing_contents_with_total_when_field_is_included_in_query()
{
var contentRefId = Guid.NewGuid();
var contentRef = TestContent.CreateRef(schemaRefId1, contentRefId, "ref1-field", "ref1");
var contentId = Guid.NewGuid();
var content = TestContent.Create(schemaId, contentId, contentRefId, Guid.Empty);
var query = @"
query {
findMyRefSchema1Content(id: ""<ID>"") {
id
referencingMySchemaContentsWithTotal(top: 30, skip: 5) {
total
items {
id
data {
myString {
de
}
}
}
}
}
}".Replace("<ID>", contentRefId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<IReadOnlyList<Guid>>._))
.Returns(ResultList.CreateFrom(0, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.Id.ToString(), A<Q>.That.HasOData("?$top=30&$skip=5", contentRefId)))
.Returns(ResultList.CreateFrom(1, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
var expected = new
{
data = new
{
findMyRefSchema1Content = new
{
id = contentRefId,
referencingMySchemaContentsWithTotal = new
{
total = 1,
items = new[]
{
new
{
id = contentId,
data = new
{
myString = new
{
de = "value"
}
}
}
}
}
}
}
};
AssertResult(expected, result);
}
[Fact]
public async Task Should_also_fetch_union_contents_when_field_is_included_in_query()
{

48
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryTests.cs

@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
}
[Fact]
public async Task Should_query_contents_by_filter()
public async Task Should_query_contents_ids_by_filter()
{
var filter = F.Eq("data.value.iv", 12);
@ -69,6 +69,23 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_by_filter()
{
var query = new ClrQuery
{
Sort = new List<SortNode>
{
new SortNode("lastModified", SortOrder.Descending)
},
Filter = F.Eq("data.value.iv", 12)
};
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), query, null, SearchScope.Published);
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_scheduled()
{
@ -78,7 +95,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
}
[Fact]
public async Task Should_query_contents_by_default()
public async Task Should_query_contents_with_default_query()
{
var query = new ClrQuery();
@ -87,6 +104,16 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_default_query_and_id()
{
var query = new ClrQuery();
var contents = await QueryAsync(query, id: Guid.NewGuid());
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_large_skip()
{
@ -129,7 +156,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
Assert.NotEmpty(contents);
}
private async Task<IResultList<IContentEntity>> QueryAsync(ClrQuery clrQuery, int take = 1000, int skip = 100)
[Fact]
public async Task Should_query_contents_with_query_filter_and_id()
{
var query = new ClrQuery
{
Filter = F.Eq("data.value.iv", 12)
};
var contents = await QueryAsync(query, 1000, 0, id: Guid.NewGuid());
Assert.Empty(contents);
}
private async Task<IResultList<IContentEntity>> QueryAsync(ClrQuery clrQuery, int take = 1000, int skip = 100, Guid? id = null)
{
if (clrQuery.Take == long.MaxValue)
{
@ -149,7 +189,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
};
}
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), clrQuery, SearchScope.All);
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), clrQuery, id, SearchScope.All);
return contents;
}

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs

@ -183,16 +183,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
[InlineData(0, 0, SearchScope.Published)]
public async Task QueryAsync_should_return_contents(int isFrontend, int unpublished, SearchScope scope)
{
var reference = Guid.NewGuid();
var ctx =
CreateContext(isFrontend: isFrontend == 1, allowSchema: true)
.WithUnpublished(unpublished == 1);
var content = CreateContent(contentId);
A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, A<ClrQuery>._, scope))
A.CallTo(() => contentRepository.QueryAsync(ctx.App, schema, A<ClrQuery>._, reference, scope))
.Returns(ResultList.CreateFrom(5, content));
var result = await sut.QueryAsync(ctx, schemaId.Name, Q.Empty);
var result = await sut.QueryAsync(ctx, schemaId.Name, Q.Empty.WithReference(reference));
Assert.Equal(contentData, result[0].Data);
Assert.Equal(contentId, result[0].Id);

10
backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AExtensions.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using FakeItEasy;
@ -19,9 +20,14 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
return that.Matches(x => x.Query!.ToString() == query);
}
public static Q HasOData(this INegatableArgumentConstraintManager<Q> that, string query)
public static Q HasOData(this INegatableArgumentConstraintManager<Q> that, string odata)
{
return that.Matches(x => x.ODataQuery == query);
return that.HasOData(odata, null);
}
public static Q HasOData(this INegatableArgumentConstraintManager<Q> that, string odata, Guid? reference = null)
{
return that.Matches(x => x.ODataQuery == odata && x.Reference == reference);
}
public static ClrQuery Is(this INegatableArgumentConstraintManager<ClrQuery> that, string query)

Loading…
Cancel
Save