Browse Source

Merge branch 'master' of github.com:Squidex/squidex into fix/surrogate-keys

# Conflicts:
#	backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs
#	backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs
#	backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryServiceTests.cs
#	backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs
pull/637/head
Sebastian 5 years ago
parent
commit
a7e6da6f6c
  1. 12
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs
  2. 29
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs
  3. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  4. 7
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/OperationBase.cs
  5. 7
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByIds.cs
  6. 14
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByQuery.cs
  7. 17
      backend/src/Squidex.Domain.Apps.Entities/Assets/AssetExtensions.cs
  8. 2
      backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsSearchSource.cs
  9. 7
      backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetEnricher.cs
  10. 15
      backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryParser.cs
  11. 2
      backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs
  12. 7
      backend/src/Squidex.Domain.Apps.Entities/Contents/BulkUpdateCommandMiddleware.cs
  13. 74
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentExtensions.cs
  14. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentsSearchSource.cs
  15. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  16. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppQueriesGraphType.cs
  17. 13
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs
  18. 26
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs
  19. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs
  20. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/SharedTypes.cs
  21. 11
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs
  22. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs
  23. 27
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/QueryExecutionContext.cs
  24. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs
  25. 8
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs
  26. 8
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs
  27. 7
      backend/src/Squidex.Domain.Apps.Entities/Contents/ReferencesFluidExtension.cs
  28. 84
      backend/src/Squidex.Domain.Apps.Entities/Context.cs
  29. 55
      backend/src/Squidex.Domain.Apps.Entities/ContextExtensions.cs
  30. 11
      backend/src/Squidex.Domain.Apps.Entities/Q.cs
  31. 2
      backend/src/Squidex.Web/ApiPermissionAttribute.cs
  32. 2
      backend/src/Squidex.Web/ContextExtensions.cs
  33. 2
      backend/src/Squidex.Web/ContextProvider.cs
  34. 10
      backend/src/Squidex.Web/Pipeline/AppResolver.cs
  35. 4
      backend/src/Squidex.Web/Resources.cs
  36. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs
  37. 21
      backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs
  38. 2
      backend/src/Squidex/Areas/Api/Controllers/UI/UIController.cs
  39. 2
      backend/src/Squidex/Config/Domain/StoreServices.cs
  40. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppCommandMiddlewareTests.cs
  41. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs
  42. 7
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetEnricherTests.cs
  43. 32
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryParserTests.cs
  44. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryServiceTests.cs
  45. 21
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/BulkUpdateCommandMiddlewareTests.cs
  46. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentCommandMiddlewareTests.cs
  47. 63
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs
  48. 24
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs
  49. 98
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryTests.cs
  50. 8
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryParserTests.cs
  51. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs
  52. 8
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichWithWorkflowsTests.cs
  53. 11
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs
  54. 13
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs
  55. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs
  56. 5
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleEnricherTests.cs
  57. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleQueryServiceTests.cs
  58. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Search/SearchManagerTests.cs
  59. 9
      backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AExtensions.cs
  60. 4
      backend/tests/Squidex.Web.Tests/ApiPermissionAttributeTests.cs
  61. 15
      backend/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs
  62. 3
      backend/tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs
  63. 2
      backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj

12
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs

@ -97,7 +97,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
.ToListAsync();
long assetTotal = assetEntities.Count;
if (assetTotal >= q.Query.Take || q.Query.Skip > 0)
if (q.NoTotal)
{
assetTotal = -1;
}
else if (assetEntities.Count >= q.Query.Take || q.Query.Skip > 0)
{
assetTotal = await Collection.Find(filter).CountDocumentsAsync();
}
@ -118,7 +122,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
.ToListAsync();
long assetTotal = assetEntities.Count;
if (assetTotal >= q.Query.Take || q.Query.Skip > 0)
if (q.NoTotal)
{
assetTotal = -1;
}
else if (assetEntities.Count >= q.Query.Take || q.Query.Skip > 0)
{
assetTotal = await Collection.Find(filter).CountDocumentsAsync();
}

29
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs

@ -33,8 +33,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
private readonly QueryReferrers queryReferrers;
private readonly QueryScheduled queryScheduled;
private readonly string name;
private readonly bool useWildcardIndex;
public MongoContentCollection(string name, IMongoDatabase database, IAppProvider appProvider)
public MongoContentCollection(string name, IMongoDatabase database, IAppProvider appProvider, bool useWildcardIndex)
: base(database)
{
this.name = name;
@ -46,6 +47,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
queryReferences = new QueryReferences(queryByIds);
queryReferrers = new QueryReferrers();
queryScheduled = new QueryScheduled();
this.useWildcardIndex = useWildcardIndex;
}
public IMongoCollection<MongoContentEntity> GetInternalCollection()
@ -60,13 +63,23 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default)
{
await queryAsStream.PrepareAsync(collection, ct);
await queryBdId.PrepareAsync(collection, ct);
await queryByIds.PrepareAsync(collection, ct);
await queryByQuery.PrepareAsync(collection, ct);
await queryReferences.PrepareAsync(collection, ct);
await queryReferrers.PrepareAsync(collection, ct);
await queryScheduled.PrepareAsync(collection, ct);
if (useWildcardIndex)
{
await collection.Indexes.CreateOneAsync(
new CreateIndexModel<MongoContentEntity>(
Index.Wildcard()
), null, ct);
}
var skipIndex = useWildcardIndex;
await queryAsStream.PrepareAsync(collection, skipIndex, ct);
await queryBdId.PrepareAsync(collection, skipIndex, ct);
await queryByIds.PrepareAsync(collection, skipIndex, ct);
await queryByQuery.PrepareAsync(collection, skipIndex, ct);
await queryReferences.PrepareAsync(collection, skipIndex, ct);
await queryReferrers.PrepareAsync(collection, skipIndex, ct);
await queryScheduled.PrepareAsync(collection, skipIndex, ct);
}
public IAsyncEnumerable<IContentEntity> StreamAll(DomainId appId, HashSet<DomainId>? schemaIds)

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

@ -32,17 +32,17 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
StatusSerializer.Register();
}
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider)
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider, bool useWildcardIndex)
{
Guard.NotNull(appProvider, nameof(appProvider));
collectionAll =
new MongoContentCollection(
"States_Contents_All3", database, appProvider);
"States_Contents_All3", database, appProvider, useWildcardIndex);
collectionPublished =
new MongoContentCollection(
"States_Contents_Published3", database, appProvider);
"States_Contents_Published3", database, appProvider, useWildcardIndex);
}
public async Task InitializeAsync(CancellationToken ct = default)

7
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/OperationBase.cs

@ -21,11 +21,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
public IMongoCollection<MongoContentEntity> Collection { get; private set; }
public Task PrepareAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default)
public async Task PrepareAsync(IMongoCollection<MongoContentEntity> collection, bool skipIndex, CancellationToken ct = default)
{
Collection = collection;
return PrepareAsync(ct);
if (!skipIndex)
{
await PrepareAsync(ct);
}
}
protected virtual Task PrepareAsync(CancellationToken ct = default)

7
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByIds.cs

@ -48,13 +48,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
var contentEntities = await FindContentsAsync(q.Query, filter);
var contentTotal = (long)contentEntities.Count;
if (contentEntities.Count > 0)
if (q.NoTotal)
{
if (contentTotal >= q.Query.Take || q.Query.Skip > 0)
contentTotal = -1;
}
else if (contentTotal >= q.Query.Take || q.Query.Skip > 0)
{
contentTotal = await Collection.Find(filter).CountDocumentsAsync();
}
}
return ResultList.Create(contentTotal, contentEntities);
}

14
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByQuery.cs

@ -105,13 +105,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
var contentEntities = await FindContentsAsync(query, filter);
var contentTotal = (long)contentEntities.Count;
if (contentEntities.Count > 0)
if (q.NoTotal)
{
if (contentTotal >= q.Query.Take || q.Query.Skip > 0)
contentTotal = -1;
}
else if (contentTotal >= q.Query.Take || q.Query.Skip > 0)
{
contentTotal = await Collection.Find(filter).CountDocumentsAsync();
}
}
return ResultList.Create<IContentEntity>(contentTotal, contentEntities);
}
@ -140,13 +141,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
var contentEntities = await FindContentsAsync(query, filter);
var contentTotal = (long)contentEntities.Count;
if (contentEntities.Count > 0)
if (q.NoTotal)
{
if (contentTotal >= q.Query.Take || q.Query.Skip > 0)
contentTotal = -1;
}
else if (contentTotal >= q.Query.Take || q.Query.Skip > 0)
{
contentTotal = await Collection.Find(filter).CountDocumentsAsync();
}
}
return ResultList.Create<IContentEntity>(contentTotal, contentEntities);
}

17
backend/src/Squidex.Domain.Apps.Entities/Assets/AssetExtensions.cs

