Browse Source

Cache fix.

pull/636/head
Sebastian 5 years ago
parent
commit
1b06ae6ddd
  1. 2
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByQuery.cs
  2. 12
      backend/src/Squidex.Domain.Apps.Entities/AppProvider.cs
  3. 24
      backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs
  4. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/DefaultWorkflowsValidator.cs
  5. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs
  6. 2
      backend/src/Squidex.Domain.Apps.Entities/IAppProvider.cs
  7. 2
      backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/Guards/RuleTriggerValidator.cs
  8. 19
      backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs
  9. 2
      backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
  10. 2
      backend/src/Squidex.Web/Pipeline/SchemaResolver.cs
  11. 2
      backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs
  12. 2
      backend/src/Squidex/Squidex.csproj
  13. 138
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs
  14. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultWorkflowsValidatorTests.cs
  15. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs
  16. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs
  17. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs
  18. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/Triggers/ContentChangedTriggerTests.cs
  19. 28
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs
  20. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj
  21. 6
      backend/tests/Squidex.Web.Tests/Pipeline/SchemaResolverTests.cs

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

@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
try try
{ {
var schema = await appProvider.GetSchemaAsync(appId, schemaId, false); var schema = await appProvider.GetSchemaAsync(appId, schemaId);
if (schema == null) if (schema == null)
{ {

12
backend/src/Squidex.Domain.Apps.Entities/AppProvider.cs

@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Entities
return (null, null); return (null, null);
} }
var schema = await GetSchemaAsync(appId, id, false, canCache); var schema = await GetSchemaAsync(appId, id, canCache);
if (schema == null) if (schema == null)
{ {
@ -71,7 +71,7 @@ namespace Squidex.Domain.Apps.Entities
localCache.Add(AppCacheKey(app.Id), app); localCache.Add(AppCacheKey(app.Id), app);
} }
return app?.IsArchived == true ? null : app; return app;
} }
public async Task<IAppEntity?> GetAppAsync(string appName, bool canCache = false) public async Task<IAppEntity?> GetAppAsync(string appName, bool canCache = false)
@ -86,7 +86,7 @@ namespace Squidex.Domain.Apps.Entities
localCache.Add(AppCacheKey(app.Id), app); localCache.Add(AppCacheKey(app.Id), app);
} }
return app?.IsArchived == true ? null : app; return app;
} }
public async Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, string name, bool canCache = false) public async Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, string name, bool canCache = false)
@ -101,10 +101,10 @@ namespace Squidex.Domain.Apps.Entities
localCache.Add(SchemaCacheKey(appId, schema.Id), schema); localCache.Add(SchemaCacheKey(appId, schema.Id), schema);
} }
return schema?.IsDeleted == true ? null : schema; return schema;
} }
public async Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, DomainId id, bool allowDeleted = false, bool canCache = false) public async Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, DomainId id, bool canCache = false)
{ {
var schema = await localCache.GetOrCreateAsync(SchemaCacheKey(appId, id), () => var schema = await localCache.GetOrCreateAsync(SchemaCacheKey(appId, id), () =>
{ {
@ -116,7 +116,7 @@ namespace Squidex.Domain.Apps.Entities
localCache.Add(SchemaCacheKey(appId, schema.Id), schema); localCache.Add(SchemaCacheKey(appId, schema.Id), schema);
} }
return schema?.IsDeleted == true && !allowDeleted ? null : schema; return schema;
} }
public async Task<List<IAppEntity>> GetUserAppsAsync(string userId, PermissionSet permissions) public async Task<List<IAppEntity>> GetUserAppsAsync(string userId, PermissionSet permissions)

24
backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs

@ -149,7 +149,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
if (app != null) if (app != null)
{ {
await CacheItAsync(app, false); await CacheItAsync(app);
} }
return app; return app;
@ -232,7 +232,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
if (app != null) if (app != null)
{ {
await CacheItAsync(app, true); await InvalidateItAsync(app);
switch (context.Command) switch (context.Command)
{ {
@ -290,6 +290,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
{ {
await Index(contributorId).RemoveAsync(app.Id); await Index(contributorId).RemoveAsync(app.Id);
} }
if (app.CreatedBy.IsClient || !app.Contributors.ContainsKey(app.CreatedBy.Identifier))
{
await Index(app.CreatedBy.Identifier).RemoveAsync(app.Id);
}
} }
private IAppsByNameIndexGrain Index() private IAppsByNameIndexGrain Index()
@ -306,7 +311,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
{ {
var app = (await grainFactory.GetGrain<IAppGrain>(id.ToString()).GetStateAsync()).Value; var app = (await grainFactory.GetGrain<IAppGrain>(id.ToString()).GetStateAsync()).Value;
if (app.Version <= EtagVersion.Empty) if (app.Version <= EtagVersion.Empty || app.IsArchived)
{ {
return null; return null;
} }
@ -324,11 +329,18 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
return $"{typeof(AppsIndex)}_Apps_Name_{name}"; return $"{typeof(AppsIndex)}_Apps_Name_{name}";
} }
private Task CacheItAsync(IAppEntity app, bool publish) private Task InvalidateItAsync(IAppEntity app)
{
return grainCache.RemoveAsync(
GetCacheKey(app.Id),
GetCacheKey(app.Name));
}
private Task CacheItAsync(IAppEntity app)
{ {
return Task.WhenAll( return Task.WhenAll(
grainCache.AddAsync(GetCacheKey(app.Id), app, CacheDuration, publish), grainCache.AddAsync(GetCacheKey(app.Id), app, CacheDuration),
grainCache.AddAsync(GetCacheKey(app.Name), app, CacheDuration, publish)); grainCache.AddAsync(GetCacheKey(app.Name), app, CacheDuration));
} }
} }
} }

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

