From 31e5aa3342263c589f23ae4311baa29ef1a5635d Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 23 Jun 2018 15:39:07 +0200 Subject: [PATCH] * Migration improved * Schema field name made invariant. --- .../Contents/NamedContentData.cs | 2 +- .../Schemas/FieldCollection.cs | 3 +- .../Rules/RuleEnqueuer.cs | 22 ++++++++- .../Grains/EventConsumerManagerGrain.cs | 16 +++++++ .../Grains/IEventConsumerManagerGrain.cs | 4 ++ src/Squidex/Config/Domain/EntitiesServices.cs | 6 +++ src/Squidex/app/theme/theme.scss | 3 -- src/Squidex/package.json | 1 - .../Rules/RuleEnqueuerTests.cs | 4 ++ .../Grains/EventConsumerManagerGrainTests.cs | 24 ++++++++++ tools/Migrate_01/MigrationPath.cs | 45 +++++++++---------- .../Migrations/StartEventConsumers.cs | 30 +++++++++++++ .../Migrations/StopEventConsumers.cs | 30 +++++++++++++ 13 files changed, 157 insertions(+), 33 deletions(-) create mode 100644 tools/Migrate_01/Migrations/StartEventConsumers.cs create mode 100644 tools/Migrate_01/Migrations/StopEventConsumers.cs diff --git a/src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs b/src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs index faa409abb..3f776c24b 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs @@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Core.Contents } public NamedContentData(int capacity) - : base(capacity, EqualityComparer.Default) + : base(capacity, StringComparer.OrdinalIgnoreCase) { } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs index d62ef5873..006488a2b 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.Contracts; +using System.Globalization; using System.Linq; using Squidex.Infrastructure; @@ -59,7 +60,7 @@ namespace Squidex.Domain.Apps.Core.Schemas } else { - fieldsByName = fieldsOrdered.ToImmutableDictionary(x => x.Name); + fieldsByName = fieldsOrdered.ToImmutableDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs b/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs index f279552e6..5b0d07d12 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs @@ -5,7 +5,10 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; +using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Domain.Apps.Events; @@ -17,8 +20,10 @@ namespace Squidex.Domain.Apps.Entities.Rules { public sealed class RuleEnqueuer : IEventConsumer { + private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(2); private readonly IRuleEventRepository ruleEventRepository; private readonly IAppProvider appProvider; + private readonly IMemoryCache cache; private readonly RuleService ruleService; public string Name @@ -31,15 +36,18 @@ namespace Squidex.Domain.Apps.Entities.Rules get { return ".*"; } } - public RuleEnqueuer(IAppProvider appProvider, IRuleEventRepository ruleEventRepository, + public RuleEnqueuer(IAppProvider appProvider, IMemoryCache cache, IRuleEventRepository ruleEventRepository, RuleService ruleService) { Guard.NotNull(appProvider, nameof(appProvider)); + Guard.NotNull(cache, nameof(cache)); Guard.NotNull(ruleEventRepository, nameof(ruleEventRepository)); Guard.NotNull(ruleService, nameof(ruleService)); this.appProvider = appProvider; + this.cache = cache; + this.ruleEventRepository = ruleEventRepository; this.ruleService = ruleService; } @@ -53,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Rules { if (@event.Payload is AppEvent appEvent) { - var rules = await appProvider.GetRulesAsync(appEvent.AppId.Id); + var rules = await GetRulesAsync(appEvent.AppId.Id); foreach (var ruleEntity in rules) { @@ -66,5 +74,15 @@ namespace Squidex.Domain.Apps.Entities.Rules } } } + + private Task> GetRulesAsync(Guid appId) + { + return cache.GetOrCreateAsync(appId, entry => + { + entry.AbsoluteExpirationRelativeToNow = CacheDuration; + + return appProvider.GetRulesAsync(appId); + }); + } } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs index 97b3e93ca..ca9097142 100644 --- a/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs +++ b/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs @@ -70,6 +70,22 @@ namespace Squidex.Infrastructure.EventSourcing.Grains return new Immutable>(consumerInfos.Select(r => r.Value).ToList()); } + public Task StartAllAsync() + { + return Task.WhenAll( + eventConsumers + .Select(c => GrainFactory.GetGrain(c.Name)) + .Select(c => c.StartAsync())); + } + + public Task StopAllAsync() + { + return Task.WhenAll( + eventConsumers + .Select(c => GrainFactory.GetGrain(c.Name)) + .Select(c => c.StopAsync())); + } + public Task ResetAsync(string consumerName) { var eventConsumer = GrainFactory.GetGrain(consumerName); diff --git a/src/Squidex.Infrastructure/EventSourcing/Grains/IEventConsumerManagerGrain.cs b/src/Squidex.Infrastructure/EventSourcing/Grains/IEventConsumerManagerGrain.cs index 1730b7b30..c0b53d403 100644 --- a/src/Squidex.Infrastructure/EventSourcing/Grains/IEventConsumerManagerGrain.cs +++ b/src/Squidex.Infrastructure/EventSourcing/Grains/IEventConsumerManagerGrain.cs @@ -16,8 +16,12 @@ namespace Squidex.Infrastructure.EventSourcing.Grains { Task ActivateAsync(string streamName); + Task StopAllAsync(); + Task StopAsync(string consumerName); + Task StartAllAsync(); + Task StartAsync(string consumerName); Task ResetAsync(string consumerName); diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index d8d92d6b5..ab8132503 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -187,6 +187,12 @@ namespace Squidex.Config.Domain services.AddTransientAs() .As(); + services.AddTransientAs() + .As(); + + services.AddTransientAs() + .As(); + services.AddTransientAs() .AsSelf(); } diff --git a/src/Squidex/app/theme/theme.scss b/src/Squidex/app/theme/theme.scss index 3dca4d75d..8fef5f495 100644 --- a/src/Squidex/app/theme/theme.scss +++ b/src/Squidex/app/theme/theme.scss @@ -6,9 +6,6 @@ // Pikaday @import '~pikaday/css/pikaday.css'; -// Dragula -@import '~dragula/dist/dragula.css'; - // Bootstrap Overrides @import '_bootstrap.scss'; diff --git a/src/Squidex/package.json b/src/Squidex/package.json index d414b3fbc..55c6b4665 100644 --- a/src/Squidex/package.json +++ b/src/Squidex/package.json @@ -49,7 +49,6 @@ "@angular/compiler-cli": "6.0.4", "@ngtools/webpack": "6.0.8", "@types/core-js": "2.5.0", - "@types/dragula": "2.1.33", "@types/jasmine": "2.8.8", "@types/mousetrap": "1.6", "@types/node": "10.1.2", diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs index e27cceb12..16f02a2db 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs @@ -9,6 +9,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using FakeItEasy; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; using NodaTime; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules; @@ -25,6 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Rules public class RuleEnqueuerTests { private readonly IAppProvider appProvider = A.Fake(); + private readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); private readonly IRuleEventRepository ruleEventRepository = A.Fake(); private readonly RuleService ruleService = A.Fake(); private readonly Instant now = SystemClock.Instance.GetCurrentInstant(); @@ -35,6 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Rules { sut = new RuleEnqueuer( appProvider, + cache, ruleEventRepository, ruleService); } diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerManagerGrainTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerManagerGrainTests.cs index 22433e7fa..c04256296 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerManagerGrainTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerManagerGrainTests.cs @@ -102,6 +102,18 @@ namespace Squidex.Infrastructure.EventSourcing.Grains .MustNotHaveHappened(); } + [Fact] + public async Task Should_start_all_grains() + { + await sut.StartAllAsync(); + + A.CallTo(() => grainA.StartAsync()) + .MustHaveHappened(); + + A.CallTo(() => grainB.StartAsync()) + .MustHaveHappened(); + } + [Fact] public async Task Should_start_matching_grain() { @@ -114,6 +126,18 @@ namespace Squidex.Infrastructure.EventSourcing.Grains .MustNotHaveHappened(); } + [Fact] + public async Task Should_stop_all_grains() + { + await sut.StopAllAsync(); + + A.CallTo(() => grainA.StopAsync()) + .MustHaveHappened(); + + A.CallTo(() => grainB.StopAsync()) + .MustHaveHappened(); + } + [Fact] public async Task Should_stop_matching_grain() { diff --git a/tools/Migrate_01/MigrationPath.cs b/tools/Migrate_01/MigrationPath.cs index 5be1fcdb7..e0b7b602c 100644 --- a/tools/Migrate_01/MigrationPath.cs +++ b/tools/Migrate_01/MigrationPath.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Extensions.DependencyInjection; using Migrate_01.Migrations; using Squidex.Infrastructure.Migrations; @@ -15,7 +16,7 @@ namespace Migrate_01 { public sealed class MigrationPath : IMigrationPath { - private const int CurrentVersion = 12; + private const int CurrentVersion = 11; private readonly IServiceProvider serviceProvider; public MigrationPath(IServiceProvider serviceProvider) @@ -30,60 +31,54 @@ namespace Migrate_01 return (CurrentVersion, null); } - var migrations = new List(); + var migrations = ResolveMigrators(version).Where(x => x != null).ToList(); + + return (CurrentVersion, migrations); + } + + private IEnumerable ResolveMigrators(int version) + { + yield return serviceProvider.GetRequiredService(); // Version 06: Convert Event store. Must always be executed first. if (version < 6) { - migrations.Add(serviceProvider.GetRequiredService()); + yield return serviceProvider.GetRequiredService(); } // Version 07: Introduces AppId for backups. else if (version < 7) { - migrations.Add(serviceProvider.GetRequiredService()); + yield return serviceProvider.GetRequiredService(); } // Version 05: Fixes the broken command architecture and requires a rebuild of all snapshots. if (version < 5) { - migrations.Add(serviceProvider.GetRequiredService()); + yield return serviceProvider.GetRequiredService(); } // Version 09: Grain indexes. if (version < 9) { - var migration = serviceProvider.GetService(); - - if (migration != null) - { - migrations.Add(migration); - } - - migrations.Add(serviceProvider.GetRequiredService()); + yield return serviceProvider.GetService(); + yield return serviceProvider.GetRequiredService(); } // Version 11: Introduce content drafts. - // Version 12: Fix problems with datetimes. - if (version < 12) + if (version < 11) { - var migration = serviceProvider.GetService(); - - if (migration != null) - { - migrations.Add(migration); - } - - migrations.Add(serviceProvider.GetRequiredService()); + yield return serviceProvider.GetService(); + yield return serviceProvider.GetRequiredService(); } // Version 01: Introduce app patterns. if (version < 1) { - migrations.Add(serviceProvider.GetRequiredService()); + yield return serviceProvider.GetRequiredService(); } - return (CurrentVersion, migrations); + yield return serviceProvider.GetRequiredService(); } } } diff --git a/tools/Migrate_01/Migrations/StartEventConsumers.cs b/tools/Migrate_01/Migrations/StartEventConsumers.cs new file mode 100644 index 000000000..e65f2f42a --- /dev/null +++ b/tools/Migrate_01/Migrations/StartEventConsumers.cs @@ -0,0 +1,30 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using Orleans; +using Squidex.Infrastructure.EventSourcing.Grains; +using Squidex.Infrastructure.Migrations; +using Squidex.Infrastructure.Orleans; + +namespace Migrate_01.Migrations +{ + public sealed class StartEventConsumers : IMigration + { + private readonly IEventConsumerManagerGrain eventConsumerManager; + + public StartEventConsumers(IGrainFactory grainFactory) + { + eventConsumerManager = grainFactory.GetGrain(SingleGrain.Id); + } + + public Task UpdateAsync() + { + return eventConsumerManager.StartAllAsync(); + } + } +} diff --git a/tools/Migrate_01/Migrations/StopEventConsumers.cs b/tools/Migrate_01/Migrations/StopEventConsumers.cs new file mode 100644 index 000000000..74e45072e --- /dev/null +++ b/tools/Migrate_01/Migrations/StopEventConsumers.cs @@ -0,0 +1,30 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using Orleans; +using Squidex.Infrastructure.EventSourcing.Grains; +using Squidex.Infrastructure.Migrations; +using Squidex.Infrastructure.Orleans; + +namespace Migrate_01.Migrations +{ + public sealed class StopEventConsumers : IMigration + { + private readonly IEventConsumerManagerGrain eventConsumerManager; + + public StopEventConsumers(IGrainFactory grainFactory) + { + eventConsumerManager = grainFactory.GetGrain(SingleGrain.Id); + } + + public Task UpdateAsync() + { + return eventConsumerManager.StopAllAsync(); + } + } +}