From ee3afe88cbc79dabfadbd7bb1bcedb227c74b4e7 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 22 Jul 2018 17:04:38 +0200 Subject: [PATCH] Cleaner middleware, tests and service registration. --- .../Backup/CleanerGrain.cs | 9 ++- .../Backup/EnqueueAppToCleanerMiddleware.cs | 44 ++++++++++++++ .../Backup/ICleanerGrain.cs | 18 ++++++ .../Rules/Indexes/RulesByAppIndexGrain.cs | 14 ++--- .../Schemas/Indexes/SchemasByAppIndexGrain.cs | 14 ++--- .../Tags/GrainTagService.cs | 29 +++++---- .../Tags/ITagService.cs | 8 +-- .../Tags/TagGrain.cs | 12 ++-- src/Squidex/Config/Domain/EntitiesServices.cs | 60 ++++++++++++------- .../EnqueueAppToCleanerMiddlewareTests.cs | 48 +++++++++++++++ .../Tags/GrainTagServiceTests.cs | 11 +++- .../Tags/TagGrainTests.cs | 15 +++++ 12 files changed, 222 insertions(+), 60 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Entities/Backup/EnqueueAppToCleanerMiddleware.cs create mode 100644 src/Squidex.Domain.Apps.Entities/Backup/ICleanerGrain.cs create mode 100644 tests/Squidex.Domain.Apps.Entities.Tests/Backup/EnqueueAppToCleanerMiddlewareTests.cs diff --git a/src/Squidex.Domain.Apps.Entities/Backup/CleanerGrain.cs b/src/Squidex.Domain.Apps.Entities/Backup/CleanerGrain.cs index bd6b4c757..ffdeafa00 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/CleanerGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/CleanerGrain.cs @@ -25,7 +25,7 @@ using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities.Backup { [Reentrant] - public sealed class CleanerGrain : GrainOfString, IRemindable, IBackgroundGrain + public sealed class CleanerGrain : GrainOfString, IRemindable, ICleanerGrain { private readonly IGrainFactory grainFactory; private readonly IStore store; @@ -72,6 +72,13 @@ namespace Squidex.Domain.Apps.Entities.Backup await CleanAsync(); } + public Task EnqueueAppAsync(Guid appId) + { + state.Apps.Add(appId); + + return persistence.WriteSnapshotAsync(state); + } + public Task ActivateAsync() { return CleanAsync(); diff --git a/src/Squidex.Domain.Apps.Entities/Backup/EnqueueAppToCleanerMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Backup/EnqueueAppToCleanerMiddleware.cs new file mode 100644 index 000000000..3e6edfbcf --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Backup/EnqueueAppToCleanerMiddleware.cs @@ -0,0 +1,44 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using Orleans; +using Squidex.Domain.Apps.Entities.Apps.Commands; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.Orleans; + +namespace Squidex.Domain.Apps.Entities.Backup +{ + public sealed class EnqueueAppToCleanerMiddleware : ICommandMiddleware + { + private readonly ICleanerGrain cleaner; + + public EnqueueAppToCleanerMiddleware(IGrainFactory grainFactory) + { + Guard.NotNull(grainFactory, nameof(grainFactory)); + + cleaner = grainFactory.GetGrain(SingleGrain.Id); + } + + public async Task HandleAsync(CommandContext context, Func next) + { + if (context.IsCompleted) + { + switch (context.Command) + { + case ArchiveApp archiveApp: + await cleaner.EnqueueAppAsync(archiveApp.AppId); + break; + } + } + + await next(); + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Backup/ICleanerGrain.cs b/src/Squidex.Domain.Apps.Entities/Backup/ICleanerGrain.cs new file mode 100644 index 000000000..82077e895 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Backup/ICleanerGrain.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using Squidex.Infrastructure.Orleans; + +namespace Squidex.Domain.Apps.Entities.Backup +{ + public interface ICleanerGrain : IBackgroundGrain + { + Task EnqueueAppAsync(Guid appId); + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesByAppIndexGrain.cs b/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesByAppIndexGrain.cs index 23be15dfa..911833a74 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesByAppIndexGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesByAppIndexGrain.cs @@ -44,6 +44,13 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes return persistence.ReadAsync(); } + public Task ClearAsync() + { + state = new State(); + + return persistence.DeleteAsync(); + } + public Task RebuildAsync(HashSet rules) { state = new State { Rules = rules }; @@ -65,13 +72,6 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes return persistence.WriteSnapshotAsync(state); } - public Task ClearAsync() - { - state = new State(); - - return persistence.DeleteAsync(); - } - public Task> GetRuleIdsAsync() { return Task.FromResult(state.Rules.ToList()); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasByAppIndexGrain.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasByAppIndexGrain.cs index dc2fc69c6..69eff9348 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasByAppIndexGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasByAppIndexGrain.cs @@ -44,6 +44,13 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes return persistence.ReadAsync(); } + public Task ClearAsync() + { + state = new State(); + + return persistence.DeleteAsync(); + } + public Task RebuildAsync(Dictionary schemas) { state = new State { Schemas = schemas }; @@ -72,13 +79,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes return Task.FromResult(schemaId); } - public Task ClearAsync() - { - state = new State(); - - return persistence.DeleteAsync(); - } - public Task> GetSchemaIdsAsync() { return Task.FromResult(state.Schemas.Values.ToList()); diff --git a/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs b/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs index ff869cf80..714759843 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs @@ -13,7 +13,7 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Tags { - public sealed class GrainTagService : ITagService + public sealed class GrainTagService : ITagService, IAppStorage { private readonly IGrainFactory grainFactory; @@ -24,31 +24,36 @@ namespace Squidex.Domain.Apps.Entities.Tags this.grainFactory = grainFactory; } - public Task> NormalizeTagsAsync(Guid appId, string category, HashSet names, HashSet ids) + public Task> NormalizeTagsAsync(Guid appId, string group, HashSet names, HashSet ids) { - return GetGrain(appId, category).NormalizeTagsAsync(names, ids); + return GetGrain(appId, group).NormalizeTagsAsync(names, ids); } - public Task> GetTagIdsAsync(Guid appId, string category, HashSet names) + public Task> GetTagIdsAsync(Guid appId, string group, HashSet names) { - return GetGrain(appId, category).GetTagIdsAsync(names); + return GetGrain(appId, group).GetTagIdsAsync(names); } - public Task> DenormalizeTagsAsync(Guid appId, string category, HashSet ids) + public Task> DenormalizeTagsAsync(Guid appId, string group, HashSet ids) { - return GetGrain(appId, category).DenormalizeTagsAsync(ids); + return GetGrain(appId, group).DenormalizeTagsAsync(ids); } - public Task> GetTagsAsync(Guid appId, string category) + public Task> GetTagsAsync(Guid appId, string group) { - return GetGrain(appId, category).GetTagsAsync(); + return GetGrain(appId, group).GetTagsAsync(); } - private ITagGrain GetGrain(Guid appId, string category) + public Task ClearAsync(Guid appId) { - Guard.NotNullOrEmpty(category, nameof(category)); + return GetGrain(appId, TagGroups.Assets).ClearAsync(); + } + + private ITagGrain GetGrain(Guid appId, string group) + { + Guard.NotNullOrEmpty(group, nameof(group)); - return grainFactory.GetGrain($"{appId}_{category}"); + return grainFactory.GetGrain($"{appId}_{group}"); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Tags/ITagService.cs b/src/Squidex.Domain.Apps.Entities/Tags/ITagService.cs index 52dae1dc4..b4972b99c 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/ITagService.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/ITagService.cs @@ -13,12 +13,12 @@ namespace Squidex.Domain.Apps.Entities.Tags { public interface ITagService { - Task> NormalizeTagsAsync(Guid appId, string category, HashSet names, HashSet ids); + Task> NormalizeTagsAsync(Guid appId, string group, HashSet names, HashSet ids); - Task> GetTagIdsAsync(Guid appId, string category, HashSet names); + Task> GetTagIdsAsync(Guid appId, string group, HashSet names); - Task> DenormalizeTagsAsync(Guid appId, string category, HashSet ids); + Task> DenormalizeTagsAsync(Guid appId, string group, HashSet ids); - Task> GetTagsAsync(Guid appId, string category); + Task> GetTagsAsync(Guid appId, string group); } } diff --git a/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs b/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs index 66e17e6cb..c0f9c6fac 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs @@ -51,6 +51,13 @@ namespace Squidex.Domain.Apps.Entities.Tags return persistence.ReadAsync(); } + public Task ClearAsync() + { + state = new State(); + + return persistence.DeleteAsync(); + } + public async Task> NormalizeTagsAsync(HashSet names, HashSet ids) { var result = new HashSet(); @@ -147,10 +154,5 @@ namespace Squidex.Domain.Apps.Entities.Tags { return Task.FromResult(state.Tags.Values.ToDictionary(x => x.Name, x => x.Count)); } - - public Task ClearAsync() - { - return persistence.DeleteAsync(); - } } } diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index c87313238..9f1da9a04 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -86,7 +86,7 @@ namespace Squidex.Config.Domain .AsSelf(); services.AddSingletonAs() - .As(); + .As().As(); services.AddSingletonAs() .As>(); @@ -94,6 +94,40 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As>(); + services.AddSingletonAs() + .As(); + + services.AddSingletonAs() + .As(); + + services.AddSingletonAs() + .As(); + + AddCommandPipeline(services); + + services.AddSingleton>(DomainObjectGrainFormatter.Format); + + services.AddSingleton(c => + { + var uiOptions = c.GetRequiredService>(); + + var result = new InitialPatterns(); + + foreach (var pattern in uiOptions.Value.RegexSuggestions) + { + if (!string.IsNullOrWhiteSpace(pattern.Key) && + !string.IsNullOrWhiteSpace(pattern.Value)) + { + result[Guid.NewGuid()] = new AppPattern(pattern.Key, pattern.Value); + } + } + + return result; + }); + } + + private static void AddCommandPipeline(IServiceCollection services) + { services.AddSingletonAs() .As(); @@ -151,28 +185,8 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As(); - services.AddSingletonAs() - .As(); - - services.AddSingleton>(DomainObjectGrainFormatter.Format); - - services.AddSingleton(c => - { - var uiOptions = c.GetRequiredService>(); - - var result = new InitialPatterns(); - - foreach (var pattern in uiOptions.Value.RegexSuggestions) - { - if (!string.IsNullOrWhiteSpace(pattern.Key) && - !string.IsNullOrWhiteSpace(pattern.Value)) - { - result[Guid.NewGuid()] = new AppPattern(pattern.Key, pattern.Value); - } - } - - return result; - }); + services.AddSingletonAs() + .As(); } public static void AddMyMigrationServices(this IServiceCollection services) diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/EnqueueAppToCleanerMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/EnqueueAppToCleanerMiddlewareTests.cs new file mode 100644 index 000000000..80819b22f --- /dev/null +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/EnqueueAppToCleanerMiddlewareTests.cs @@ -0,0 +1,48 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using FakeItEasy; +using Orleans; +using Squidex.Domain.Apps.Entities.Apps.Commands; +using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.Orleans; +using Xunit; + +namespace Squidex.Domain.Apps.Entities.Backup +{ + public class EnqueueAppToCleanerMiddlewareTests + { + private readonly IGrainFactory grainFactory = A.Fake(); + private readonly ICommandBus commandBus = A.Fake(); + private readonly ICleanerGrain index = A.Fake(); + private readonly Guid appId = Guid.NewGuid(); + private readonly EnqueueAppToCleanerMiddleware sut; + + public EnqueueAppToCleanerMiddlewareTests() + { + A.CallTo(() => grainFactory.GetGrain(SingleGrain.Id, null)) + .Returns(index); + + sut = new EnqueueAppToCleanerMiddleware(grainFactory); + } + + [Fact] + public async Task Should_enqueue_for_cleanup_on_archive() + { + var context = + new CommandContext(new ArchiveApp { AppId = appId }, commandBus) + .Complete(); + + await sut.HandleAsync(context); + + A.CallTo(() => index.EnqueueAppAsync(appId)) + .MustHaveHappened(); + } + } +} diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/GrainTagServiceTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/GrainTagServiceTests.cs index f1aa0fd33..64cb5eebe 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/GrainTagServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/GrainTagServiceTests.cs @@ -30,7 +30,16 @@ namespace Squidex.Domain.Apps.Entities.Tags } [Fact] - public async Task Should_call_grain_when_retrieving_tas() + public async Task Should_call_grain_when_clearing() + { + await sut.ClearAsync(appId); + + A.CallTo(() => grain.ClearAsync()) + .MustHaveHappened(); + } + + [Fact] + public async Task Should_call_grain_when_retrieving_tags() { await sut.GetTagsAsync(appId, TagGroups.Assets); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs index ce385f017..998252ada 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs @@ -30,6 +30,21 @@ namespace Squidex.Domain.Apps.Entities.Tags sut.OnActivateAsync(string.Empty).Wait(); } + [Fact] + public async Task Should_delete_and_reset_state_when_cleaning() + { + await sut.NormalizeTagsAsync(HashSet.Of("tag1", "tag2"), null); + await sut.NormalizeTagsAsync(HashSet.Of("tag2", "tag3"), null); + await sut.ClearAsync(); + + var allTags = await sut.GetTagsAsync(); + + Assert.Empty(allTags); + + A.CallTo(() => persistence.DeleteAsync()) + .MustHaveHappened(); + } + [Fact] public async Task Should_add_tags_to_grain() {