@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
if (workflows.Values.Count(x => x.SchemaIds.Contains(schemaId)) > 1) if (workflows.Values.Count(x => x.SchemaIds.Contains(schemaId)) > 1)
{ {
var schema = await appProvider.GetSchemaAsync(appId, schemaId, false); var schema = await appProvider.GetSchemaAsync(appId, schemaId);
if (schema != null) if (schema != null)
{ {

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

@ -191,7 +191,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
{ {
var schemaId = DomainId.Create(guid); var schemaId = DomainId.Create(guid);
schema = await appProvider.GetSchemaAsync(context.App.Id, schemaId, false, canCache); schema = await appProvider.GetSchemaAsync(context.App.Id, schemaId, canCache);
} }
if (schema == null) if (schema == null)

2
backend/src/Squidex.Domain.Apps.Entities/IAppProvider.cs

@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities
Task<List<IAppEntity>> GetUserAppsAsync(string userId, PermissionSet permissions); Task<List<IAppEntity>> GetUserAppsAsync(string userId, PermissionSet permissions);
Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, DomainId id, bool allowDeleted, bool canCache = false); Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, DomainId id, bool canCache = false);
Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, string name, bool canCache = false); Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, string name, bool canCache = false);

2
backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/Guards/RuleTriggerValidator.cs

@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards
Guard.NotNull(action, nameof(action)); Guard.NotNull(action, nameof(action));
Guard.NotNull(appProvider, nameof(appProvider)); Guard.NotNull(appProvider, nameof(appProvider));
var visitor = new RuleTriggerValidator(x => appProvider.GetSchemaAsync(appId, x, false)); var visitor = new RuleTriggerValidator(x => appProvider.GetSchemaAsync(appId, x));
return action.Accept(visitor); return action.Accept(visitor);
} }

19
backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs

@ -99,7 +99,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
if (schema != null) if (schema != null)
{ {
await CacheItAsync(schema, false); await CacheItAsync(schema);
} }
return schema; return schema;
@ -164,7 +164,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
if (schema != null) if (schema != null)
{ {
await CacheItAsync(schema, true); await InvalidateItAsync(schema);
if (context.Command is DeleteSchema) if (context.Command is DeleteSchema)
{ {
@ -208,7 +208,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
{ {
var schema = (await grainFactory.GetGrain<ISchemaGrain>(id.ToString()).GetStateAsync()).Value; var schema = (await grainFactory.GetGrain<ISchemaGrain>(id.ToString()).GetStateAsync()).Value;
if (schema.Version <= EtagVersion.Empty) if (schema.Version <= EtagVersion.Empty || schema.IsDeleted)
{ {
return null; return null;
} }
@ -226,11 +226,18 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
return $"{typeof(SchemasIndex)}_Schemas_Id_{appId}_{id}"; return $"{typeof(SchemasIndex)}_Schemas_Id_{appId}_{id}";
} }
private Task CacheItAsync(ISchemaEntity schema, bool publish) private Task InvalidateItAsync(ISchemaEntity schema)
{
return grainCache.RemoveAsync(
GetCacheKey(schema.AppId.Id, schema.Id),
GetCacheKey(schema.AppId.Id, schema.SchemaDef.Name));
}
private Task CacheItAsync(ISchemaEntity schema)
{ {
return Task.WhenAll( return Task.WhenAll(
grainCache.AddAsync(GetCacheKey(schema.AppId.Id, schema.Id), schema, CacheDuration, publish), grainCache.AddAsync(GetCacheKey(schema.AppId.Id, schema.Id), schema, CacheDuration),
grainCache.AddAsync(GetCacheKey(schema.AppId.Id, schema.SchemaDef.Name), schema, CacheDuration, publish)); grainCache.AddAsync(GetCacheKey(schema.AppId.Id, schema.SchemaDef.Name), schema, CacheDuration));
} }
} }
} }