@ -11,23 +11,14 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
private const string HeaderNoEnrichment = "X-NoAssetEnrichment";
public static bool ShouldEnrichAsset(this Context context)
public static bool ShouldSkipAssetEnrichment(this Context context)
{
return !context.Headers.ContainsKey(HeaderNoEnrichment);
return context.Headers.ContainsKey(HeaderNoEnrichment);
}
public static Context WithoutAssetEnrichment(this Context context, bool value = true)
public static ICloneBuilder WithoutAssetEnrichment(this ICloneBuilder builder, bool value = true)
{
if (value)
{
context.Headers[HeaderNoEnrichment] = "1";
}
else
{
context.Headers.Remove(HeaderNoEnrichment);
}
return context;
return builder.WithBoolean(HeaderNoEnrichment, value);
}
}
}

2
backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsSearchSource.cs

@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
var result = new SearchResults();
if (context.Permissions.Allows(Permissions.AppAssetsRead, context.App.Name))
if (context.UserPermissions.Allows(Permissions.AppAssetsRead, context.App.Name))
{
var filter = ClrFilter.Contains("fileName", query);

7
backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetEnricher.cs

@ -58,7 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
requestCache.AddDependency(asset.UniqueId, asset.Version);
}
if (ShouldEnrich(context))
if (!context.ShouldSkipAssetEnrichment())
{
await EnrichTagsAsync(results);
@ -134,10 +134,5 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
return await tagService.DenormalizeTagsAsync(group.Key, TagGroups.Assets, uniqueIds);
}
private static bool ShouldEnrich(Context context)
{
return context.ShouldEnrichAsset();
}
}
}

15
backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryParser.cs

@ -46,25 +46,32 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
this.options = options.Value;
}
public virtual async Task<Q> ParseQueryAsync(Context context, Q q)
public virtual async Task<Q> ParseAsync(Context context, Q q)
{
Guard.NotNull(context, nameof(context));
Guard.NotNull(q, nameof(q));
using (Profiler.TraceMethod<AssetQueryParser>())
{
var query = ParseQuery(q);
var query = ParseClrQuery(q);
await TransformTagAsync(context, query);
WithSorting(query);
WithPaging(query);
return q!.WithQuery(query);
q = q.WithQuery(query);
if (context.ShouldSkipTotal())
{
q = q.WithoutTotal();
}
return q;
}
}
private ClrQuery ParseQuery(Q q)
private ClrQuery ParseClrQuery(Q q)
{
var query = q.Query;

2
backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs

@ -166,7 +166,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
using (Profiler.TraceMethod<AssetQueryService>())
{
q = await queryParser.ParseQueryAsync(context, q);
q = await queryParser.ParseAsync(context, q);
var assets = await assetRepository.QueryAsync(context.App.Id, parentId, q);

7
backend/src/Squidex.Domain.Apps.Entities/Contents/BulkUpdateCommandMiddleware.cs

@ -80,7 +80,12 @@ namespace Squidex.Domain.Apps.Entities.Contents
PropagateCompletion = true
});
var requestContext = contextProvider.Context.WithoutContentEnrichment().WithUnpublished(true);
var requestContext = contextProvider.Context.Clone(b => b
.WithoutContentEnrichment()
.WithoutCleanup()
.WithUnpublished(true)
.WithoutTotal());
var requestedSchema = bulkUpdates.SchemaId.Name;
var results = new ConcurrentBag<BulkUpdateResultItem>();

74
backend/src/Squidex.Domain.Apps.Entities/Contents/ContextExtensions.cs → backend/src/Squidex.Domain.Apps.Entities/Contents/ContentExtensions.cs

@ -15,7 +15,7 @@ using Squidex.Infrastructure.Caching;
namespace Squidex.Domain.Apps.Entities.Contents
{
public static class ContextExtensions
public static class ContentExtensions
{
private const string HeaderFlatten = "X-Flatten";
private const string HeaderLanguages = "X-Languages";
@ -44,24 +44,24 @@ namespace Squidex.Domain.Apps.Entities.Contents
return context.ShouldProvideUnpublished() || context.IsFrontendClient ? SearchScope.All : SearchScope.Published;
}
public static bool ShouldCleanup(this Context context)
public static bool ShouldSkipCleanup(this Context context)
{
return !context.Headers.ContainsKey(HeaderNoCleanup);
return context.Headers.ContainsKey(HeaderNoCleanup);
}
public static Context WithoutCleanup(this Context context, bool value = true)
public static ICloneBuilder WithoutCleanup(this ICloneBuilder builder, bool value = true)
{
return SetBoolean(context, HeaderNoCleanup, value);
return builder.WithBoolean(HeaderNoCleanup, value);
}
public static bool ShouldEnrichContent(this Context context)
public static bool ShouldSkipContentEnrichment(this Context context)
{
return !context.Headers.ContainsKey(HeaderNoEnrichment);
return context.Headers.ContainsKey(HeaderNoEnrichment);
}
public static Context WithoutContentEnrichment(this Context context, bool value = true)
public static ICloneBuilder WithoutContentEnrichment(this ICloneBuilder builder, bool value = true)
{
return SetBoolean(context, HeaderNoEnrichment, value);
return builder.WithBoolean(HeaderNoEnrichment, value);
}
public static bool ShouldProvideUnpublished(this Context context)
@ -69,9 +69,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
return context.Headers.ContainsKey(HeaderUnpublished);
}
public static Context WithUnpublished(this Context context, bool value = true)
public static ICloneBuilder WithUnpublished(this ICloneBuilder builder, bool value = true)
{
return SetBoolean(context, HeaderUnpublished, value);
return builder.WithBoolean(HeaderUnpublished, value);
}
public static bool ShouldFlatten(this Context context)
@ -79,9 +79,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
return context.Headers.ContainsKey(HeaderFlatten);
}
public static Context WithFlatten(this Context context, bool value = true)
public static ICloneBuilder WithFlatten(this ICloneBuilder builder, bool value = true)
{
return SetBoolean(context, HeaderFlatten, value);
return builder.WithBoolean(HeaderFlatten, value);
}
public static bool ShouldResolveFlow(this Context context)
@ -89,9 +89,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
return context.Headers.ContainsKey(HeaderResolveFlow);
}
public static Context WithResolveFlow(this Context context, bool value = true)
public static ICloneBuilder WithResolveFlow(this ICloneBuilder builder, bool value = true)
{
return SetBoolean(context, HeaderResolveFlow, value);
return builder.WithBoolean(HeaderResolveFlow, value);
}
public static bool ShouldResolveLanguages(this Context context)
@ -99,9 +99,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
return !context.Headers.ContainsKey(HeaderNoResolveLanguages);
}
public static Context WithoutResolveLanguages(this Context context, bool value = true)
public static ICloneBuilder WithoutResolveLanguages(this ICloneBuilder builder, bool value = true)
{
return SetBoolean(context, HeaderNoResolveLanguages, value);
return builder.WithBoolean(HeaderNoResolveLanguages, value);
}
public static IEnumerable<string> AssetUrls(this Context context)
@ -114,18 +114,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
return Enumerable.Empty<string>();
}
public static Context WithAssetUrlsToResolve(this Context context, IEnumerable<string> fieldNames)
public static ICloneBuilder WithAssetUrlsToResolve(this ICloneBuilder builder, IEnumerable<string>? fieldNames)
{
if (fieldNames?.Any() == true)
{
context.Headers[HeaderResolveUrls] = string.Join(",", fieldNames);
}
else
{
context.Headers.Remove(HeaderResolveUrls);
}
return context;
return builder.WithStrings(HeaderResolveUrls, fieldNames);
}
public static IEnumerable<Language> Languages(this Context context)
@ -148,32 +139,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
return Enumerable.Empty<Language>();
}
public static Context WithLanguages(this Context context, IEnumerable<string> fieldNames)
{
if (fieldNames?.Any() == true)
{
context.Headers[HeaderLanguages] = string.Join(",", fieldNames);
}
else
{
context.Headers.Remove(HeaderLanguages);
}
return context;
}
private static Context SetBoolean(Context context, string key, bool value)
{
if (value)
public static ICloneBuilder WithLanguages(this ICloneBuilder builder, IEnumerable<string> fieldNames)
{
context.Headers[key] = "1";
}
else
{
context.Headers.Remove(key);
}
return context;
return builder.WithStrings(HeaderLanguages, fieldNames);
}
}
}

2
backend/src/Squidex.Domain.Apps.Entities/Contents/ContentsSearchSource.cs

@ -105,7 +105,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
private static bool HasPermission(Context context, string schemaName)
{
return context.Permissions.Allows(Permissions.AppContentsReadOwn, context.App.Name, schemaName);
return context.UserPermissions.Allows(Permissions.AppContentsReadOwn, context.App.Name, schemaName);
}
private static string FormatName(IEnrichedContentEntity content, string masterLanguage)

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs

@ -64,7 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public GraphQLExecutionContext WithContext(Context newContext)
{
context = newContext.WithoutCleanup().WithoutContentEnrichment();
context = newContext.Clone(b => b.WithoutCleanup().WithoutContentEnrichment());
return this;
}

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

@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Name = $"query{schemaInfo.TypeName}ContentsWithTotal",
Arguments = ContentActions.QueryOrReferencing.Arguments,
ResolvedType = resultType,
Resolver = ContentActions.QueryOrReferencing.Query,
Resolver = ContentActions.QueryOrReferencing.QueryWithTotal,
Description = $"Query {schemaInfo.DisplayName} content items with total count."
}).WithSchemaId(schemaInfo);
}

