From 92780b99af59b6d189aba49bd4cd4aefd80ba1c5 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Tue, 26 Mar 2019 18:50:24 +0100 Subject: [PATCH] Added rebuilder. --- src/Squidex/Config/Domain/EntitiesServices.cs | 3 + src/Squidex/Config/Startup/RebuilderHost.cs | 31 ++++++++ src/Squidex/WebStartup.cs | 4 + src/Squidex/appsettings.json | 23 ++++++ tools/Migrate_01/MigrationPath.cs | 3 + tools/Migrate_01/RebuildOptions.cs | 22 ++++++ tools/Migrate_01/RebuildRunner.cs | 57 ++++++++++++++ tools/Migrate_01/Rebuilder.cs | 78 ++++++++++--------- 8 files changed, 184 insertions(+), 37 deletions(-) create mode 100644 src/Squidex/Config/Startup/RebuilderHost.cs create mode 100644 tools/Migrate_01/RebuildOptions.cs create mode 100644 tools/Migrate_01/RebuildRunner.cs diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index 98641c63f..8a8b554a7 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -239,6 +239,9 @@ namespace Squidex.Config.Domain services.AddTransientAs() .AsSelf(); + services.AddTransientAs() + .AsSelf(); + services.AddTransientAs() .As(); diff --git a/src/Squidex/Config/Startup/RebuilderHost.cs b/src/Squidex/Config/Startup/RebuilderHost.cs new file mode 100644 index 000000000..55877619b --- /dev/null +++ b/src/Squidex/Config/Startup/RebuilderHost.cs @@ -0,0 +1,31 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Migrate_01; +using Squidex.Infrastructure.Log; + +namespace Squidex.Config.Startup +{ + public sealed class RebuilderHost : SafeHostedService + { + private readonly RebuildRunner rebuildRunner; + + public RebuilderHost(IApplicationLifetime lifetime, ISemanticLog log, RebuildRunner rebuildRunner) + : base(lifetime, log) + { + this.rebuildRunner = rebuildRunner; + } + + protected override Task StartAsync(ISemanticLog log, CancellationToken ct) + { + return rebuildRunner.RunAsync(ct); + } + } +} diff --git a/src/Squidex/WebStartup.cs b/src/Squidex/WebStartup.cs index b68fffec1..ec885434c 100644 --- a/src/Squidex/WebStartup.cs +++ b/src/Squidex/WebStartup.cs @@ -9,6 +9,7 @@ using System; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Migrate_01; using Squidex.Areas.Api; using Squidex.Areas.Api.Config.Swagger; using Squidex.Areas.Api.Controllers.Contents; @@ -82,6 +83,8 @@ namespace Squidex config.GetSection("healthz:gc")); services.Configure( config.GetSection("etags")); + services.Configure( + config.GetSection("rebuild")); services.Configure( config.GetSection("contentsController")); @@ -100,6 +103,7 @@ namespace Squidex { afterServices.AddHostedService(); afterServices.AddHostedService(); + afterServices.AddHostedService(); }); return provider; diff --git a/src/Squidex/appsettings.json b/src/Squidex/appsettings.json index 8ed70e2bc..56a59bb83 100644 --- a/src/Squidex/appsettings.json +++ b/src/Squidex/appsettings.json @@ -341,5 +341,28 @@ * The deepl api key if you want to support automated translations. */ "deeplAuthKey": "" + }, + + "rebuild": { + /* + * Set to true to rebuild apps. + */ + "apps": false, + /* + * Set to true to rebuild assets. + */ + "assets": false, + /* + * Set to true to rebuild contents. + */ + "contents": false, + /* + * Set to true to rebuild rules. + */ + "rules": false, + /* + * Set to true to rebuild schemas. + */ + "schemas": false } } diff --git a/tools/Migrate_01/MigrationPath.cs b/tools/Migrate_01/MigrationPath.cs index 047afeab2..0b0808829 100644 --- a/tools/Migrate_01/MigrationPath.cs +++ b/tools/Migrate_01/MigrationPath.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; using Migrate_01.Migrations; +using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; namespace Migrate_01 @@ -21,6 +22,8 @@ namespace Migrate_01 public MigrationPath(IServiceProvider serviceProvider) { + Guard.NotNull(serviceProvider, nameof(serviceProvider)); + this.serviceProvider = serviceProvider; } diff --git a/tools/Migrate_01/RebuildOptions.cs b/tools/Migrate_01/RebuildOptions.cs new file mode 100644 index 000000000..436841883 --- /dev/null +++ b/tools/Migrate_01/RebuildOptions.cs @@ -0,0 +1,22 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Migrate_01 +{ + public sealed class RebuildOptions + { + public bool Apps { get; set; } + + public bool Assets { get; set; } + + public bool Contents { get; set; } + + public bool Rules { get; set; } + + public bool Schemas { get; set; } + } +} diff --git a/tools/Migrate_01/RebuildRunner.cs b/tools/Migrate_01/RebuildRunner.cs new file mode 100644 index 000000000..742e9e670 --- /dev/null +++ b/tools/Migrate_01/RebuildRunner.cs @@ -0,0 +1,57 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Squidex.Infrastructure; + +namespace Migrate_01 +{ + public sealed class RebuildRunner + { + private readonly Rebuilder rebuilder; + private readonly RebuildOptions rebuildOptions; + + public RebuildRunner(Rebuilder rebuilder, IOptions rebuildOptions) + { + Guard.NotNull(rebuilder, nameof(rebuilder)); + Guard.NotNull(rebuildOptions, nameof(rebuildOptions)); + + this.rebuilder = rebuilder; + this.rebuildOptions = rebuildOptions.Value; + } + + public async Task RunAsync(CancellationToken ct) + { + if (rebuildOptions.Apps) + { + await rebuilder.RebuildAppsAsync(ct); + } + + if (rebuildOptions.Schemas) + { + await rebuilder.RebuildSchemasAsync(ct); + } + + if (rebuildOptions.Rules) + { + await rebuilder.RebuildRulesAsync(ct); + } + + if (rebuildOptions.Assets) + { + await rebuilder.RebuildAssetsAsync(ct); + } + + if (rebuildOptions.Contents) + { + await rebuilder.RebuildContentAsync(ct); + } + } + } +} diff --git a/tools/Migrate_01/Rebuilder.cs b/tools/Migrate_01/Rebuilder.cs index ee77eb829..6a8f17e2b 100644 --- a/tools/Migrate_01/Rebuilder.cs +++ b/tools/Migrate_01/Rebuilder.cs @@ -39,80 +39,84 @@ namespace Migrate_01 IStore store, IEventStore eventStore) { + Guard.NotNull(eventStore, nameof(eventStore)); + Guard.NotNull(localCache, nameof(localCache)); + Guard.NotNull(store, nameof(store)); + this.eventStore = eventStore; this.localCache = localCache; this.store = store; } - public async Task RebuildAppsAsync() + public async Task RebuildAppsAsync(CancellationToken ct = default) { await store.GetSnapshotStore().ClearAsync(); - await RebuildManyAsync("^app\\-", id => RebuildAsync(id, (e, s) => s.Apply(e))); + await RebuildManyAsync("^app\\-", id => RebuildAsync(id, (e, s) => s.Apply(e)), ct); } - public async Task RebuildSchemasAsync() + public async Task RebuildSchemasAsync(CancellationToken ct = default) { await store.GetSnapshotStore().ClearAsync(); - await RebuildManyAsync("^schema\\-", id => RebuildAsync(id, (e, s) => s.Apply(e))); + await RebuildManyAsync("^schema\\-", id => RebuildAsync(id, (e, s) => s.Apply(e)), ct); } - public async Task RebuildRulesAsync() + public async Task RebuildRulesAsync(CancellationToken ct = default) { await store.GetSnapshotStore().ClearAsync(); - await RebuildManyAsync("^rule\\-", id => RebuildAsync(id, (e, s) => s.Apply(e))); + await RebuildManyAsync("^rule\\-", id => RebuildAsync(id, (e, s) => s.Apply(e)), ct); } - public async Task RebuildAssetsAsync() + public async Task RebuildAssetsAsync(CancellationToken ct = default) { await store.GetSnapshotStore().ClearAsync(); - await RebuildManyAsync("^asset\\-", id => RebuildAsync(id, (e, s) => s.Apply(e))); + await RebuildManyAsync("^asset\\-", id => RebuildAsync(id, (e, s) => s.Apply(e)), ct); } - public async Task RebuildContentAsync() + public async Task RebuildContentAsync(CancellationToken ct = default) { - using (localCache.StartContext()) - { - await store.GetSnapshotStore().ClearAsync(); + await store.GetSnapshotStore().ClearAsync(); - await RebuildManyAsync("^content\\-", async id => + await RebuildManyAsync("^content\\-", async id => + { + try { - try - { - await RebuildAsync(id, (e, s) => s.Apply(e)); - } - catch (DomainObjectNotFoundException) - { - return; - } - }); - } + await RebuildAsync(id, (e, s) => s.Apply(e)); + } + catch (DomainObjectNotFoundException) + { + return; + } + }, ct); } - private async Task RebuildManyAsync(string filter, Func action) + private async Task RebuildManyAsync(string filter, Func action, CancellationToken ct) { - var handledIds = new HashSet(); - - var worker = new ActionBlock(action, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 32 }); - - await eventStore.QueryAsync(async storedEvent => + using (localCache.StartContext()) { - var headers = storedEvent.Data.Headers; + var handledIds = new HashSet(); - var id = headers.AggregateId(); + var worker = new ActionBlock(action, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 32 }); - if (handledIds.Add(id)) + await eventStore.QueryAsync(async storedEvent => { - await worker.SendAsync(id); - } - }, filter, ct: CancellationToken.None); + var headers = storedEvent.Data.Headers; - worker.Complete(); + var id = headers.AggregateId(); - await worker.Completion; + if (handledIds.Add(id)) + { + await worker.SendAsync(id); + } + }, filter, ct); + + worker.Complete(); + + await worker.Completion; + } } private async Task RebuildAsync(Guid key, Func, TState, TState> func) where TState : IDomainState, new()