2
backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -25,7 +25,7 @@
<PackageReference Include="NJsonSchema" Version="10.3.2" /> <PackageReference Include="NJsonSchema" Version="10.3.2" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets" Version="1.3.0" /> <PackageReference Include="Squidex.Assets" Version="1.3.0" />
<PackageReference Include="Squidex.Caching" Version="1.6.0" /> <PackageReference Include="Squidex.Caching" Version="1.7.0" />
<PackageReference Include="Squidex.Hosting.Abstractions" Version="1.8.0" /> <PackageReference Include="Squidex.Hosting.Abstractions" Version="1.8.0" />
<PackageReference Include="Squidex.Log" Version="1.1.0" /> <PackageReference Include="Squidex.Log" Version="1.1.0" />
<PackageReference Include="Squidex.Text" Version="1.5.0" /> <PackageReference Include="Squidex.Text" Version="1.5.0" />

2
backend/src/Squidex.Web/Pipeline/SchemaResolver.cs

@ -62,7 +62,7 @@ namespace Squidex.Web.Pipeline
{ {
var schemaId = DomainId.Create(guid); var schemaId = DomainId.Create(guid);
return appProvider.GetSchemaAsync(appId, schemaId, false, canCache); return appProvider.GetSchemaAsync(appId, schemaId, canCache);
} }
else else
{ {

2
backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs

@ -84,7 +84,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
{ {
var schemaId = DomainId.Create(guid); var schemaId = DomainId.Create(guid);
schema = await appProvider.GetSchemaAsync(AppId, schemaId, false); schema = await appProvider.GetSchemaAsync(AppId, schemaId);
} }
else else
{ {

2
backend/src/Squidex/Squidex.csproj

@ -63,7 +63,7 @@
<PackageReference Include="Squidex.Assets.FTP" Version="1.3.0" /> <PackageReference Include="Squidex.Assets.FTP" Version="1.3.0" />
<PackageReference Include="Squidex.Assets.Mongo" Version="1.3.0" /> <PackageReference Include="Squidex.Assets.Mongo" Version="1.3.0" />
<PackageReference Include="Squidex.Assets.S3" Version="1.3.0" /> <PackageReference Include="Squidex.Assets.S3" Version="1.3.0" />
<PackageReference Include="Squidex.Caching.Orleans" Version="1.5.0" /> <PackageReference Include="Squidex.Caching.Orleans" Version="1.7.0" />
<PackageReference Include="Squidex.ClientLibrary" Version="6.8.0" /> <PackageReference Include="Squidex.ClientLibrary" Version="6.8.0" />
<PackageReference Include="Squidex.Hosting" Version="1.8.0" /> <PackageReference Include="Squidex.Hosting" Version="1.8.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />

138
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs

@ -29,10 +29,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
{ {
private readonly IGrainFactory grainFactory = A.Fake<IGrainFactory>(); private readonly IGrainFactory grainFactory = A.Fake<IGrainFactory>();
private readonly IAppsByNameIndexGrain indexByName = A.Fake<IAppsByNameIndexGrain>(); private readonly IAppsByNameIndexGrain indexByName = A.Fake<IAppsByNameIndexGrain>();
private readonly IAppsByUserIndexGrain indexByUser = A.Fake<IAppsByUserIndexGrain>(); private readonly IAppsByUserIndexGrain indexForUser = A.Fake<IAppsByUserIndexGrain>();
private readonly IAppsByUserIndexGrain indexForClient = A.Fake<IAppsByUserIndexGrain>();
private readonly ICommandBus commandBus = A.Fake<ICommandBus>(); private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app"); private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly string userId = "user-1"; private readonly string userId = "user1";
private readonly string clientId = "client1";
private readonly AppsIndex sut; private readonly AppsIndex sut;
public AppsIndexTests() public AppsIndexTests()
@ -41,7 +43,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
.Returns(indexByName); .Returns(indexByName);
A.CallTo(() => grainFactory.GetGrain<IAppsByUserIndexGrain>(userId, null)) A.CallTo(() => grainFactory.GetGrain<IAppsByUserIndexGrain>(userId, null))
.Returns(indexByUser); .Returns(indexForUser);
A.CallTo(() => grainFactory.GetGrain<IAppsByUserIndexGrain>(clientId, null))
.Returns(indexForClient);
var cache = var cache =
new ReplicatedCache(new MemoryCache(Options.Create(new MemoryCacheOptions())), new SimplePubSub(A.Fake<ILogger<SimplePubSub>>()), new ReplicatedCache(new MemoryCache(Options.Create(new MemoryCacheOptions())), new SimplePubSub(A.Fake<ILogger<SimplePubSub>>()),
@ -68,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
{ {
var (expected, _) = SetupApp(); var (expected, _) = SetupApp();
A.CallTo(() => indexByUser.GetIdsAsync()) A.CallTo(() => indexForUser.GetIdsAsync())
.Returns(new List<DomainId> { appId.Id }); .Returns(new List<DomainId> { appId.Id });
var actual = await sut.GetAppsForUserAsync(userId, PermissionSet.Empty); var actual = await sut.GetAppsForUserAsync(userId, PermissionSet.Empty);
@ -84,7 +89,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
A.CallTo(() => indexByName.GetIdsAsync(A<string[]>.That.IsSameSequenceAs(new[] { appId.Name }))) A.CallTo(() => indexByName.GetIdsAsync(A<string[]>.That.IsSameSequenceAs(new[] { appId.Name })))
.Returns(new List<DomainId> { appId.Id }); .Returns(new List<DomainId> { appId.Id });
A.CallTo(() => indexByUser.GetIdsAsync()) A.CallTo(() => indexForUser.GetIdsAsync())
.Returns(new List<DomainId> { appId.Id }); .Returns(new List<DomainId> { appId.Id });
var actual = await sut.GetAppsForUserAsync(userId, new PermissionSet($"squidex.apps.{appId.Name}")); var actual = await sut.GetAppsForUserAsync(userId, new PermissionSet($"squidex.apps.{appId.Name}"));
@ -188,26 +193,42 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
.MustNotHaveHappened(); .MustNotHaveHappened();
} }
[Fact]
public async Task Should_return_null_if_app_archived()
{
SetupApp(isArchived: true);
var actual1 = await sut.GetAppAsync(appId.Id, true);
var actual2 = await sut.GetAppAsync(appId.Id, true);
Assert.Null(actual1);
Assert.Null(actual2);
}
[Fact] [Fact]
public async Task Should_return_null_if_app_not_created() public async Task Should_return_null_if_app_not_created()
{ {
SetupApp(EtagVersion.NotFound); SetupApp(EtagVersion.NotFound);
var actual = await sut.GetAppAsync(appId.Id, false); var actual1 = await sut.GetAppAsync(appId.Id, true);
var actual2 = await sut.GetAppAsync(appId.Id, true);
Assert.Null(actual); Assert.Null(actual1);
Assert.Null(actual2);
} }
[Fact] [Fact]
public async Task Should_add_app_to_indexes_on_create() public async Task Should_add_app_to_indexes_when_creating()
{ {
var token = RandomHash.Simple(); var token = RandomHash.Simple();
A.CallTo(() => indexByName.ReserveAsync(appId.Id, appId.Name)) A.CallTo(() => indexByName.ReserveAsync(appId.Id, appId.Name))
.Returns(token); .Returns(token);
var command = Create(appId.Name);
var context = var context =
new CommandContext(Create(appId.Name), commandBus) new CommandContext(command, commandBus)
.Complete(); .Complete();
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -218,20 +239,22 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
A.CallTo(() => indexByName.RemoveReservationAsync(A<string>._)) A.CallTo(() => indexByName.RemoveReservationAsync(A<string>._))
.MustNotHaveHappened(); .MustNotHaveHappened();
A.CallTo(() => indexByUser.AddAsync(appId.Id)) A.CallTo(() => indexForUser.AddAsync(appId.Id))
.MustHaveHappened(); .MustHaveHappened();
} }
[Fact] [Fact]
public async Task Should_also_app_to_user_index_if_app_created_by_client() public async Task Should_also_add_to_user_index_if_app_is_created_by_client()
{ {
var token = RandomHash.Simple(); var token = RandomHash.Simple();
A.CallTo(() => indexByName.ReserveAsync(appId.Id, appId.Name)) A.CallTo(() => indexByName.ReserveAsync(appId.Id, appId.Name))
.Returns(token); .Returns(token);
var command = CreateFromClient(appId.Name);
var context = var context =
new CommandContext(CreateFromClient(appId.Name), commandBus) new CommandContext(command, commandBus)
.Complete(); .Complete();
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -242,8 +265,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
A.CallTo(() => indexByName.RemoveReservationAsync(A<string>._)) A.CallTo(() => indexByName.RemoveReservationAsync(A<string>._))
.MustNotHaveHappened(); .MustNotHaveHappened();
A.CallTo(() => indexByUser.AddAsync(appId.Id)) A.CallTo(() => indexForClient.AddAsync(appId.Id))
.MustHaveHappened(); .MustHaveHappened();
A.CallTo(() => indexForUser.AddAsync(appId.Id))
.MustNotHaveHappened();
} }
[Fact] [Fact]
@ -254,8 +280,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
A.CallTo(() => indexByName.ReserveAsync(appId.Id, appId.Name)) A.CallTo(() => indexByName.ReserveAsync(appId.Id, appId.Name))
.Returns(token); .Returns(token);
var command = CreateFromClient(appId.Name);
var context = var context =
new CommandContext(CreateFromClient(appId.Name), commandBus); new CommandContext(command, commandBus);
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -265,18 +293,20 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
A.CallTo(() => indexByName.RemoveReservationAsync(token)) A.CallTo(() => indexByName.RemoveReservationAsync(token))
.MustHaveHappened(); .MustHaveHappened();
A.CallTo(() => indexByUser.AddAsync(appId.Id)) A.CallTo(() => indexForUser.AddAsync(appId.Id))
.MustNotHaveHappened(); .MustNotHaveHappened();
} }
[Fact] [Fact]
public async Task Should_not_add_to_indexes_on_create_if_name_taken() public async Task Should_not_add_to_indexes_when_name_is_taken()
{ {
A.CallTo(() => indexByName.ReserveAsync(appId.Id, appId.Name)) A.CallTo(() => indexByName.ReserveAsync(appId.Id, appId.Name))
.Returns(Task.FromResult<string?>(null)); .Returns(Task.FromResult<string?>(null));
var command = Create(appId.Name);
var context = var context =
new CommandContext(Create(appId.Name), commandBus) new CommandContext(command, commandBus)
.Complete(); .Complete();
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context)); await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context));
@ -287,15 +317,17 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
A.CallTo(() => indexByName.RemoveReservationAsync(A<string>._)) A.CallTo(() => indexByName.RemoveReservationAsync(A<string>._))
.MustNotHaveHappened(); .MustNotHaveHappened();
A.CallTo(() => indexByUser.AddAsync(appId.Id)) A.CallTo(() => indexForUser.AddAsync(appId.Id))
.MustNotHaveHappened(); .MustNotHaveHappened();
} }
[Fact] [Fact]
public async Task Should_not_add_to_indexes_on_create_if_name_invalid() public async Task Should_not_add_to_indexes_when_name_is_invalid()
{ {
var command = Create("INVALID");
var context = var context =
new CommandContext(Create("INVALID"), commandBus) new CommandContext(command, commandBus)
.Complete(); .Complete();
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -306,12 +338,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
A.CallTo(() => indexByName.RemoveReservationAsync(A<string>._)) A.CallTo(() => indexByName.RemoveReservationAsync(A<string>._))
.MustNotHaveHappened(); .MustNotHaveHappened();
A.CallTo(() => indexByUser.AddAsync(appId.Id)) A.CallTo(() => indexForUser.AddAsync(appId.Id))
.MustNotHaveHappened(); .MustNotHaveHappened();
} }
[Fact] [Fact]
public async Task Should_add_app_to_index_on_contributor_assignment() public async Task Should_add_app_to_index_when_contributor_assigned()
{ {
SetupApp(); SetupApp();
@ -323,7 +355,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
await sut.HandleAsync(context); await sut.HandleAsync(context);
A.CallTo(() => indexByUser.AddAsync(appId.Id)) A.CallTo(() => indexForUser.AddAsync(appId.Id))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -362,7 +394,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
} }
[Fact] [Fact]
public async Task Should_remove_from_user_index_on_remove_of_contributor() public async Task Should_remove_from_user_index_when_contributor_removed()
{ {
SetupApp(); SetupApp();
@ -374,12 +406,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
await sut.HandleAsync(context); await sut.HandleAsync(context);
A.CallTo(() => indexByUser.RemoveAsync(appId.Id)) A.CallTo(() => indexForUser.RemoveAsync(appId.Id))
.MustHaveHappened(); .MustHaveHappened();
} }
[Fact] [Fact]
public async Task Should_remove_app_from_indexes_on_archive() public async Task Should_remove_app_from_indexes_when_app_gets_archived()
{ {
SetupApp(); SetupApp();
@ -394,7 +426,30 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
A.CallTo(() => indexByName.RemoveAsync(appId.Id)) A.CallTo(() => indexByName.RemoveAsync(appId.Id))
.MustHaveHappened(); .MustHaveHappened();
A.CallTo(() => indexByUser.RemoveAsync(appId.Id)) A.CallTo(() => indexForUser.RemoveAsync(appId.Id))
.MustHaveHappenedOnceExactly();
}
[Fact]
public async Task Should_also_remove_app_from_client_index_when_created_by_client()
{
SetupApp(fromClient: true);
var command = new ArchiveApp { AppId = appId };
var context =
new CommandContext(command, commandBus)
.Complete();
await sut.HandleAsync(context);
A.CallTo(() => indexByName.RemoveAsync(appId.Id))
.MustHaveHappened();
A.CallTo(() => indexForUser.RemoveAsync(appId.Id))
.MustHaveHappened();
A.CallTo(() => indexForClient.RemoveAsync(appId.Id))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -405,7 +460,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
await sut.RebuildByContributorsAsync(userId, apps); await sut.RebuildByContributorsAsync(userId, apps);
A.CallTo(() => indexByUser.RebuildAsync(apps)) A.CallTo(() => indexForUser.RebuildAsync(apps))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -416,7 +471,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
await sut.RebuildByContributorsAsync(appId.Id, users); await sut.RebuildByContributorsAsync(appId.Id, users);
A.CallTo(() => indexByUser.AddAsync(appId.Id)) A.CallTo(() => indexForUser.AddAsync(appId.Id))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -458,7 +513,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
.MustHaveHappened(); .MustHaveHappened();
} }
private (IAppEntity, IAppGrain) SetupApp(long version = 0) private (IAppEntity, IAppGrain) SetupApp(long version = 0, bool fromClient = false, bool isArchived = false)
{ {
var appEntity = A.Fake<IAppEntity>(); var appEntity = A.Fake<IAppEntity>();
@ -468,9 +523,22 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
.Returns(appId.Name); .Returns(appId.Name);
A.CallTo(() => appEntity.Version) A.CallTo(() => appEntity.Version)
.Returns(version); .Returns(version);
A.CallTo(() => appEntity.IsArchived)
.Returns(isArchived);
A.CallTo(() => appEntity.Contributors) A.CallTo(() => appEntity.Contributors)
.Returns(AppContributors.Empty.Assign(userId, Role.Owner)); .Returns(AppContributors.Empty.Assign(userId, Role.Owner));
if (fromClient)
{
A.CallTo(() => appEntity.CreatedBy)
.Returns(ClientActor());
}
else
{
A.CallTo(() => appEntity.CreatedBy)
.Returns(UserActor());
}
var appGrain = A.Fake<IAppGrain>(); var appGrain = A.Fake<IAppGrain>();
A.CallTo(() => appGrain.GetStateAsync()) A.CallTo(() => appGrain.GetStateAsync())
@ -484,22 +552,22 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes
private CreateApp Create(string name) private CreateApp Create(string name)
{ {
return new CreateApp { AppId = appId.Id, Name = name, Actor = ActorSubject() }; return new CreateApp { AppId = appId.Id, Name = name, Actor = UserActor() };
} }
private CreateApp CreateFromClient(string name) private CreateApp CreateFromClient(string name)
{ {
return new CreateApp { AppId = appId.Id, Name = name, Actor = ActorClient() }; return new CreateApp { AppId = appId.Id, Name = name, Actor = ClientActor() };
} }
private RefToken ActorSubject() private RefToken UserActor()
{ {
return new RefToken(RefTokenType.Subject, userId); return new RefToken(RefTokenType.Subject, userId);
} }
private RefToken ActorClient() private RefToken ClientActor()
{ {
return new RefToken(RefTokenType.Client, userId); return new RefToken(RefTokenType.Client, clientId);
} }
} }
} }

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