13
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs

@ -100,7 +100,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets
{
var query = fieldContext.BuildODataQuery();
return await context.QueryAssetsAsync(query);
var q = Q.Empty.WithODataQuery(query).WithoutTotal();
return await context.QueryAssetsAsync(q);
});
public static readonly IFieldResolver ResolverWithTotal = Resolvers.Async<object, object>(async (_, fieldContext, context) =>
{
var query = fieldContext.BuildODataQuery();
var q = Q.Empty.WithODataQuery(query);
return await context.QueryAssetsAsync(q);
});
}
}

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

@ -142,14 +142,36 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
{
var query = fieldContext.BuildODataQuery();
return await context.QueryContentsAsync(fieldContext.FieldDefinition.SchemaId(), query);
var q = Q.Empty.WithODataQuery(query).WithoutTotal();
return await context.QueryContentsAsync(fieldContext.FieldDefinition.SchemaId(), q);
});
public static readonly IFieldResolver QueryWithTotal = Resolvers.Async<object, object>(async (_, fieldContext, context) =>
{
var query = fieldContext.BuildODataQuery();
var q = Q.Empty.WithODataQuery(query);
return await context.QueryContentsAsync(fieldContext.FieldDefinition.SchemaId(), q);
});
public static readonly IFieldResolver Referencing = Resolvers.Async<IContentEntity, object?>(async (source, fieldContext, context) =>
{
var query = fieldContext.BuildODataQuery();
return await context.QueryReferencingContentsAsync(fieldContext.FieldDefinition.SchemaId(), query, source.Id);
var q = Q.Empty.WithODataQuery(query).WithReference(source.Id).WithoutTotal();
return await context.QueryContentsAsync(fieldContext.FieldDefinition.SchemaId(), q);
});
public static readonly IFieldResolver ReferencingWithTotal = Resolvers.Async<IContentEntity, object?>(async (source, fieldContext, context) =>
{
var query = fieldContext.BuildODataQuery();
var q = Q.Empty.WithODataQuery(query).WithReference(source.Id);
return await context.QueryContentsAsync(fieldContext.FieldDefinition.SchemaId(), q);
});
}

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

@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
Name = $"referencing{referencingSchemaInfo.TypeName}ContentsWithTotal",
Arguments = ContentActions.QueryOrReferencing.Arguments,
ResolvedType = contentResultsTyp,
Resolver = ContentActions.QueryOrReferencing.Referencing,
Resolver = ContentActions.QueryOrReferencing.ReferencingWithTotal,
Description = $"Query {referencingSchemaInfo.DisplayName} content items with total count."
}).WithSchemaId(referencingSchemaInfo);
}

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/SharedTypes.cs

@ -90,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Name = "queryAssetsWithTotal",
Arguments = AssetActions.Query.Arguments,
ResolvedType = AssetsResult,
Resolver = AssetActions.Query.Resolver,
Resolver = AssetActions.Query.ResolverWithTotal,
Description = "Get assets and total count."
};
});

11
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs

@ -63,14 +63,19 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
using (Profiler.TraceMethod<ContentQueryParser>())
{
var query = ParseQuery(context, q, schema);
var query = ParseClrQuery(context, q, schema);
await TransformFilterAsync(query, context, schema);
WithSorting(query);
WithPaging(query);
q = q!.WithQuery(query);
q = q.WithQuery(query);
if (context.ShouldSkipTotal())
{
q = q.WithoutTotal();
}
return q;
}
@ -113,7 +118,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
}
}
private ClrQuery ParseQuery(Context context, Q q, ISchemaEntity? schema)
private ClrQuery ParseClrQuery(Context context, Q q, ISchemaEntity? schema)
{
var query = q.Query;

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

@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
if (!HasPermission(context, schema, Permissions.AppContentsRead))
{
q.CreatedBy = context.User.Token();
q = q with { CreatedBy = context.User.Token() };
}
q = await queryParser.ParseAsync(context, q, schema);
@ -211,7 +211,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
private static bool HasPermission(Context context, ISchemaEntity schema, string permissionId)
{
return context.Permissions.Allows(permissionId, context.App.Name, schema.SchemaDef.Name);
return context.UserPermissions.Allows(permissionId, context.App.Name, schema.SchemaDef.Name);
}
}
}

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

@ -39,10 +39,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return contentQuery.FindAsync(Context, schemaIdOrName, id, version);
}
public virtual async Task<IResultList<IEnrichedAssetEntity>> QueryAssetsAsync(string odata)
public virtual async Task<IResultList<IEnrichedAssetEntity>> QueryAssetsAsync(Q q)
{
var q = Q.Empty.WithODataQuery(odata);
IResultList<IEnrichedAssetEntity> assets;
await maxRequests.WaitAsync();
@ -63,10 +61,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return assets;
}
public virtual async Task<IResultList<IEnrichedContentEntity>> QueryContentsAsync(string schemaIdOrName, string odata)
public virtual async Task<IResultList<IEnrichedContentEntity>> QueryContentsAsync(string schemaIdOrName, Q q)
{
var q = Q.Empty.WithODataQuery(odata);
IResultList<IEnrichedContentEntity> contents;
await maxRequests.WaitAsync();
@ -100,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
await maxRequests.WaitAsync();
try
{
assets = await assetQuery.QueryAsync(Context, null, Q.Empty.WithIds(notLoadedAssets));
assets = await assetQuery.QueryAsync(Context, null, Q.Empty.WithIds(notLoadedAssets).WithoutTotal());
}
finally
{
@ -129,7 +125,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
await maxRequests.WaitAsync();
try
{
contents = await contentQuery.QueryAsync(Context, Q.Empty.WithIds(notLoadedContents));
contents = await contentQuery.QueryAsync(Context, Q.Empty.WithIds(notLoadedContents).WithoutTotal());
}
finally
{
@ -144,20 +140,5 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return ids.Select(cachedContents.GetOrDefault).NotNull().ToList();
}
public async Task<IResultList<IEnrichedContentEntity>> QueryReferencingContentsAsync(string schemaIdOrName, string odata, DomainId reference)
{
var q = Q.Empty.WithODataQuery(odata).WithReference(reference);
await maxRequests.WaitAsync();
try
{
return await contentQuery.QueryAsync(Context, schemaIdOrName, q);
}
finally
{
maxRequests.Release();
}
}
}
}

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs

@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
private async Task<ValueConverter?> CleanReferencesAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas)
{
if (context.ShouldCleanup())
if (!context.ShouldSkipCleanup())
{
var ids = new HashSet<DomainId>();

8
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs

@ -118,7 +118,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
return EmptyAssets;
}
var assets = await assetQuery.QueryAsync(context.Clone().WithoutAssetEnrichment(true), null, Q.Empty.WithIds(ids));
var queryContext = context.Clone(b => b
.WithoutAssetEnrichment(true)
.WithoutTotal());
var assets = await assetQuery.QueryAsync(queryContext, null, Q.Empty.WithIds(ids));
return assets.ToLookup(x => x.Id);
}
@ -133,7 +137,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
private static bool ShouldEnrich(Context context)
{
return context.IsFrontendClient && context.ShouldEnrichContent();
return context.IsFrontendClient && !context.ShouldSkipContentEnrichment();
}
}
}

8
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs

@ -153,14 +153,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
return EmptyContents;
}
var references = await ContentQuery.QueryAsync(context.Clone().WithoutContentEnrichment(true), Q.Empty.WithIds(ids));
var queryContext = context.Clone(b => b
.WithoutContentEnrichment(true)
.WithoutTotal());
var references = await ContentQuery.QueryAsync(queryContext, Q.Empty.WithIds(ids));
return references.ToLookup(x => x.Id);
}
private static bool ShouldEnrich(Context context)
{
return context.IsFrontendClient && context.ShouldEnrichContent();
return context.IsFrontendClient && !context.ShouldSkipContentEnrichment();
}
}
}

7
backend/src/Squidex.Domain.Apps.Entities/Contents/ReferencesFluidExtension.cs

@ -49,13 +49,10 @@ namespace Squidex.Domain.Apps.Entities.Contents
return Completion.Normal;
}
var appContext =
Context.Admin()
var appContext = Context.Admin(app).Clone(b => b
.WithoutContentEnrichment()
.WithoutCleanup()
.WithUnpublished();
appContext.App = app;
.WithUnpublished());
var id = (await arguments[1].Expression.EvaluateAsync(context)).ToStringValue();

84
backend/src/Squidex.Domain.Apps.Entities/Context.cs