@ -30,10 +30,10 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
var schema = Mocks.Schema(appId, schemaId, new Schema(schemaId.Name)); var schema = Mocks.Schema(appId, schemaId, new Schema(schemaId.Name));
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<DomainId>._, false, false)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<DomainId>._, false))
.Returns(Task.FromResult<ISchemaEntity?>(null)); .Returns(Task.FromResult<ISchemaEntity?>(null));
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false, false)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false))
.Returns(schema); .Returns(schema);
sut = new DefaultWorkflowsValidator(appProvider); sut = new DefaultWorkflowsValidator(appProvider);

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

@ -140,7 +140,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
{ {
var appProvider = A.Fake<IAppProvider>(); var appProvider = A.Fake<IAppProvider>();
A.CallTo(() => appProvider.GetSchemaAsync(A<DomainId>._, A<DomainId>._, false, false)) A.CallTo(() => appProvider.GetSchemaAsync(A<DomainId>._, A<DomainId>._, false))
.ReturnsLazily(x => Task.FromResult<ISchemaEntity?>(CreateSchema(x.GetArgument<DomainId>(0)!, x.GetArgument<DomainId>(1)!))); .ReturnsLazily(x => Task.FromResult<ISchemaEntity?>(CreateSchema(x.GetArgument<DomainId>(0)!, x.GetArgument<DomainId>(1)!)));
return appProvider; return appProvider;

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

@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
var ctx = CreateContext(isFrontend: false, allowSchema: true); var ctx = CreateContext(isFrontend: false, allowSchema: true);
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false, true)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, true))
.Returns(schema); .Returns(schema);
var result = await sut.GetSchemaOrThrowAsync(ctx, input); var result = await sut.GetSchemaOrThrowAsync(ctx, input);

2
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs

@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards
public GuardRuleTests() public GuardRuleTests()
{ {
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false, false)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false))
.Returns(Mocks.Schema(appId, schemaId)); .Returns(Mocks.Schema(appId, schemaId));
} }

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/Triggers/ContentChangedTriggerTests.cs

@ -42,14 +42,14 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards.Triggers
new ValidationError("Schema ID is required.", "Schemas") new ValidationError("Schema ID is required.", "Schemas")
}); });
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<DomainId>._, false, false)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<DomainId>._, false))
.MustNotHaveHappened(); .MustNotHaveHappened();
} }
[Fact] [Fact]
public async Task Should_add_error_if_schemas_ids_are_not_valid() public async Task Should_add_error_if_schemas_ids_are_not_valid()
{ {
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false, false)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false))
.Returns(Task.FromResult<ISchemaEntity?>(null)); .Returns(Task.FromResult<ISchemaEntity?>(null));
var trigger = new ContentChangedTriggerV2 var trigger = new ContentChangedTriggerV2
@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards.Triggers
[Fact] [Fact]
public async Task Should_not_add_error_if_schemas_ids_are_valid() public async Task Should_not_add_error_if_schemas_ids_are_valid()
{ {
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<DomainId>._, false, false)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<DomainId>._, false))
.Returns(Mocks.Schema(appId, schemaId)); .Returns(Mocks.Schema(appId, schemaId));
var trigger = new ContentChangedTriggerV2 var trigger = new ContentChangedTriggerV2