@ -20,66 +20,104 @@ namespace Squidex.Domain.Apps.Entities
{
public sealed class Context
{
public IDictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private static readonly IReadOnlyDictionary<string, string> EmptyHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public IAppEntity App { get; set; }
public IReadOnlyDictionary<string, string> Headers { get; }
public ClaimsPermissions UserPermissions { get; }
public ClaimsPrincipal User { get; }
public ClaimsPermissions Permissions { get; }
public IAppEntity App { get; set; }
public bool IsFrontendClient { get; }
public bool IsFrontendClient => User.IsInClient(DefaultClients.Frontend);
public Context(ClaimsPrincipal user)
public Context(ClaimsPrincipal user, IAppEntity app)
: this(app, user, user.Claims.Permissions(), EmptyHeaders)
{
Guard.NotNull(user, nameof(user));
User = user;
Permissions = User.Claims.Permissions();
IsFrontendClient = User.IsInClient(DefaultClients.Frontend);
}
public Context(ClaimsPrincipal user, IAppEntity app)
: this(user)
private Context(IAppEntity app, ClaimsPrincipal user, ClaimsPermissions userPermissions, IReadOnlyDictionary<string, string> headers)
{
App = app;
User = user;
UserPermissions = userPermissions;
Headers = headers;
}
public static Context Anonymous()
public static Context Anonymous(IAppEntity app)
{
var claimsIdentity = new ClaimsIdentity();
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
return new Context(claimsPrincipal);
return new Context(claimsPrincipal, app);
}
public static Context Admin()
public static Context Admin(IAppEntity app)
{
var claimsIdentity = new ClaimsIdentity();
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
claimsIdentity.AddClaim(new Claim(SquidexClaimTypes.Permissions, P.All));
return new Context(claimsPrincipal);
return new Context(claimsPrincipal, app);
}
public bool Allows(string permissionId, string schema = Permission.Any)
{
return Permissions.Allows(permissionId, App.Name, schema);
return UserPermissions.Allows(permissionId, App.Name, schema);
}
private sealed class HeaderBuilder : ICloneBuilder
{
private readonly Context context;
private Dictionary<string, string>? headers;
public HeaderBuilder(Context context)
{
this.context = context;
}
public Context Build()
{
if (headers != null)
{
return new Context(context.App!, context.User, context.UserPermissions, headers);
}
return context;
}
public Context Clone()
public void Remove(string key)
{
var clone = new Context(User, App);
headers ??= new Dictionary<string, string>(context.Headers, StringComparer.OrdinalIgnoreCase);
headers.Remove(key);
}
foreach (var (key, value) in Headers)
public void SetHeader(string key, string value)
{
clone.Headers[key] = value;
headers ??= new Dictionary<string, string>(context.Headers, StringComparer.OrdinalIgnoreCase);
headers[key] = value;
}
}
public Context Clone(Action<ICloneBuilder> action)
{
var builder = new HeaderBuilder(this);
action(builder);
return clone;
return builder.Build();
}
}
public interface ICloneBuilder
{
void SetHeader(string key, string value);
void Remove(string key);
}
}

55
backend/src/Squidex.Domain.Apps.Entities/ContextExtensions.cs

@ -0,0 +1,55 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
namespace Squidex.Domain.Apps.Entities
{
public static class ContextExtensions
{
private const string HeaderNoTotal = "X-NoTotal";
public static bool ShouldSkipTotal(this Context context)
{
return context.Headers.ContainsKey(HeaderNoTotal);
}
public static ICloneBuilder WithoutTotal(this ICloneBuilder builder, bool value = true)
{
return builder.WithBoolean(HeaderNoTotal, value);
}
public static ICloneBuilder WithBoolean(this ICloneBuilder builder, string key, bool value)
{
if (value)
{
builder.SetHeader(key, "1");
}
else
{
builder.Remove(key);
}
return builder;
}
public static ICloneBuilder WithStrings(this ICloneBuilder builder, string key, IEnumerable<string>? values)
{
if (values?.Any() == true)
{
builder.SetHeader(key, string.Join(",", values));
}
else
{
builder.Remove(key);
}
return builder;
}
}
}

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