28
backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs

@ -154,7 +154,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
} }
[Fact] [Fact]
public async Task Should_return_schema_if_deleted() public async Task Should_return_empty_schemas_if_schema_deleted()
{ {
var (schema, _) = SetupSchema(0, true); var (schema, _) = SetupSchema(0, true);
@ -163,19 +163,21 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
var actual = await sut.GetSchemasAsync(appId.Id); var actual = await sut.GetSchemasAsync(appId.Id);
Assert.Same(actual[0], schema); Assert.Empty(actual);
} }
[Fact] [Fact]
public async Task Should_add_schema_to_index_on_create() public async Task Should_add_schema_to_index_when_creating()
{ {
var token = RandomHash.Simple(); var token = RandomHash.Simple();
A.CallTo(() => index.ReserveAsync(schemaId.Id, schemaId.Name)) A.CallTo(() => index.ReserveAsync(schemaId.Id, schemaId.Name))
.Returns(token); .Returns(token);
var command = Create(schemaId.Name);
var context = var context =
new CommandContext(Create(schemaId.Name), commandBus) new CommandContext(command, commandBus)
.Complete(); .Complete();
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -195,8 +197,10 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
A.CallTo(() => index.ReserveAsync(schemaId.Id, schemaId.Name)) A.CallTo(() => index.ReserveAsync(schemaId.Id, schemaId.Name))
.Returns(token); .Returns(token);
var command = Create(schemaId.Name);
var context = var context =
new CommandContext(Create(schemaId.Name), commandBus); new CommandContext(command, commandBus);
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -208,13 +212,15 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
} }
[Fact] [Fact]
public async Task Should_not_add_to_index_on_create_if_name_taken() public async Task Should_not_add_to_indexes_when_name_is_taken()
{ {
A.CallTo(() => index.ReserveAsync(schemaId.Id, schemaId.Name)) A.CallTo(() => index.ReserveAsync(schemaId.Id, schemaId.Name))
.Returns(Task.FromResult<string?>(null)); .Returns(Task.FromResult<string?>(null));
var command = Create(schemaId.Name);
var context = var context =
new CommandContext(Create(schemaId.Name), commandBus) new CommandContext(command, commandBus)
.Complete(); .Complete();
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context)); await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context));
@ -227,10 +233,12 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
} }
[Fact] [Fact]
public async Task Should_not_add_to_index_on_create_if_name_invalid() public async Task Should_not_add_to_indexes_when_name_is_invalid()
{ {
var command = Create("INVALID");
var context = var context =
new CommandContext(Create("INVALID"), commandBus) new CommandContext(command, commandBus)
.Complete(); .Complete();
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -277,7 +285,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
} }
[Fact] [Fact]
public async Task Should_remove_schema_from_index_on_delete_when_existed_before() public async Task Should_remove_schema_from_index_when_deleted_and_exists()
{ {
var (schema, _) = SetupSchema(); var (schema, _) = SetupSchema();

2
backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj

@ -27,7 +27,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="Microsoft.Orleans.TestingHost" Version="3.4.0" /> <PackageReference Include="Microsoft.Orleans.TestingHost" Version="3.4.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Caching.Orleans" Version="1.5.0" /> <PackageReference Include="Squidex.Caching.Orleans" Version="1.7.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />

6
backend/tests/Squidex.Web.Tests/Pipeline/SchemaResolverTests.cs

@ -66,7 +66,7 @@ namespace Squidex.Web.Pipeline
{ {
actionContext.RouteData.Values["name"] = schemaId.Id.ToString(); actionContext.RouteData.Values["name"] = schemaId.Id.ToString();
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<DomainId>._, false, true)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A<DomainId>._, true))
.Returns(Task.FromResult<ISchemaEntity?>(null)); .Returns(Task.FromResult<ISchemaEntity?>(null));
await sut.OnActionExecutionAsync(actionExecutingContext, next); await sut.OnActionExecutionAsync(actionExecutingContext, next);
@ -82,7 +82,7 @@ namespace Squidex.Web.Pipeline
var schema = CreateSchema(); var schema = CreateSchema();
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false, true)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, true))
.Returns(schema); .Returns(schema);
await sut.OnActionExecutionAsync(actionExecutingContext, next); await sut.OnActionExecutionAsync(actionExecutingContext, next);
@ -100,7 +100,7 @@ namespace Squidex.Web.Pipeline
var schema = CreateSchema(); var schema = CreateSchema();
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false, false)) A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false))
.Returns(schema); .Returns(schema);
await sut.OnActionExecutionAsync(actionExecutingContext, next); await sut.OnActionExecutionAsync(actionExecutingContext, next);

Loading…
Cancel
Save