@ -17,6 +17,8 @@ namespace Squidex.Domain.Apps.Entities
{
public static Q Empty => new Q();
public ClrQuery Query { get; init; } = new ClrQuery();
public IReadOnlyList<DomainId>? Ids { get; init; }
public DomainId Referencing { get; init; }
@ -29,9 +31,9 @@ namespace Squidex.Domain.Apps.Entities
public Query<IJsonValue>? JsonQuery { get; init; }
public ClrQuery Query { get; init; } = new ClrQuery();
public RefToken? CreatedBy { get; init; }
public RefToken? CreatedBy { get; set; }
public bool NoTotal { get; init; }
private Q()
{
@ -44,6 +46,11 @@ namespace Squidex.Domain.Apps.Entities
return this with { Query = query };
}
public Q WithoutTotal(bool value = true)
{
return this with { NoTotal = value };
}
public Q WithODataQuery(string? query)
{
return this with { ODataQuery = query };

2
backend/src/Squidex.Web/ApiPermissionAttribute.cs

@ -35,7 +35,7 @@ namespace Squidex.Web
{
if (permissionIds.Length > 0)
{
var permissions = context.HttpContext.Context().Permissions;
var permissions = context.HttpContext.Context().UserPermissions;
var hasPermission = false;

2
backend/src/Squidex.Web/ContextExtensions.cs

@ -18,7 +18,7 @@ namespace Squidex.Web
if (context == null)
{
context = RequestContext.Anonymous();
context = RequestContext.Anonymous(null);
httpContext.Features.Set(context);
}

2
backend/src/Squidex.Web/ContextProvider.cs

@ -25,7 +25,7 @@ namespace Squidex.Web
{
if (asyncLocal.Value == null)
{
asyncLocal.Value = Context.Anonymous();
asyncLocal.Value = Context.Anonymous(null!);
}
return asyncLocal.Value;

10
backend/src/Squidex.Web/Pipeline/AppResolver.cs

@ -123,15 +123,17 @@ namespace Squidex.Web.Pipeline
private static Context SetContext(HttpContext httpContext, IAppEntity app)
{
var requestContext = new Context(httpContext.User, app);
var requestContext =
new Context(httpContext.User, app).Clone(builder =>
{
foreach (var (key, value) in httpContext.Request.Headers)
{
if (key.StartsWith("X-", StringComparison.OrdinalIgnoreCase))
{
requestContext.Headers.Add(key, value.ToString());
builder.SetHeader(key, value.ToString());
}
}
});
httpContext.Features.Set(requestContext);
@ -140,7 +142,7 @@ namespace Squidex.Web.Pipeline
private static bool HasPermission(string appName, Context requestContext)
{
return requestContext.Permissions.Includes(Permissions.ForApp(Permissions.App, appName));
return requestContext.UserPermissions.Includes(Permissions.ForApp(Permissions.App, appName));
}
private static bool AllowAnonymous(ActionExecutingContext context)

4
backend/src/Squidex.Web/Resources.cs

@ -198,7 +198,7 @@ namespace Squidex.Web
public bool Includes(Permission permission, PermissionSet? additional = null)
{
return Context.Permissions.Includes(permission) || additional?.Includes(permission) == true;
return Context.UserPermissions.Includes(permission) || additional?.Includes(permission) == true;
}
public bool IsAllowedForSchema(string id, string schema)
@ -230,7 +230,7 @@ namespace Squidex.Web
var permission = P.ForApp(id, app, schema);
return Context.Permissions.Allows(permission) || additional?.Allows(permission) == true;
return Context.UserPermissions.Allows(permission) || additional?.Allows(permission) == true;
}
private string? GetAppName()

2
backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs

@ -76,7 +76,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
public async Task<IActionResult> GetApps()
{
var userOrClientId = HttpContext.User.UserOrClientId()!;
var userPermissions = Resources.Context.Permissions;
var userPermissions = Resources.Context.UserPermissions;
var apps = await appProvider.GetUserAppsAsync(userOrClientId, userPermissions);

21
backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs

@ -16,6 +16,7 @@ using Microsoft.Net.Http.Headers;
using Squidex.Areas.Api.Controllers.Assets.Models;
using Squidex.Assets;
using Squidex.Domain.Apps.Core.Assets;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
@ -73,14 +74,16 @@ namespace Squidex.Areas.Api.Controllers.Assets
[AllowAnonymous]
public async Task<IActionResult> GetAssetContentBySlug(string app, string idOrSlug, [FromQuery] AssetContentQueryDto queries, string? more = null)
{
var asset = await assetQuery.FindAsync(Context, DomainId.Create(idOrSlug));
var requestContext = Context.Clone(b => b.WithoutAssetEnrichment());
var asset = await assetQuery.FindAsync(requestContext, DomainId.Create(idOrSlug));
if (asset == null)
{
asset = await assetQuery.FindBySlugAsync(Context, idOrSlug);
asset = await assetQuery.FindBySlugAsync(requestContext, idOrSlug);
}
return await DeliverAssetAsync(asset, queries);
return await DeliverAssetAsync(requestContext, asset, queries);
}
/// <summary>
@ -101,12 +104,14 @@ namespace Squidex.Areas.Api.Controllers.Assets
[Obsolete("Use overload with app name")]
public async Task<IActionResult> GetAssetContent(DomainId id, [FromQuery] AssetContentQueryDto queries)
{
var asset = await assetQuery.FindGlobalAsync(Context, id);
var requestContext = Context.Clone(b => b.WithoutAssetEnrichment());
var asset = await assetQuery.FindGlobalAsync(requestContext, id);
return await DeliverAssetAsync(asset, queries);
return await DeliverAssetAsync(requestContext, asset, queries);
}
private async Task<IActionResult> DeliverAssetAsync(IAssetEntity? asset, AssetContentQueryDto queries)
private async Task<IActionResult> DeliverAssetAsync(Context context, IAssetEntity? asset, AssetContentQueryDto queries)
{
queries ??= new AssetContentQueryDto();
@ -124,9 +129,9 @@ namespace Squidex.Areas.Api.Controllers.Assets
if (asset != null && queries.Version > EtagVersion.Any && asset.Version != queries.Version)
{
if (Context.App != null)
if (context.App != null)
{
asset = await assetQuery.FindAsync(Context, asset.Id, queries.Version);
asset = await assetQuery.FindAsync(context, asset.Id, queries.Version);
}
else
{

2
backend/src/Squidex/Areas/Api/Controllers/UI/UIController.cs

@ -49,7 +49,7 @@ namespace Squidex.Areas.Api.Controllers.UI
{
var result = new UISettingsDto
{
CanCreateApps = !uiOptions.OnlyAdminsCanCreateApps || Context.Permissions.Includes(CreateAppPermission)
CanCreateApps = !uiOptions.OnlyAdminsCanCreateApps || Context.UserPermissions.Includes(CreateAppPermission)
};
return Ok(result);

2
backend/src/Squidex/Config/Domain/StoreServices.cs

@ -115,7 +115,7 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<MongoAssetFolderRepository>()
.As<IAssetFolderRepository>().As<ISnapshotStore<AssetFolderDomainObject.State, DomainId>>();
services.AddSingletonAs(c => ActivatorUtilities.CreateInstance<MongoContentRepository>(c, GetDatabase(c, mongoContentDatabaseName)))
services.AddSingletonAs(c => ActivatorUtilities.CreateInstance<MongoContentRepository>(c, GetDatabase(c, mongoContentDatabaseName), false))
.As<IContentRepository>().As<ISnapshotStore<ContentDomainObject.State, DomainId>>();
services.AddSingletonAs<MongoSchemasHash>()

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppCommandMiddlewareTests.cs

@ -26,7 +26,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject
private readonly IAppImageStore appImageStore = A.Fake<IAppImageStore>();
private readonly IAssetThumbnailGenerator assetThumbnailGenerator = A.Fake<IAssetThumbnailGenerator>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly Context requestContext = Context.Anonymous();
private readonly Context requestContext;
private readonly AppCommandMiddleware sut;
public sealed class MyCommand : SquidexCommand
@ -40,6 +40,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject
public AppCommandMiddlewareTests()
{
requestContext = Context.Anonymous(Mocks.App(appId));
A.CallTo(() => contextProvider.Context)
.Returns(requestContext);

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs

@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject
private readonly DomainId assetId = DomainId.NewGuid();
private readonly AssetDomainObjectGrain asset;
private readonly AssetFile file;
private readonly Context requestContext = Context.Anonymous();
private readonly Context requestContext;
private readonly AssetCommandMiddleware sut;
public sealed class MyCommand : SquidexCommand
@ -63,6 +63,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject
asset = new AssetDomainObjectGrain(serviceProvider, null!);
asset.ActivateAsync(Id.ToString()).Wait();
requestContext = Context.Anonymous(Mocks.App(AppNamedId));
A.CallTo(() => contextProvider.Context)
.Returns(requestContext);

7
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetEnricherTests.cs

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.Tags;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Caching;
using Xunit;
@ -23,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
private readonly IAssetMetadataSource assetMetadataSource1 = A.Fake<IAssetMetadataSource>();
private readonly IAssetMetadataSource assetMetadataSource2 = A.Fake<IAssetMetadataSource>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly Context requestContext = Context.Anonymous();
private readonly Context requestContext;
private readonly AssetEnricher sut;
public AssetEnricherTests()
@ -34,6 +35,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
assetMetadataSource2
};
requestContext = Context.Anonymous(Mocks.App(appId));
sut = new AssetEnricher(tagService, assetMetadataSources, requestCache);
}
@ -96,7 +99,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
AppId = appId
};
var result = await sut.EnrichAsync(source, requestContext.Clone().WithoutAssetEnrichment());
var result = await sut.EnrichAsync(source, requestContext.Clone(b => b.WithoutAssetEnrichment()));
Assert.Null(result.TagNames);
}

32
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryParserTests.cs

@ -34,12 +34,20 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
sut = new AssetQueryParser(TestUtils.DefaultSerializer, tagService, options);
}
[Fact]
public async Task Should_skip_total_when_set_in_context()
{
var q = await sut.ParseAsync(requestContext.Clone(b => b.WithoutTotal()), Q.Empty);
Assert.True(q.NoTotal);
}
[Fact]
public async Task Should_throw_if_odata_query_is_invalid()
{
var query = Q.Empty.WithODataQuery("$filter=invalid");
await Assert.ThrowsAsync<ValidationException>(() => sut.ParseQueryAsync(requestContext, query));
await Assert.ThrowsAsync<ValidationException>(() => sut.ParseAsync(requestContext, query));
}
[Fact]
@ -47,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
var query = Q.Empty.WithJsonQuery("invalid");
await Assert.ThrowsAsync<ValidationException>(() => sut.ParseQueryAsync(requestContext, query));
await Assert.ThrowsAsync<ValidationException>(() => sut.ParseAsync(requestContext, query));
}
[Fact]
@ -55,7 +63,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
var query = Q.Empty.WithODataQuery("$top=100&$orderby=fileName asc&$search=Hello World");
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("FullText: 'Hello World'; Take: 100; Sort: fileName Ascending, id Ascending", q.Query.ToString());
}
@ -65,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
var query = Q.Empty.WithODataQuery("$top=200&$filter=fileName eq 'ABC'");
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("Filter: fileName == 'ABC'; Take: 200; Sort: lastModified Descending, id Ascending", q.Query.ToString());
}
@ -75,7 +83,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
var query = Q.Empty.WithJsonQuery("{ \"filter\": { \"path\": \"fileName\", \"op\": \"eq\", \"value\": \"ABC\" } }");
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("Filter: fileName == 'ABC'; Take: 30; Sort: lastModified Descending, id Ascending", q.Query.ToString());
}
@ -85,7 +93,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
var query = Q.Empty.WithJsonQuery("{ \"fullText\": \"Hello\" }");
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("FullText: 'Hello'; Take: 30; Sort: lastModified Descending, id Ascending", q.Query.ToString());
}
@ -95,7 +103,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
var query = Q.Empty;
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("Take: 30; Sort: lastModified Descending, id Ascending", q.Query.ToString());
}
@ -105,7 +113,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
var query = Q.Empty.WithODataQuery("$top=300&$skip=20");
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("Skip: 20; Take: 200; Sort: lastModified Descending, id Ascending", q.Query.ToString());
}
@ -115,7 +123,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
var query = Q.Empty.WithODataQuery("$top=300&$skip=20&$orderby=id desc");
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("Skip: 20; Take: 200; Sort: id Descending", q.Query.ToString());
}
@ -128,7 +136,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
var query = Q.Empty.WithODataQuery("$filter=tags eq 'name1'");
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("Filter: tags == 'id1'; Take: 30; Sort: lastModified Descending, id Ascending", q.Query.ToString());
}
@ -141,7 +149,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
var query = Q.Empty.WithODataQuery("$filter=tags eq 'name1'");
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("Filter: tags == 'name1'; Take: 30; Sort: lastModified Descending, id Ascending", q.Query.ToString());
}
@ -151,7 +159,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
var query = Q.Empty.WithODataQuery("$filter=fileSize eq 123");
var q = await sut.ParseQueryAsync(requestContext, query);
var q = await sut.ParseAsync(requestContext, query);
Assert.Equal("Filter: fileSize == 123; Take: 30; Sort: lastModified Descending, id Ascending", q.Query.ToString());

2
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryServiceTests.cs

@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
SetupEnricher();
A.CallTo(() => queryParser.ParseQueryAsync(requestContext, A<Q>._))
A.CallTo(() => queryParser.ParseAsync(requestContext, A<Q>._))
.ReturnsLazily(c => Task.FromResult(c.GetArgument<Q>(1)!));
sut = new AssetQueryService(assetEnricher, assetRepository, assetLoader, assetFolderRepository, queryParser);

21
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/BulkUpdateCommandMiddlewareTests.cs

@ -81,7 +81,12 @@ namespace Squidex.Domain.Apps.Entities.Contents
var (id, _, query) = CreateTestData(true);
A.CallTo(() => contentQuery.QueryAsync(requestContext, A<string>._, A<Q>.That.Matches(x => x.JsonQuery == query)))
A.CallTo(() => contentQuery.QueryAsync(
A<Context>.That.Matches(x =>
x.ShouldSkipCleanup() &&
x.ShouldSkipContentEnrichment() &&
x.ShouldSkipTotal()),
schemaId.Name, A<Q>.That.Matches(x => x.JsonQuery == query)))
.Returns(ResultList.CreateFrom(2, CreateContent(id), CreateContent(id)));
var command = BulkCommand(BulkUpdateType.ChangeStatus, query);
@ -101,7 +106,12 @@ namespace Squidex.Domain.Apps.Entities.Contents
var (id, data, query) = CreateTestData(true);
A.CallTo(() => contentQuery.QueryAsync(requestContext, A<string>._, A<Q>.That.Matches(x => x.JsonQuery == query)))
A.CallTo(() => contentQuery.QueryAsync(
A<Context>.That.Matches(x =>
x.ShouldSkipCleanup() &&
x.ShouldSkipContentEnrichment() &&
x.ShouldSkipTotal()),
schemaId.Name, A<Q>.That.Matches(x => x.JsonQuery == query)))
.Returns(ResultList.CreateFrom(1, CreateContent(id)));
var command = BulkCommand(BulkUpdateType.Upsert, query: query, data: data);
@ -125,7 +135,12 @@ namespace Squidex.Domain.Apps.Entities.Contents
var id1 = DomainId.NewGuid();
var id2 = DomainId.NewGuid();
A.CallTo(() => contentQuery.QueryAsync(requestContext, A<string>._, A<Q>.That.Matches(x => x.JsonQuery == query)))
A.CallTo(() => contentQuery.QueryAsync(
A<Context>.That.Matches(x =>
x.ShouldSkipCleanup() &&
x.ShouldSkipContentEnrichment() &&
x.ShouldSkipTotal()),
schemaId.Name, A<Q>.That.Matches(x => x.JsonQuery == query)))
.Returns(ResultList.CreateFrom(2,
CreateContent(id1),
CreateContent(id2)));

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentCommandMiddlewareTests.cs

@ -21,7 +21,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
private readonly IContentEnricher contentEnricher = A.Fake<IContentEnricher>();
private readonly IContextProvider contextProvider = A.Fake<IContextProvider>();
private readonly DomainId contentId = DomainId.NewGuid();
private readonly Context requestContext = Context.Anonymous();
private readonly Context requestContext;
private readonly ContentCommandMiddleware sut;
public sealed class MyCommand : SquidexCommand
@ -35,6 +35,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
public ContentCommandMiddlewareTests()
{
requestContext = Context.Anonymous(Mocks.App(AppNamedId));
A.CallTo(() => contextProvider.Context)
.Returns(requestContext);

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

@ -47,7 +47,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var asset = TestAsset.Create(appId, DomainId.NewGuid());
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasOData("?$top=30&$skip=5&$filter=my-query")))
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null,
A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5&$filter=my-query" && x.NoTotal == true)))
.Returns(ResultList.CreateFrom(0, asset));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -81,7 +82,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var asset = TestAsset.Create(appId, DomainId.NewGuid());
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasOData("?$top=30&$skip=5&$filter=my-query")))
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null,
A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5&$filter=my-query" && x.NoTotal == false)))
.Returns(ResultList.CreateFrom(10, asset));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -116,7 +118,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", assetId.ToString());
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasIds(assetId)))
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasIdsWithoutTotal(assetId)))
.Returns(ResultList.CreateFrom<IEnrichedAssetEntity>(1));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -145,7 +147,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", assetId.ToString()).Replace("<FIELDS>", TestAsset.AllFields);
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasIds(assetId)))
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasIdsWithoutTotal(assetId)))
.Returns(ResultList.CreateFrom(1, asset));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -196,7 +198,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var content = TestContent.Create(appId, schemaId, DomainId.NewGuid(), DomainId.Empty, DomainId.Empty);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.Id.ToString(), A<Q>.That.HasOData("?$top=30&$skip=5")))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.Id.ToString(),
A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5" && x.NoTotal == true)))
.Returns(ResultList.CreateFrom(0, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -274,7 +277,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var content = TestContent.Create(appId, schemaId, DomainId.NewGuid(), DomainId.Empty, DomainId.Empty);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.Id.ToString(), A<Q>.That.HasOData("?$top=30&$skip=5")))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.Id.ToString(),
A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5" && x.NoTotal == true)))
.Returns(ResultList.CreateFrom(0, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -308,7 +312,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var content = TestContent.Create(appId, schemaId, DomainId.NewGuid(), DomainId.Empty, DomainId.Empty);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.Id.ToString(), A<Q>.That.HasOData("?$top=30&$skip=5")))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.Id.ToString(),
A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5" && x.NoTotal == false)))
.Returns(ResultList.CreateFrom(10, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -343,7 +348,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", contentId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIds(contentId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentId)))
.Returns(ResultList.CreateFrom<IEnrichedContentEntity>(1));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -372,7 +377,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<FIELDS>", TestContent.AllFields).Replace("<ID>", contentId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIds(contentId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentId)))
.Returns(ResultList.CreateFrom(1, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -445,10 +450,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", contentId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>._))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentRefId)))
.Returns(ResultList.CreateFrom(0, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIds(contentId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentId)))
.Returns(ResultList.CreateFrom(1, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -511,10 +516,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", contentRefId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIds(contentRefId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentRefId)))
.Returns(ResultList.CreateFrom(1, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), content.SchemaId.Id.ToString(), A<Q>.That.HasOData("?$top=30&$skip=5", contentRefId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), content.SchemaId.Id.ToString(),
A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5" && x.Reference == contentRefId && x.NoTotal == true)))
.Returns(ResultList.CreateFrom(1, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -574,10 +580,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", contentRefId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIds(contentRefId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentRefId)))
.Returns(ResultList.CreateFrom(1, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), content.SchemaId.Id.ToString(), A<Q>.That.HasOData("?$top=30&$skip=5", contentRefId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), content.SchemaId.Id.ToString(),
A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5" && x.Reference == contentRefId && x.NoTotal == false)))
.Returns(ResultList.CreateFrom(1, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -647,10 +654,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", contentId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>._))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentRefId)))
.Returns(ResultList.CreateFrom(0, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIds(contentId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentId)))
.Returns(ResultList.CreateFrom(1, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -713,10 +720,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", contentId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIds(contentId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentId)))
.Returns(ResultList.CreateFrom(1, content));
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>._))
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasIdsWithoutTotal(assetRefId)))
.Returns(ResultList.CreateFrom(0, assetRef));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -769,10 +776,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", assetId2.ToString());
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasIds(assetId1)))
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasIdsWithoutTotal(assetId1)))
.Returns(ResultList.CreateFrom(0, asset1));
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasIds(assetId2)))
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A<Q>.That.HasIdsWithoutTotal(assetId2)))
.Returns(ResultList.CreateFrom(0, asset2));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query1 }, new GraphQLQuery { Query = query2 });
@ -828,7 +835,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}
}".Replace("<ID>", contentId.ToString());
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIds(contentId)))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.HasIdsWithoutTotal(contentId)))
.Returns(ResultList.CreateFrom(1, content));
var result = await sut.QueryAsync(requestContext, new GraphQLQuery { Query = query });
@ -840,12 +847,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
private Context MatchsAssetContext()
{
return A<Context>.That.Matches(x => x.App == app && x.User == requestContext.User);
return A<Context>.That.Matches(x =>
x.App == app &&
x.ShouldSkipCleanup() &&
x.ShouldSkipContentEnrichment() &&
x.User == requestContext.User);
}
private Context MatchsContentContext()
{
return A<Context>.That.Matches(x => x.App == app && x.User == requestContext.User);
return A<Context>.That.Matches(x =>
x.App == app &&
x.ShouldSkipCleanup() &&
x.ShouldSkipContentEnrichment() &&
x.User == requestContext.User);
}
}
}

24
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs

@ -33,6 +33,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
private readonly int numValues = 10000;
private readonly IMongoClient mongoClient = new MongoClient("mongodb://localhost");
private readonly IMongoDatabase mongoDatabase;
private readonly IMongoDatabase mongoDatabaseWildcard;
public MongoContentRepository ContentRepository { get; }
@ -54,20 +55,30 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
public ContentsQueryFixture()
{
mongoDatabase = mongoClient.GetDatabase("QueryTests");
mongoDatabaseWildcard = mongoClient.GetDatabase("QueryTestsWildcard");
SetupJson();
var contentRepository =
var appProvider = CreateAppProvider();
ContentRepository =
new MongoContentRepository(
mongoDatabase,
CreateAppProvider());
appProvider, false);
Task.Run(async () =>
{
await Task.WhenAll(
SetupAsync(ContentRepository, mongoDatabase));
}).Wait();
}
private async Task SetupAsync(MongoContentRepository contentRepository, IMongoDatabase database)
{
await contentRepository.InitializeAsync();
await mongoDatabase.RunCommandAsync<BsonDocument>("{ profile : 0 }");
await mongoDatabase.DropCollectionAsync("system.profile");
await database.RunCommandAsync<BsonDocument>("{ profile : 0 }");
await database.DropCollectionAsync("system.profile");
var collections = contentRepository.GetInternalCollections();
@ -130,10 +141,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
}
}
await mongoDatabase.RunCommandAsync<BsonDocument>("{ profile : 2 }");
}).Wait();
ContentRepository = contentRepository;
await database.RunCommandAsync<BsonDocument>("{ profile : 2 }");
}
private static IAppProvider CreateAppProvider()

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

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NodaTime;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Queries;
@ -29,18 +30,25 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
_ = fixture;
}
[Fact]
public async Task Should_verify_ids()
public IEnumerable<object[]> Collections()
{
yield return new[] { _.ContentRepository };
}
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_verify_ids(IContentRepository repository)
{
var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet();
var contents = await _.ContentRepository.QueryIdsAsync(_.RandomAppId(), ids, SearchScope.Published);
var contents = await repository.QueryIdsAsync(_.RandomAppId(), ids, SearchScope.Published);
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_by_ids()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_by_ids(IContentRepository repository)
{
var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet();
@ -49,33 +57,36 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
_.RandomSchema()
};
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), schemas, Q.Empty.WithIds(ids), SearchScope.All);
var contents = await repository.QueryAsync(_.RandomApp(), schemas, Q.Empty.WithIds(ids), SearchScope.All);
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_by_ids_and_schema()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_by_ids_and_schema(IContentRepository repository)
{
var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet();
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), Q.Empty.WithIds(ids), SearchScope.All);
var contents = await repository.QueryAsync(_.RandomApp(), _.RandomSchema(), Q.Empty.WithIds(ids), SearchScope.All);
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_ids_by_filter()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_ids_by_filter(IContentRepository repository)
{
var filter = F.Eq("data.value.iv", 12);
var contents = await _.ContentRepository.QueryIdsAsync(_.RandomAppId(), _.RandomSchemaId(), filter);
var contents = await repository.QueryIdsAsync(_.RandomAppId(), _.RandomSchemaId(), filter);
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_by_filter()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_by_filter(IContentRepository repository)
{
var query = new ClrQuery
{
@ -86,41 +97,45 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
Filter = F.Eq("data.value.iv", 12)
};
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), Q.Empty.WithQuery(query), SearchScope.Published);
var contents = await repository.QueryAsync(_.RandomApp(), _.RandomSchema(), Q.Empty.WithQuery(query), SearchScope.Published);
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_scheduled()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_scheduled(IContentRepository repository)
{
var time = SystemClock.Instance.GetCurrentInstant();
await _.ContentRepository.QueryScheduledWithoutDataAsync(time, _ => Task.CompletedTask);
await repository.QueryScheduledWithoutDataAsync(time, _ => Task.CompletedTask);
}
[Fact]
public async Task Should_query_contents_with_default_query()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_with_default_query(IContentRepository repository)
{
var query = new ClrQuery();
var contents = await QueryAsync(query);
var contents = await QueryAsync(repository, query);
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_default_query_and_id()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_with_default_query_and_id(IContentRepository repository)
{
var query = new ClrQuery();
var contents = await QueryAsync(query, reference: DomainId.NewGuid());
var contents = await QueryAsync(repository, query, reference: DomainId.NewGuid());
Assert.Empty(contents);
}
[Fact]
public async Task Should_query_contents_with_large_skip()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_with_large_skip(IContentRepository repository)
{
var query = new ClrQuery
{
@ -130,51 +145,58 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
}
};
var contents = await QueryAsync(query, 1000, 9000);
var contents = await QueryAsync(repository, query, 1000, 9000);
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_query_fulltext()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_with_query_fulltext(IContentRepository repository)
{
var query = new ClrQuery
{
FullText = "hello"
};
var contents = await QueryAsync(query);
var contents = await QueryAsync(repository, query);
Assert.NotNull(contents);
}
[Fact]
public async Task Should_query_contents_with_query_filter()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_with_query_filter(IContentRepository repository)
{
var query = new ClrQuery
{
Filter = F.Eq("data.value.iv", 200)
};
var contents = await QueryAsync(query, 1000, 0);
var contents = await QueryAsync(repository, query, 1000, 0);
Assert.NotEmpty(contents);
}
[Fact]
public async Task Should_query_contents_with_query_filter_and_id()
[Theory]
[MemberData(nameof(Collections))]
public async Task Should_query_contents_with_query_filter_and_id(IContentRepository repository)
{
var query = new ClrQuery
{
Filter = F.Eq("data.value.iv", 12)
};
var contents = await QueryAsync(query, 1000, 0, reference: DomainId.NewGuid());
var contents = await QueryAsync(repository, query, 1000, 0, reference: DomainId.NewGuid());
Assert.Empty(contents);
}
private async Task<IResultList<IContentEntity>> QueryAsync(ClrQuery clrQuery, int take = 1000, int skip = 100, DomainId reference = default)
private async Task<IResultList<IContentEntity>> QueryAsync(IContentRepository repository,
ClrQuery clrQuery,
int take = 1000,
int skip = 100,
DomainId reference = default)
{
if (clrQuery.Take == long.MaxValue)
{
@ -199,7 +221,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
.WithQuery(clrQuery)
.WithReference(reference);
var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), q, SearchScope.All);
var contents = await repository.QueryAsync(_.RandomApp(), _.RandomSchema(), q, SearchScope.All);
return contents;
}

8
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryParserTests.cs

@ -51,6 +51,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
sut = new ContentQueryParser(cache, TestUtils.DefaultSerializer, textIndex, options);
}
[Fact]
public async Task Should_skip_total_when_set_in_context()
{
var q = await sut.ParseAsync(requestContext.Clone(b => b.WithoutTotal()), Q.Empty);
Assert.True(q.NoTotal);
}
[Fact]
public async Task Should_throw_if_odata_query_is_invalid()
{

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

@ -303,7 +303,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
claimsIdentity.AddClaim(new Claim(SquidexClaimTypes.Permissions, concretePermission));
}
return new Context(claimsPrincipal, Mocks.App(appId)).WithUnpublished(isUnpublished == 1);
return new Context(claimsPrincipal, Mocks.App(appId)).Clone(b => b.WithUnpublished(isUnpublished == 1));
}
private static void AssertContent(IContentEntity source, IEnrichedContentEntity? result)

8
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichWithWorkflowsTests.cs

@ -123,7 +123,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
A.CallTo(() => contentWorkflow.GetInfoAsync(content, content.Status))
.Returns(Task.FromResult<StatusInfo>(null!));
var ctx = requestContext.WithResolveFlow(true);
var ctx = requestContext.Clone(b => b.WithResolveFlow(false));
await sut.EnrichAsync(ctx, new[] { content }, null!);
@ -138,7 +138,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
A.CallTo(() => contentWorkflow.CanUpdateAsync(content, content.Status, requestContext.User))
.Returns(true);
var ctx = requestContext.WithResolveFlow(true);
var ctx = requestContext.Clone(b => b.WithResolveFlow(false));
await sut.EnrichAsync(ctx, new[] { content }, null!);
@ -148,11 +148,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
[Fact]
public async Task Should_not_enrich_content_with_can_update_if_disabled_in_context()
{
requestContext.WithResolveFlow(false);
var content = new ContentEntity { SchemaId = schemaId };
var ctx = new Context(Mocks.ApiUser(), Mocks.App(appId)).WithResolveFlow(false);
var ctx = new Context(Mocks.ApiUser(), Mocks.App(appId)).Clone(b => b.WithResolveFlow(false));
await sut.EnrichAsync(ctx, new[] { content }, null!);

11
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs

@ -88,7 +88,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
new[] { doc2.Id })
};
A.CallTo(() => assetQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichAsset()), null, A<Q>.That.HasIds(doc1.Id, doc2.Id)))
A.CallTo(() => assetQuery.QueryAsync(
A<Context>.That.Matches(x => x.ShouldSkipAssetEnrichment() && x.ShouldSkipTotal()), null, A<Q>.That.HasIds(doc1.Id, doc2.Id)))
.Returns(ResultList.CreateFrom(4, doc1, doc2));
await sut.EnrichAsync(requestContext, contents, schemaProvider);
@ -119,7 +120,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
new[] { doc2.Id, doc1.Id })
};
A.CallTo(() => assetQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichAsset()), null, A<Q>.That.HasIds(doc1.Id, doc2.Id, img1.Id, img2.Id)))
A.CallTo(() => assetQuery.QueryAsync(
A<Context>.That.Matches(x => x.ShouldSkipAssetEnrichment() && x.ShouldSkipTotal()), null, A<Q>.That.HasIds(doc1.Id, doc2.Id, img1.Id, img2.Id)))
.Returns(ResultList.CreateFrom(4, img1, img2, doc1, doc2));
await sut.EnrichAsync(requestContext, contents, schemaProvider);
@ -171,7 +173,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
CreateContent(new[] { DomainId.NewGuid() }, Array.Empty<DomainId>())
};
var ctx = new Context(Mocks.FrontendUser(), Mocks.App(appId)).WithoutContentEnrichment(true);
var ctx = new Context(Mocks.FrontendUser(), Mocks.App(appId)).Clone(b => b.WithoutContentEnrichment(true));
await sut.EnrichAsync(ctx, contents, schemaProvider);
@ -212,7 +214,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
Assert.NotNull(contents[0].ReferenceData);
A.CallTo(() => assetQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichAsset()), null, A<Q>.That.HasIds(id1)))
A.CallTo(() => assetQuery.QueryAsync(
A<Context>.That.Matches(x => x.ShouldSkipAssetEnrichment() && x.ShouldSkipTotal()), null, A<Q>.That.HasIds(id1)))
.MustHaveHappened();
}

13
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs

@ -88,7 +88,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
}
[Fact]
public async Task Should_add_referenced_id_and__as_dependency()
public async Task Should_add_referenced_id_and_as_dependency()
{
var ref1_1 = CreateRefContent(DomainId.NewGuid(), 1, "ref1_1", 13, refSchemaId1);
var ref1_2 = CreateRefContent(DomainId.NewGuid(), 2, "ref1_2", 17, refSchemaId1);
@ -101,7 +101,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
CreateContent(new[] { ref1_2.Id }, new[] { ref2_2.Id })
};
A.CallTo(() => contentQuery.QueryAsync(A<Context>._, A<Q>.That.HasIds(ref1_1.Id, ref1_2.Id, ref2_1.Id, ref2_2.Id)))
A.CallTo(() => contentQuery.QueryAsync(
A<Context>.That.Matches(x => x.ShouldSkipContentEnrichment() && x.ShouldSkipTotal()), A<Q>.That.HasIds(ref1_1.Id, ref1_2.Id, ref2_1.Id, ref2_2.Id)))
.Returns(ResultList.CreateFrom(4, ref1_1, ref1_2, ref2_1, ref2_2));
await sut.EnrichAsync(requestContext, contents, schemaProvider);
@ -139,7 +140,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
CreateContent(new[] { ref1_2.Id }, new[] { ref2_2.Id })
};
A.CallTo(() => contentQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichContent()), A<Q>.That.HasIds(ref1_1.Id, ref1_2.Id, ref2_1.Id, ref2_2.Id)))
A.CallTo(() => contentQuery.QueryAsync(
A<Context>.That.Matches(x => x.ShouldSkipContentEnrichment() && x.ShouldSkipTotal()), A<Q>.That.HasIds(ref1_1.Id, ref1_2.Id, ref2_1.Id, ref2_2.Id)))
.Returns(ResultList.CreateFrom(4, ref1_1, ref1_2, ref2_1, ref2_2));
await sut.EnrichAsync(requestContext, contents, schemaProvider);
@ -191,7 +193,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
CreateContent(new[] { ref1_2.Id }, new[] { ref2_1.Id, ref2_2.Id })
};
A.CallTo(() => contentQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichContent()), A<Q>.That.HasIds(ref1_1.Id, ref1_2.Id, ref2_1.Id, ref2_2.Id)))
A.CallTo(() => contentQuery.QueryAsync(
A<Context>.That.Matches(x => x.ShouldSkipContentEnrichment() && x.ShouldSkipTotal()), A<Q>.That.HasIds(ref1_1.Id, ref1_2.Id, ref2_1.Id, ref2_2.Id)))
.Returns(ResultList.CreateFrom(4, ref1_1, ref1_2, ref2_1, ref2_2));
await sut.EnrichAsync(requestContext, contents, schemaProvider);
@ -255,7 +258,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
CreateContent(new[] { DomainId.NewGuid() }, Array.Empty<DomainId>())
};
var ctx = new Context(Mocks.FrontendUser(), Mocks.App(appId)).WithoutContentEnrichment(true);
var ctx = new Context(Mocks.FrontendUser(), Mocks.App(appId)).Clone(b => b.WithoutContentEnrichment(true));
await sut.EnrichAsync(ctx, contents, schemaProvider);

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs

@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject
private readonly IRuleEnricher ruleEnricher = A.Fake<IRuleEnricher>();
private readonly IContextProvider contextProvider = A.Fake<IContextProvider>();
private readonly DomainId ruleId = DomainId.NewGuid();
private readonly Context requestContext = Context.Anonymous();
private readonly Context requestContext;
private readonly RuleCommandMiddleware sut;
public sealed class MyCommand : SquidexCommand
@ -34,6 +34,8 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject
public RuleCommandMiddlewareTests()
{
requestContext = Context.Anonymous(Mocks.App(AppNamedId));
A.CallTo(() => contextProvider.Context)
.Returns(requestContext);

5
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleEnricherTests.cs

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using FakeItEasy;
using NodaTime;
using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Caching;
using Xunit;
@ -21,11 +22,13 @@ namespace Squidex.Domain.Apps.Entities.Rules.Queries
private readonly IRuleEventRepository ruleEventRepository = A.Fake<IRuleEventRepository>();
private readonly IRequestCache requestCache = A.Fake<IRequestCache>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly Context requestContext = Context.Anonymous();
private readonly Context requestContext;
private readonly RuleEnricher sut;
public RuleEnricherTests()
{
requestContext = Context.Anonymous(Mocks.App(appId));
sut = new RuleEnricher(ruleEventRepository, requestCache);
}

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleQueryServiceTests.cs

@ -20,12 +20,12 @@ namespace Squidex.Domain.Apps.Entities.Rules.Queries
private readonly IRulesIndex rulesIndex = A.Fake<IRulesIndex>();
private readonly IRuleEnricher ruleEnricher = A.Fake<IRuleEnricher>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly Context requestContext = Context.Anonymous();
private readonly Context requestContext;
private readonly RuleQueryService sut;
public RuleQueryServiceTests()
{
requestContext.App = Mocks.App(appId);
requestContext = Context.Anonymous(Mocks.App(appId));
sut = new RuleQueryService(rulesIndex, ruleEnricher);
}

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Search/SearchManagerTests.cs

@ -9,6 +9,8 @@ using System;
using System.Threading.Tasks;
using FakeItEasy;
using FluentAssertions;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Log;
using Xunit;
@ -19,7 +21,7 @@ namespace Squidex.Domain.Apps.Entities.Search
private readonly ISearchSource source1 = A.Fake<ISearchSource>();
private readonly ISearchSource source2 = A.Fake<ISearchSource>();
private readonly ISemanticLog log = A.Fake<ISemanticLog>();
private readonly Context requestContext = Context.Anonymous();
private readonly Context requestContext = Context.Anonymous(Mocks.App(NamedId.Of(DomainId.NewGuid(), "my-app")));
private readonly SearchManager sut;
public SearchManagerTests()

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

@ -20,14 +20,9 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
return that.Matches(x => x.Query!.ToString() == query);
}
public static Q HasOData(this INegatableArgumentConstraintManager<Q> that, string odata)
public static Q HasIdsWithoutTotal(this INegatableArgumentConstraintManager<Q> that, params DomainId[] ids)
{
return that.HasOData(odata, default);
}
public static Q HasOData(this INegatableArgumentConstraintManager<Q> that, string odata, DomainId reference = default)
{
return that.Matches(x => x.ODataQuery == odata && x.Reference == reference);
return that.Matches(x => x.Ids != null && x.Ids.SetEquals(ids) && x.NoTotal == true);
}
public static Q HasIds(this INegatableArgumentConstraintManager<Q> that, params DomainId[] ids)

4
backend/tests/Squidex.Web.Tests/ApiPermissionAttributeTests.cs

@ -141,7 +141,9 @@ namespace Squidex.Web
private void SetContext()
{
actionExecutingContext.HttpContext.Features.Set(new Context(new ClaimsPrincipal(actionExecutingContext.HttpContext.User)));
var context = new Context(new ClaimsPrincipal(actionExecutingContext.HttpContext.User), null!);
actionExecutingContext.HttpContext.Features.Set(context);
}
}
}

15
backend/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs

@ -11,6 +11,7 @@ using FakeItEasy;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Xunit;
@ -22,28 +23,24 @@ namespace Squidex.Web.CommandMiddlewares
private readonly IContextProvider contextProvider = A.Fake<IContextProvider>();
private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly Context requestContext = Context.Anonymous();
private readonly Context requestContext;
private readonly EnrichWithAppIdCommandMiddleware sut;
public EnrichWithAppIdCommandMiddlewareTests()
{
requestContext = Context.Anonymous(Mocks.App(appId));
A.CallTo(() => contextProvider.Context)
.Returns(requestContext);
var app = A.Fake<IAppEntity>();
A.CallTo(() => app.Id).Returns(appId.Id);
A.CallTo(() => app.Name).Returns(appId.Name);
requestContext.App = app;
sut = new EnrichWithAppIdCommandMiddleware(contextProvider);
}
[Fact]
public async Task Should_throw_exception_if_app_not_found()
{
requestContext.App = null!;
A.CallTo(() => contextProvider.Context)
.Returns(Context.Anonymous(null!));
var command = new CreateContent();
var context = Ctx(command);

3
backend/tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs

@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Plans;
using Xunit;
@ -109,7 +110,7 @@ namespace Squidex.Web.Pipeline
private void SetupApp()
{
httpContext.Context().App = appEntity;
httpContext.Features.Set(Context.Anonymous(appEntity));
}
}
}

2
backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj

@ -7,7 +7,7 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Squidex.Domain.Users\Squidex.Domain.Users.csproj" /> <ProjectReference Include="..\..\src\Squidex.Web\Squidex.Web.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Users\Squidex.Domain.Users.csproj" /> <ProjectReference Include="..\..\src\Squidex.Web\Squidex.Web.csproj" /> <ProjectReference Include="..\Squidex.Domain.Apps.Entities.Tests\Squidex.Domain.Apps.Entities.Tests.csproj" />
</ItemGroup>
<ItemGroup>

Loading…
Cancel
Save