From d69649f3b92c603aeee0fdaf8494d1715cf8e040 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Tue, 5 Jul 2022 09:24:35 +0200 Subject: [PATCH] Refactorings (#893) * Better immutability for domain objects. * More tests * Fix CI. --- .../ConvertContent/ExcludeChangedTypes.cs | 1 - .../MongoCountCollection.cs | 1 - .../Apps/AppPermanentDeleter.cs | 4 +- .../Apps/AppUISettings.cs | 11 +- .../Apps/AppUISettingsGrain.cs | 16 +- .../Apps/BackupApps.cs | 2 +- .../Apps/DomainObject/AppDomainObject.cs | 64 ++++---- .../Apps/DomainObject/AppDomainObjectGrain.cs | 8 +- .../Apps/DomainObject/IAppGrain.cs | 3 +- .../Apps/IAppUISettingsGrain.cs | 7 +- .../Apps/Indexes/AppsCacheGrain.cs | 4 +- .../Apps/Indexes/AppsIndex.cs | 2 +- .../Apps/Plans/UsageNotifierGrain.cs | 17 ++- .../Assets/DomainObject/AssetDomainObject.cs | 4 +- .../DomainObject/AssetDomainObjectGrain.cs | 12 +- .../DomainObject/AssetFolderDomainObject.cs | 4 +- .../AssetFolderDomainObjectGrain.cs | 12 +- .../Assets/DomainObject/IAssetGrain.cs | 3 +- .../Assets/Queries/AssetLoader.cs | 6 +- .../Assets/RebuildFiles.cs | 10 +- .../Assets/Transformations.cs | 4 +- .../Backup/BackupGrain.cs | 44 +++--- .../Backup/BackupReader.cs | 8 +- .../Backup/BackupService.cs | 14 +- .../Backup/DefaultBackupHandlerFactory.cs | 26 ++++ .../Backup/IBackupGrain.cs | 3 +- .../Backup/IBackupHandlerFactory.cs | 14 ++ .../Backup/IBackupReader.cs | 2 +- .../Backup/IBackupService.cs | 2 +- .../Backup/IRestoreGrain.cs | 3 +- .../Backup/RestoreGrain.cs | 43 +++--- .../DomainObject/CommentsCommandMiddleware.cs | 9 +- .../Comments/DomainObject/CommentsGrain.cs | 37 +++-- .../Comments/DomainObject/ICommentsGrain.cs | 3 +- .../Comments/WatchingGrain.cs | 4 +- .../Contents/BackupContents.cs | 1 - .../Contents/Counter/CounterGrain.cs | 6 +- .../DomainObject/ContentDomainObject.cs | 4 +- .../DomainObject/ContentDomainObjectGrain.cs | 7 +- .../Contents/DomainObject/IContentGrain.cs | 3 +- .../Contents/Queries/ContentLoader.cs | 6 +- .../Rules/DomainObject/IRuleGrain.cs | 3 +- .../Rules/DomainObject/RuleDomainObject.cs | 29 ++-- .../DomainObject/RuleDomainObjectGrain.cs | 8 +- .../Rules/Indexes/RulesCacheGrain.cs | 10 +- .../Rules/Indexes/RulesIndex.cs | 2 +- .../Rules/Runner/DefaultRuleRunnerService.cs | 8 +- .../Rules/Runner/RuleRunnerGrain.cs | 26 ++-- .../Rules/UsageTracking/UsageTrackerGrain.cs | 9 +- .../Schemas/DomainObject/ISchemaGrain.cs | 3 +- .../DomainObject/SchemaDomainObject.cs | 9 +- .../DomainObject/SchemaDomainObjectGrain.cs | 8 +- .../Schemas/Indexes/SchemasCacheGrain.cs | 8 +- .../Schemas/Indexes/SchemasIndex.cs | 2 +- .../Tags/TagGrain.cs | 36 +++-- .../Commands/DefaultDomainObjectFactory.cs | 56 +++++++ .../Commands/DomainObject.cs | 105 +++++-------- .../Commands/DomainObjectGrain.cs | 28 +--- .../Commands/GrainCommandMiddleware.cs | 6 +- .../Commands/IDomainObjectFactory.cs | 18 +++ .../Commands/IDomainObjectGrain.cs | 3 +- .../Commands/Rebuilder.cs | 19 +-- .../DefaultEventConsumerFactory.cs | 31 ++++ ...aFormatter.cs => DefaultEventFormatter.cs} | 4 +- .../EventSourcing/EventCommit.cs | 2 +- .../EventSourcing/Grains/BatchSubscriber.cs | 2 +- .../Grains/EventConsumerGrain.cs | 28 ++-- .../EventSourcing/IEventConsumer.cs | 4 - .../EventSourcing/IEventConsumerFactory.cs | 14 ++ ...entDataFormatter.cs => IEventFormatter.cs} | 2 +- ...Filter.cs => ActivityPropagationFilter.cs} | 106 ++++++------- .../Orleans/CultureFilter.cs | 4 +- .../Orleans/GrainBase.cs | 9 +- .../Orleans/GrainOfString.cs | 50 ------ .../Orleans/Indexes/UniqueNameGrain.cs | 9 +- .../src/Squidex.Infrastructure/Orleans/J.cs | 34 ----- .../Orleans/JsonSerializer.cs | 46 ++++++ .../Squidex.Infrastructure/Orleans/J{T}.cs | 82 ---------- .../States/BatchContext.cs | 14 +- ...Resolver.cs => DefaultEventStreamNames.cs} | 2 +- .../States/IBatchContext.cs | 2 +- ...amNameResolver.cs => IEventStreamNames.cs} | 2 +- .../States/Persistence.cs | 36 +++-- .../Squidex.Infrastructure/States/Store.cs | 32 ++-- .../Pipeline/ActionContextLogAppender.cs | 17 +-- .../Config/TokenStoreInitializer.cs | 4 +- .../src/Squidex/Config/Domain/AppsServices.cs | 4 - .../Squidex/Config/Domain/AssetServices.cs | 7 - .../Squidex/Config/Domain/CommandsServices.cs | 3 + .../Squidex/Config/Domain/ContentsServices.cs | 4 - .../Config/Domain/EventSourcingServices.cs | 17 +-- .../src/Squidex/Config/Domain/RuleServices.cs | 4 - .../Squidex/Config/Domain/SchemasServices.cs | 8 +- .../Squidex/Config/Orleans/OrleansServices.cs | 18 ++- .../Apps/AppPermanentDeleterTests.cs | 3 +- .../Apps/AppUISettingsGrainTests.cs | 44 +++--- .../Apps/AppUISettingsTests.cs | 7 +- .../DomainObject/AppCommandMiddlewareTests.cs | 3 +- .../Apps/DomainObject/AppDomainObjectTests.cs | 16 +- .../Apps/Indexes/AppsCacheGrainTests.cs | 8 +- .../Apps/Indexes/AppsIndexTests.cs | 2 +- .../Apps/Plans/UsageNotifierGrainTests.cs | 6 +- .../Assets/AssetsFluidExtensionTests.cs | 4 +- .../Assets/AssetsJintExtensionTests.cs | 4 +- .../AssetCommandMiddlewareTests.cs | 3 +- .../AssetDomainObjectGrainTests.cs | 16 +- .../DomainObject/AssetDomainObjectTests.cs | 3 +- .../AssetFolderDomainObjectGrainTests.cs | 16 +- .../AssetFolderDomainObjectTests.cs | 3 +- .../Assets/Queries/AssetLoaderTests.cs | 17 +-- .../Assets/RepairFilesTests.cs | 8 +- .../Backup/BackupReaderWriterTests.cs | 10 +- .../Backup/BackupServiceTests.cs | 6 +- .../CommentsCommandMiddlewareTests.cs | 5 +- .../DomainObject/CommentsGrainTests.cs | 16 +- .../Comments/WatchingGrainTests.cs | 4 +- .../Contents/Counter/CounterGrainTests.cs | 10 +- .../ContentCommandMiddlewareTests.cs | 3 +- .../ContentDomainObjectGrainTests.cs | 16 +- .../DomainObject/ContentDomainObjectTests.cs | 3 +- .../Contents/GraphQL/GraphQLTestBase.cs | 4 +- .../Contents/Queries/ContentLoaderTests.cs | 11 +- .../Contents/ReferencesFluidExtensionTests.cs | 4 +- .../Contents/ReferencesJintExtensionTests.cs | 4 +- .../RuleCommandMiddlewareTests.cs | 3 +- .../DomainObject/RuleDomainObjectTests.cs | 10 +- .../Rules/Indexes/RulesCacheGrainTests.cs | 8 +- .../Rules/Indexes/RulesIndexTests.cs | 3 +- .../DomainObject/SchemaDomainObjectTests.cs | 3 +- .../Schemas/Indexes/SchemasCacheGrainTests.cs | 8 +- .../Schemas/Indexes/SchemasIndexTests.cs | 3 +- .../Tags/TagGrainTests.cs | 16 +- .../TestHelpers/AssertHelper.cs | 6 - .../TestHelpers/HandlerTestBase.cs | 6 - .../Commands/CommandRequestTests.cs | 2 +- .../Commands/DomainObjectTests.cs | 50 +++--- .../DefaultEventConsumerFactoryTests.cs | 46 ++++++ ...Tests.cs => DefaultEventFormatterTests.cs} | 8 +- .../Grains/EventConsumerGrainTests.cs | 99 ++++++------ .../EventSourcing/MongoParallelInsertTests.cs | 143 ++++++++++-------- .../Orleans/ActivationLimiterFilterTests.cs | 9 +- .../Orleans/ActivityPropagationTests.cs | 140 +++++++++++++++++ .../Orleans/AsyncLocalTests.cs | 2 +- .../Orleans/CultureFilterTests.cs | 102 +++++++++++++ .../Orleans/ExceptionWrapperFilterTests.cs | 4 +- .../Orleans/Indexes/UniqueNameGrainTests.cs | 4 +- .../Orleans/JsonExternalSerializationTests.cs | 56 +++++-- .../Orleans/JsonExternalSerializerTests.cs | 42 +++-- ...sts.cs => DefaultEventStreamNamesTests.cs} | 4 +- .../States/PersistenceBatchTests.cs | 14 +- .../States/PersistenceEventSourcingTests.cs | 18 ++- .../States/PersistenceSnapshotTests.cs | 8 +- .../TestHelpers/MyDomainObject.cs | 4 +- .../TestHelpers/MyDomainState.cs | 18 +-- 154 files changed, 1417 insertions(+), 1112 deletions(-) create mode 100644 backend/src/Squidex.Domain.Apps.Entities/Backup/DefaultBackupHandlerFactory.cs create mode 100644 backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupHandlerFactory.cs create mode 100644 backend/src/Squidex.Infrastructure/Commands/DefaultDomainObjectFactory.cs create mode 100644 backend/src/Squidex.Infrastructure/Commands/IDomainObjectFactory.cs create mode 100644 backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventConsumerFactory.cs rename backend/src/Squidex.Infrastructure/EventSourcing/{DefaultEventDataFormatter.cs => DefaultEventFormatter.cs} (93%) create mode 100644 backend/src/Squidex.Infrastructure/EventSourcing/IEventConsumerFactory.cs rename backend/src/Squidex.Infrastructure/EventSourcing/{IEventDataFormatter.cs => IEventFormatter.cs} (94%) rename backend/src/Squidex.Infrastructure/Orleans/{ActivityPropagationGrainCallFilter.cs => ActivityPropagationFilter.cs} (76%) delete mode 100644 backend/src/Squidex.Infrastructure/Orleans/GrainOfString.cs delete mode 100644 backend/src/Squidex.Infrastructure/Orleans/J.cs create mode 100644 backend/src/Squidex.Infrastructure/Orleans/JsonSerializer.cs delete mode 100644 backend/src/Squidex.Infrastructure/Orleans/J{T}.cs rename backend/src/Squidex.Infrastructure/States/{DefaultStreamNameResolver.cs => DefaultEventStreamNames.cs} (91%) rename backend/src/Squidex.Infrastructure/States/{IStreamNameResolver.cs => IEventStreamNames.cs} (92%) create mode 100644 backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventConsumerFactoryTests.cs rename backend/tests/Squidex.Infrastructure.Tests/EventSourcing/{DefaultEventDataFormatterTests.cs => DefaultEventFormatterTests.cs} (92%) create mode 100644 backend/tests/Squidex.Infrastructure.Tests/Orleans/ActivityPropagationTests.cs create mode 100644 backend/tests/Squidex.Infrastructure.Tests/Orleans/CultureFilterTests.cs rename backend/tests/Squidex.Infrastructure.Tests/States/{DefaultStreamNameResolverTests.cs => DefaultEventStreamNamesTests.cs} (88%) diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeChangedTypes.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeChangedTypes.cs index 2483dbb20..035856bf3 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeChangedTypes.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeChangedTypes.cs @@ -60,7 +60,6 @@ namespace Squidex.Domain.Apps.Core.ConvertContent { return true; } - } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCountCollection.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCountCollection.cs index e1775b56d..07146b486 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCountCollection.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCountCollection.cs @@ -7,7 +7,6 @@ using MongoDB.Driver; using NodaTime; -using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Tasks; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppPermanentDeleter.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppPermanentDeleter.cs index aa24dfd7c..89332f7c6 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppPermanentDeleter.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppPermanentDeleter.cs @@ -93,7 +93,7 @@ namespace Squidex.Domain.Apps.Entities.Apps var app = await appGrain.GetStateAsync(); // If the app does not exist, the version is lower than zero. - if (app.Value.Version < 0) + if (app.Version < 0) { return; } @@ -102,7 +102,7 @@ namespace Squidex.Domain.Apps.Entities.Apps { using (Telemetry.Activities.StartActivity(deleter.GetType().Name)) { - await deleter.DeleteAppAsync(app.Value, default); + await deleter.DeleteAppAsync(app, default); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettings.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettings.cs index 8875f7a81..ccfbbef5e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettings.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettings.cs @@ -8,7 +8,6 @@ using Orleans; using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Apps { @@ -38,11 +37,9 @@ namespace Squidex.Domain.Apps.Entities.Apps } } - public async Task GetAsync(DomainId appId, string? userId) + public Task GetAsync(DomainId appId, string? userId) { - var result = await GetGrain(appId, userId).GetAsync(); - - return result.Value; + return GetGrain(appId, userId).GetAsync(); } public Task RemoveAsync(DomainId appId, string? userId, string path) @@ -52,12 +49,12 @@ namespace Squidex.Domain.Apps.Entities.Apps public Task SetAsync(DomainId appId, string? userId, string path, JsonValue value) { - return GetGrain(appId, userId).SetAsync(path, value.AsJ()); + return GetGrain(appId, userId).SetAsync(path, value); } public Task SetAsync(DomainId appId, string? userId, JsonObject settings) { - return GetGrain(appId, userId).SetAsync(settings.AsJ()); + return GetGrain(appId, userId).SetAsync(settings); } public Task ClearAsync(DomainId appId, string? userId) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs index dd44fe254..3dc83b0c6 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Orleans; @@ -12,7 +13,7 @@ using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities.Apps { - public sealed class AppUISettingsGrain : GrainOfString, IAppUISettingsGrain + public sealed class AppUISettingsGrain : GrainBase, IAppUISettingsGrain { private readonly IGrainState state; @@ -22,14 +23,15 @@ namespace Squidex.Domain.Apps.Entities.Apps public JsonObject Settings { get; set; } = new JsonObject(); } - public AppUISettingsGrain(IGrainState state) + public AppUISettingsGrain(IGrainIdentity identity, IGrainState state) + : base(identity) { this.state = state; } - public Task> GetAsync() + public Task GetAsync() { - return Task.FromResult(state.Value.Settings.AsJ()); + return Task.FromResult(state.Value.Settings); } public Task ClearAsync() @@ -39,14 +41,14 @@ namespace Squidex.Domain.Apps.Entities.Apps return state.ClearAsync(); } - public Task SetAsync(J settings) + public Task SetAsync(JsonObject settings) { state.Value.Settings = settings; return state.WriteAsync(); } - public Task SetAsync(string path, J value) + public Task SetAsync(string path, JsonValue value) { var container = GetContainer(path, true, out var key); @@ -56,7 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Apps return Task.CompletedTask; } - container[key] = value.Value; + container[key] = value; return state.WriteAsync(); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs index 2c4b34003..aebc41442 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs @@ -37,9 +37,9 @@ namespace Squidex.Domain.Apps.Entities.Apps IAppUISettings appUISettings) { this.appsIndex = appsIndex; - this.rebuilder = rebuilder; this.appImageStore = appImageStore; this.appUISettings = appUISettings; + this.rebuilder = rebuilder; } public async Task BackupEventAsync(Envelope @event, BackupContext context, diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs index d0a80b591..0f5345fe0 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; @@ -25,22 +26,13 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject { public sealed partial class AppDomainObject : DomainObject { - private readonly InitialSettings initialSettings; - private readonly IAppPlansProvider appPlansProvider; - private readonly IAppPlanBillingManager appPlansBillingManager; - private readonly IUserResolver userResolver; + private readonly IServiceProvider serviceProvider; - public AppDomainObject(IPersistenceFactory persistence, ILogger log, - InitialSettings initialSettings, - IAppPlansProvider appPlansProvider, - IAppPlanBillingManager appPlansBillingManager, - IUserResolver userResolver) - : base(persistence, log) + public AppDomainObject(DomainId id, IPersistenceFactory persistence, ILogger log, + IServiceProvider serviceProvider) + : base(id, persistence, log) { - this.userResolver = userResolver; - this.appPlansProvider = appPlansProvider; - this.appPlansBillingManager = appPlansBillingManager; - this.initialSettings = initialSettings; + this.serviceProvider = serviceProvider; } protected override bool IsDeleted(State snapshot) @@ -125,7 +117,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject case AssignContributor assignContributor: return UpdateReturnAsync(assignContributor, async c => { - await GuardAppContributors.CanAssign(c, Snapshot, userResolver, GetPlan()); + await GuardAppContributors.CanAssign(c, Snapshot, UserResolver(), GetPlan()); AssignContributor(c, !Snapshot.Contributors.ContainsKey(assignContributor.ContributorId)); @@ -265,7 +257,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject case ChangePlan changePlan: return UpdateReturnAsync(changePlan, async c => { - GuardApp.CanChangePlan(c, Snapshot, appPlansProvider); + GuardApp.CanChangePlan(c, Snapshot, AppPlansProvider()); if (c.FromCallback) { @@ -275,9 +267,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject } else { - var result = - await appPlansBillingManager.ChangePlanAsync(c.Actor.Identifier, - Snapshot.NamedId(), c.PlanId, c.Referer); + var result = await AppPlanBillingManager().ChangePlanAsync(c.Actor.Identifier, Snapshot.NamedId(), c.PlanId, c.Referer); switch (result) { @@ -293,7 +283,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject case DeleteApp delete: return UpdateAsync(delete, async c => { - await appPlansBillingManager.ChangePlanAsync(c.Actor.Identifier, Snapshot.NamedId(), null, null); + await AppPlanBillingManager().ChangePlanAsync(c.Actor.Identifier, Snapshot.NamedId(), null, null); DeleteApp(c); }); @@ -304,11 +294,6 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject } } - private IAppLimitsPlan GetPlan() - { - return appPlansProvider.GetPlanForApp(Snapshot).Plan; - } - private void Create(CreateApp command) { var appId = NamedId.Of(command.AppId, command.Name); @@ -335,7 +320,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject private void ChangePlan(ChangePlan command) { - if (string.Equals(appPlansProvider.GetFreePlan()?.Id, command.PlanId, StringComparison.Ordinal)) + if (string.Equals(GetFreePlan()?.Id, command.PlanId, StringComparison.Ordinal)) { Raise(command, new AppPlanReset()); } @@ -466,7 +451,32 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject private AppSettingsUpdated CreateInitialSettings() { - return new AppSettingsUpdated { Settings = initialSettings.Settings }; + return new AppSettingsUpdated { Settings = serviceProvider.GetRequiredService().Settings }; + } + + private IAppPlansProvider AppPlansProvider() + { + return serviceProvider.GetRequiredService(); + } + + private IAppPlanBillingManager AppPlanBillingManager() + { + return serviceProvider.GetRequiredService(); + } + + private IUserResolver UserResolver() + { + return serviceProvider.GetRequiredService(); + } + + private IAppLimitsPlan GetFreePlan() + { + return AppPlansProvider().GetFreePlan(); + } + + private IAppLimitsPlan GetPlan() + { + return AppPlansProvider().GetPlanForApp(Snapshot).Plan; } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObjectGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObjectGrain.cs index b9ac89e84..b9a8ccf9a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObjectGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObjectGrain.cs @@ -5,19 +5,19 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Apps.DomainObject { public sealed class AppDomainObjectGrain : DomainObjectGrain, IAppGrain { - public AppDomainObjectGrain(IServiceProvider serviceProvider) - : base(serviceProvider) + public AppDomainObjectGrain(IGrainIdentity identity, IDomainObjectFactory factory) + : base(identity, factory) { } - public async Task> GetStateAsync() + public async Task GetStateAsync() { await DomainObject.EnsureLoadedAsync(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/IAppGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/IAppGrain.cs index 4a3873ada..00b826750 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/IAppGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/IAppGrain.cs @@ -6,12 +6,11 @@ // ========================================================================== using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Apps.DomainObject { public interface IAppGrain : IDomainObjectGrain { - Task> GetStateAsync(); + Task GetStateAsync(); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs index 2ed13d581..690cefd69 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs @@ -7,17 +7,16 @@ using Orleans; using Squidex.Infrastructure.Json.Objects; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Apps { public interface IAppUISettingsGrain : IGrainWithStringKey { - Task> GetAsync(); + Task GetAsync(); - Task SetAsync(string path, J value); + Task SetAsync(string path, JsonValue value); - Task SetAsync(J settings); + Task SetAsync(JsonObject settings); Task RemoveAsync(string path); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsCacheGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsCacheGrain.cs index bb89c2a7d..f0de5dca9 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsCacheGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsCacheGrain.cs @@ -6,6 +6,7 @@ // ========================================================================== using Orleans.Concurrency; +using Orleans.Core; using Squidex.Domain.Apps.Entities.Apps.Repositories; using Squidex.Infrastructure; using Squidex.Infrastructure.Orleans.Indexes; @@ -18,7 +19,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes private readonly IAppRepository appRepository; private readonly Dictionary appIds = new Dictionary(); - public AppsCacheGrain(IAppRepository appRepository) + public AppsCacheGrain(IGrainIdentity identity, IAppRepository appRepository) + : base(identity) { this.appRepository = appRepository; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs index d25509e29..5650ccdfe 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs @@ -227,7 +227,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes private async Task GetAppCoreAsync(DomainId id, bool allowArchived = false) { - var app = (await grainFactory.GetGrain(id.ToString()).GetStateAsync()).Value; + var app = await grainFactory.GetGrain(id.ToString()).GetStateAsync(); if (app.Version <= EtagVersion.Empty || (app.IsDeleted && !allowArchived)) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/UsageNotifierGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/UsageNotifierGrain.cs index 03991ea0a..def700836 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/UsageNotifierGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/UsageNotifierGrain.cs @@ -6,6 +6,7 @@ // ========================================================================== using NodaTime; +using Orleans.Core; using Squidex.Domain.Apps.Entities.Notifications; using Squidex.Infrastructure; using Squidex.Infrastructure.Orleans; @@ -15,10 +16,10 @@ using Squidex.Shared.Users; namespace Squidex.Domain.Apps.Entities.Apps.Plans { - public sealed class UsageNotifierGrain : GrainOfString, IUsageNotifierGrain + public sealed class UsageNotifierGrain : GrainBase, IUsageNotifierGrain { private static readonly TimeSpan TimeBetweenNotifications = TimeSpan.FromDays(3); - private readonly IGrainState state; + private readonly IGrainState grainState; private readonly INotificationSender notificationSender; private readonly IUserResolver userResolver; private readonly IClock clock; @@ -29,9 +30,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans public Dictionary NotificationsSent { get; } = new Dictionary(); } - public UsageNotifierGrain(IGrainState state, INotificationSender notificationSender, IUserResolver userResolver, IClock clock) + public UsageNotifierGrain(IGrainIdentity identity, + IGrainState grainState, INotificationSender notificationSender, IUserResolver userResolver, IClock clock) + : base(identity) { - this.state = state; + this.grainState = grainState; this.notificationSender = notificationSender; this.userResolver = userResolver; this.clock = clock; @@ -70,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans private bool HasBeenSentBefore(DomainId appId, DateTime now) { - if (state.Value.NotificationsSent.TryGetValue(appId, out var lastSent)) + if (grainState.Value.NotificationsSent.TryGetValue(appId, out var lastSent)) { var elapsed = now - lastSent; @@ -82,9 +85,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans private Task TrackNotifiedAsync(DomainId appId, DateTime now) { - state.Value.NotificationsSent[appId] = now; + grainState.Value.NotificationsSent[appId] = now; - return state.WriteAsync(); + return grainState.WriteAsync(); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObject.cs index 193636283..06813b306 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObject.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObject.cs @@ -24,9 +24,9 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject { private readonly IServiceProvider serviceProvider; - public AssetDomainObject(IPersistenceFactory factory, ILogger log, + public AssetDomainObject(DomainId id, IPersistenceFactory persistence, ILogger log, IServiceProvider serviceProvider) - : base(factory, log) + : base(id, persistence, log) { this.serviceProvider = serviceProvider; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObjectGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObjectGrain.cs index 2603bef42..3c5db5af1 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObjectGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObjectGrain.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; @@ -15,20 +16,21 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject { private static readonly TimeSpan Lifetime = TimeSpan.FromMinutes(5); - public AssetDomainObjectGrain(IServiceProvider serviceProvider, IActivationLimit limit) - : base(serviceProvider) + public AssetDomainObjectGrain(IGrainIdentity identity, IDomainObjectFactory factory, + IActivationLimit limit) + : base(identity, factory) { limit?.SetLimit(5000, Lifetime); } - protected override Task OnActivateAsync(string key) + public override Task OnActivateAsync() { TryDelayDeactivation(Lifetime); - return base.OnActivateAsync(key); + return base.OnActivateAsync(); } - public async Task> GetStateAsync(long version = EtagVersion.Any) + public async Task GetStateAsync(long version = EtagVersion.Any) { await DomainObject.EnsureLoadedAsync(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderDomainObject.cs index e0647cbfe..34d82e461 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderDomainObject.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderDomainObject.cs @@ -24,9 +24,9 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject { private readonly IServiceProvider serviceProvider; - public AssetFolderDomainObject(IPersistenceFactory factory, ILogger log, + public AssetFolderDomainObject(DomainId id, IPersistenceFactory persistence, ILogger log, IServiceProvider serviceProvider) - : base(factory, log) + : base(id, persistence, log) { this.serviceProvider = serviceProvider; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderDomainObjectGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderDomainObjectGrain.cs index 48c0b518f..f2ab521a3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderDomainObjectGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderDomainObjectGrain.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; @@ -14,20 +15,21 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject { private static readonly TimeSpan Lifetime = TimeSpan.FromMinutes(5); - public AssetFolderDomainObjectGrain(IServiceProvider serviceProvider, IActivationLimit limit) - : base(serviceProvider) + public AssetFolderDomainObjectGrain(IGrainIdentity grainIdentity, IDomainObjectFactory factory, + IActivationLimit limit) + : base(grainIdentity, factory) { limit?.SetLimit(5000, Lifetime); } - protected override Task OnActivateAsync(string key) + public override Task OnActivateAsync() { TryDelayDeactivation(Lifetime); - return base.OnActivateAsync(key); + return base.OnActivateAsync(); } - public async Task> GetStateAsync() + public async Task GetStateAsync() { await DomainObject.EnsureLoadedAsync(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/IAssetGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/IAssetGrain.cs index 23808a534..e6e17f61d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/IAssetGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/IAssetGrain.cs @@ -7,12 +7,11 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Assets.DomainObject { public interface IAssetGrain : IDomainObjectGrain { - Task> GetStateAsync(long version = EtagVersion.Any); + Task GetStateAsync(long version = EtagVersion.Any); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetLoader.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetLoader.cs index d5eaca83e..ddc6e45f1 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetLoader.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetLoader.cs @@ -29,14 +29,12 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries var assetGrain = grainFactory.GetGrain(key.ToString()); var assetState = await assetGrain.GetStateAsync(version); - var asset = assetState.Value; - - if (asset == null || asset.Version <= EtagVersion.Empty || (version > EtagVersion.Any && asset.Version != version)) + if (assetState == null || assetState.Version <= EtagVersion.Empty || (version > EtagVersion.Any && assetState.Version != version)) { return null; } - return asset; + return assetState; } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/RebuildFiles.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/RebuildFiles.cs index c2aea3601..facde1401 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/RebuildFiles.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/RebuildFiles.cs @@ -17,17 +17,17 @@ namespace Squidex.Domain.Apps.Entities.Assets { private static readonly MemoryStream DummyStream = new MemoryStream(Encoding.UTF8.GetBytes("dummy")); private readonly IAssetFileStore assetFileStore; + private readonly IEventFormatter eventFormatter; private readonly IEventStore eventStore; - private readonly IEventDataFormatter eventDataFormatter; public RebuildFiles( IAssetFileStore assetFileStore, - IEventStore eventStore, - IEventDataFormatter eventDataFormatter) + IEventFormatter eventFormatter, + IEventStore eventStore) { this.assetFileStore = assetFileStore; this.eventStore = eventStore; - this.eventDataFormatter = eventDataFormatter; + this.eventFormatter = eventFormatter; } public async Task RepairAsync( @@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Assets { await foreach (var storedEvent in eventStore.QueryAllAsync("^asset\\-", ct: ct)) { - var @event = eventDataFormatter.ParseIfKnown(storedEvent); + var @event = eventFormatter.ParseIfKnown(storedEvent); if (@event != null) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Transformations.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Transformations.cs index 6735c5dfc..a1a909c3d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Transformations.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Transformations.cs @@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Entities.Assets public static async Task GetBlurHashAsync(this AssetRef asset, BlurOptions options, IAssetFileStore assetFileStore, - IAssetThumbnailGenerator thumbnailGenerator, + IAssetThumbnailGenerator assetThumbnails, CancellationToken ct = default) { using (var stream = DefaultPools.MemoryStream.GetStream()) @@ -86,7 +86,7 @@ namespace Squidex.Domain.Apps.Entities.Assets stream.Position = 0; - return await thumbnailGenerator.ComputeBlurHashAsync(stream, asset.MimeType, options, ct); + return await assetThumbnails.ComputeBlurHashAsync(stream, asset.MimeType, options, ct); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs index 0bab7e048..b12f8d65f 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs @@ -6,10 +6,10 @@ // ========================================================================== using System.Text.RegularExpressions; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NodaTime; using Orleans.Concurrency; +using Orleans.Core; using Squidex.Domain.Apps.Entities.Backup.State; using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; @@ -22,46 +22,47 @@ using Squidex.Shared.Users; namespace Squidex.Domain.Apps.Entities.Backup { [Reentrant] - public sealed class BackupGrain : GrainOfString, IBackupGrain + public sealed class BackupGrain : GrainBase, IBackupGrain { private const int MaxBackups = 10; private static readonly Duration UpdateDuration = Duration.FromSeconds(1); - private readonly IGrainState state; private readonly IBackupArchiveLocation backupArchiveLocation; private readonly IBackupArchiveStore backupArchiveStore; + private readonly IBackupHandlerFactory backupHandlers; private readonly IClock clock; - private readonly IServiceProvider serviceProvider; - private readonly IEventDataFormatter eventDataFormatter; + private readonly IEventFormatter eventFormatter; private readonly IEventStore eventStore; - private readonly ILogger log; + private readonly IGrainState state; private readonly IUserResolver userResolver; + private readonly ILogger log; private CancellationTokenSource? currentJobToken; private BackupJob? currentJob; - public BackupGrain( + public BackupGrain(IGrainIdentity identity, IBackupArchiveLocation backupArchiveLocation, IBackupArchiveStore backupArchiveStore, + IBackupHandlerFactory backupHandlers, IClock clock, - IEventDataFormatter eventDataFormatter, + IEventFormatter eventFormatter, IEventStore eventStore, IGrainState state, - IServiceProvider serviceProvider, IUserResolver userResolver, ILogger log) + : base(identity) { this.backupArchiveLocation = backupArchiveLocation; this.backupArchiveStore = backupArchiveStore; + this.backupHandlers = backupHandlers; this.clock = clock; - this.eventDataFormatter = eventDataFormatter; + this.eventFormatter = eventFormatter; this.eventStore = eventStore; - this.serviceProvider = serviceProvider; this.state = state; this.userResolver = userResolver; this.log = log; } - protected override Task OnActivateAsync(string key) + public override Task OnActivateAsync() { RecoverAfterRestartAsync().Forget(); @@ -127,14 +128,12 @@ namespace Squidex.Domain.Apps.Entities.Backup private async Task ProcessAsync(BackupJob job, RefToken actor, CancellationToken ct) { - var handlers = CreateHandlers(); + var handlers = backupHandlers.CreateMany(); var lastTimestamp = job.Started; try { - var appId = DomainId.Create(Key); - await using (var stream = backupArchiveLocation.OpenStream(job.Id)) { using (var writer = await backupArchiveLocation.OpenWriterAsync(stream)) @@ -143,11 +142,11 @@ namespace Squidex.Domain.Apps.Entities.Backup var userMapping = new UserMapping(actor); - var context = new BackupContext(appId, userMapping, writer); + var context = new BackupContext(Key, userMapping, writer); await foreach (var storedEvent in eventStore.QueryAllAsync(GetFilter(), ct: ct)) { - var @event = eventDataFormatter.Parse(storedEvent); + var @event = eventFormatter.Parse(storedEvent); if (@event.Payload is SquidexEvent squidexEvent && squidexEvent.Actor != null) { @@ -217,7 +216,7 @@ namespace Squidex.Domain.Apps.Entities.Backup private string GetFilter() { - return $"^[^\\-]*-{Regex.Escape(Key)}"; + return $"^[^\\-]*-{Regex.Escape(Key.ToString())}"; } private async Task WritePeriodically(Instant lastTimestamp) @@ -278,14 +277,9 @@ namespace Squidex.Domain.Apps.Entities.Backup await state.WriteAsync(); } - private IEnumerable CreateHandlers() - { - return serviceProvider.GetRequiredService>(); - } - - public Task>> GetStateAsync() + public Task> GetStateAsync() { - return J.AsTask(state.Value.Jobs.OfType().ToList()); + return Task.FromResult(state.Value.Jobs.OfType().ToList()); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs index a7286f188..0681297ef 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs @@ -97,11 +97,11 @@ namespace Squidex.Domain.Apps.Entities.Backup return attachmentEntry; } - public async IAsyncEnumerable<(string Stream, Envelope Event)> ReadEventsAsync(IStreamNameResolver streamNameResolver, IEventDataFormatter formatter, + public async IAsyncEnumerable<(string Stream, Envelope Event)> ReadEventsAsync(IEventStreamNames eventStreams, IEventFormatter eventFormatter, [EnumeratorCancellation] CancellationToken ct = default) { - Guard.NotNull(formatter); - Guard.NotNull(streamNameResolver); + Guard.NotNull(eventFormatter); + Guard.NotNull(eventStreams); while (!ct.IsCancellationRequested) { @@ -117,7 +117,7 @@ namespace Squidex.Domain.Apps.Entities.Backup var storedEvent = serializer.Deserialize(stream).ToStoredEvent(); var eventStream = storedEvent.StreamName; - var eventEnvelope = formatter.Parse(storedEvent); + var eventEnvelope = eventFormatter.Parse(storedEvent); yield return (eventStream, eventEnvelope); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupService.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupService.cs index e999a77d1..8840fb65b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupService.cs @@ -43,20 +43,16 @@ namespace Squidex.Domain.Apps.Entities.Backup return BackupGrain(appId).DeleteAsync(backupId); } - public async Task GetRestoreAsync( + public Task GetRestoreAsync( CancellationToken ct = default) { - var state = await RestoreGrain().GetStateAsync(); - - return state.Value; + return RestoreGrain().GetStateAsync(); } - public async Task> GetBackupsAsync(DomainId appId, + public Task> GetBackupsAsync(DomainId appId, CancellationToken ct = default) { - var state = await BackupGrain(appId).GetStateAsync(); - - return state.Value; + return BackupGrain(appId).GetStateAsync(); } public async Task GetBackupAsync(DomainId appId, DomainId backupId, @@ -64,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { var state = await BackupGrain(appId).GetStateAsync(); - return state.Value.Find(x => x.Id == backupId); + return state.Find(x => x.Id == backupId); } private IRestoreGrain RestoreGrain() diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/DefaultBackupHandlerFactory.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/DefaultBackupHandlerFactory.cs new file mode 100644 index 000000000..b5f0a0c05 --- /dev/null +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/DefaultBackupHandlerFactory.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.DependencyInjection; + +namespace Squidex.Domain.Apps.Entities.Backup +{ + public sealed class DefaultBackupHandlerFactory : IBackupHandlerFactory + { + private readonly IServiceProvider serviceProvider; + + public DefaultBackupHandlerFactory(IServiceProvider serviceProvider) + { + this.serviceProvider = serviceProvider; + } + + public IEnumerable CreateMany() + { + return serviceProvider.GetRequiredService>(); + } + } +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupGrain.cs index aa16e0c2f..8c7c7c61a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupGrain.cs @@ -7,7 +7,6 @@ using Orleans; using Squidex.Infrastructure; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Backup { @@ -19,6 +18,6 @@ namespace Squidex.Domain.Apps.Entities.Backup Task ClearAsync(); - Task>> GetStateAsync(); + Task> GetStateAsync(); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupHandlerFactory.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupHandlerFactory.cs new file mode 100644 index 000000000..9fc7e0130 --- /dev/null +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupHandlerFactory.cs @@ -0,0 +1,14 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Domain.Apps.Entities.Backup +{ + public interface IBackupHandlerFactory + { + IEnumerable CreateMany(); + } +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupReader.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupReader.cs index 8cacccae2..8028c5a74 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupReader.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupReader.cs @@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Backup Task HasFileAsync(string name, CancellationToken ct = default); - IAsyncEnumerable<(string Stream, Envelope Event)> ReadEventsAsync(IStreamNameResolver streamNameResolver, IEventDataFormatter formatter, + IAsyncEnumerable<(string Stream, Envelope Event)> ReadEventsAsync(IEventStreamNames eventStreams, IEventFormatter eventFormatter, CancellationToken ct = default); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupService.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupService.cs index df6226fdb..3765fb691 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/IBackupService.cs @@ -15,7 +15,7 @@ namespace Squidex.Domain.Apps.Entities.Backup Task StartRestoreAsync(RefToken actor, Uri url, string? newAppName); - Task GetRestoreAsync( + Task GetRestoreAsync( CancellationToken ct = default); Task> GetBackupsAsync(DomainId appId, diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/IRestoreGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/IRestoreGrain.cs index d384c93ec..fc48d4c02 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/IRestoreGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/IRestoreGrain.cs @@ -7,7 +7,6 @@ using Orleans; using Squidex.Infrastructure; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Backup { @@ -15,6 +14,6 @@ namespace Squidex.Domain.Apps.Entities.Backup { Task RestoreAsync(Uri url, RefToken actor, string? newAppName = null); - Task> GetStateAsync(); + Task GetStateAsync(); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs index b4ed14071..920dd3f53 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs @@ -6,9 +6,9 @@ // ========================================================================== using System.Threading.Tasks.Dataflow; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NodaTime; +using Orleans.Core; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Backup.State; @@ -25,18 +25,18 @@ using Squidex.Shared.Users; namespace Squidex.Domain.Apps.Entities.Backup { - public sealed class RestoreGrain : GrainOfString, IRestoreGrain + public sealed class RestoreGrain : GrainBase, IRestoreGrain { private readonly IBackupArchiveLocation backupArchiveLocation; + private readonly IBackupHandlerFactory backupHandlers; private readonly IClock clock; private readonly ICommandBus commandBus; + private readonly IEventFormatter eventFormatter; private readonly IEventStore eventStore; - private readonly IEventDataFormatter eventDataFormatter; + private readonly IEventStreamNames eventStreams; + private readonly IGrainState state; private readonly ILogger log; - private readonly IServiceProvider serviceProvider; - private readonly IStreamNameResolver streamNameResolver; private readonly IUserResolver userResolver; - private readonly IGrainState state; private RestoreContext runningContext; private StreamMapper runningStreamMapper; @@ -47,29 +47,31 @@ namespace Squidex.Domain.Apps.Entities.Backup public RestoreGrain( IBackupArchiveLocation backupArchiveLocation, + IBackupHandlerFactory backupHandlers, IClock clock, ICommandBus commandBus, - IEventDataFormatter eventDataFormatter, + IEventFormatter eventFormatter, IEventStore eventStore, + IEventStreamNames eventStreams, + IGrainIdentity identity, IGrainState state, - IServiceProvider serviceProvider, - IStreamNameResolver streamNameResolver, IUserResolver userResolver, ILogger log) + : base(identity) { this.backupArchiveLocation = backupArchiveLocation; + this.backupHandlers = backupHandlers; this.clock = clock; this.commandBus = commandBus; - this.eventDataFormatter = eventDataFormatter; + this.eventFormatter = eventFormatter; this.eventStore = eventStore; - this.serviceProvider = serviceProvider; + this.eventStreams = eventStreams; this.state = state; - this.streamNameResolver = streamNameResolver; this.userResolver = userResolver; this.log = log; } - protected override Task OnActivateAsync(string key) + public override Task OnActivateAsync() { RecoverAfterRestartAsync().Forget(); @@ -127,7 +129,7 @@ namespace Squidex.Domain.Apps.Entities.Backup private async Task ProcessAsync() { - var handlers = CreateHandlers(); + var handlers = backupHandlers.CreateMany(); var ct = default(CancellationToken); @@ -300,7 +302,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { var offset = runningStreamMapper.GetStreamOffset(stream); - commits.Add(EventCommit.Create(stream, offset, @event, eventDataFormatter)); + commits.Add(EventCommit.Create(stream, offset, @event, eventFormatter)); } await eventStore.AppendUnsafeAsync(commits); @@ -328,7 +330,7 @@ namespace Squidex.Domain.Apps.Entities.Backup batchBlock.BidirectionalLinkTo(writeBlock); - await foreach (var job in reader.ReadEventsAsync(streamNameResolver, eventDataFormatter)) + await foreach (var job in reader.ReadEventsAsync(eventStreams, eventFormatter)) { var newStream = await HandleEventAsync(reader, handlers, job.Stream, job.Event); @@ -425,14 +427,9 @@ namespace Squidex.Domain.Apps.Entities.Backup } } - private IEnumerable CreateHandlers() - { - return serviceProvider.GetRequiredService>(); - } - - public Task> GetStateAsync() + public Task GetStateAsync() { - return Task.FromResult>(CurrentJob); + return Task.FromResult(CurrentJob); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/CommentsCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/CommentsCommandMiddleware.cs index 6d7b7b230..048691a68 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/CommentsCommandMiddleware.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/CommentsCommandMiddleware.cs @@ -9,7 +9,6 @@ using System.Text.RegularExpressions; using Orleans; using Squidex.Domain.Apps.Entities.Comments.Commands; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; using Squidex.Shared.Users; namespace Squidex.Domain.Apps.Entities.Comments.DomainObject @@ -36,17 +35,15 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject await MentionUsersAsync(createComment); } - await ExecuteCommandAsync(context, commentsCommand); + await ExecuteCommandAsync(commentsCommand); } await next(context); } - private async Task ExecuteCommandAsync(CommandContext context, CommentsCommand commentsCommand) + private Task ExecuteCommandAsync(CommentsCommand commentsCommand) { - var result = await GetGrain(commentsCommand).ExecuteAsync(commentsCommand.AsJ()); - - context.Complete(result.Value); + return GetGrain(commentsCommand).ExecuteAsync(commentsCommand); } private ICommentsGrain GetGrain(CommentsCommand commentsCommand) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/CommentsGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/CommentsGrain.cs index b842218ee..e5f5c854a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/CommentsGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/CommentsGrain.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Domain.Apps.Entities.Comments.Commands; using Squidex.Domain.Apps.Entities.Comments.DomainObject.Guards; using Squidex.Domain.Apps.Events.Comments; @@ -18,12 +19,12 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Entities.Comments.DomainObject { - public sealed class CommentsGrain : GrainOfString, ICommentsGrain + public sealed class CommentsGrain : GrainBase, ICommentsGrain { private readonly List> uncommittedEvents = new List>(); private readonly List> events = new List>(); + private readonly IEventFormatter eventFormatter; private readonly IEventStore eventStore; - private readonly IEventDataFormatter eventDataFormatter; private long version = EtagVersion.Empty; private string streamName; @@ -32,21 +33,24 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject get => version; } - public CommentsGrain(IEventStore eventStore, IEventDataFormatter eventDataFormatter) + public CommentsGrain(IGrainIdentity identity, + IEventFormatter eventFormatter, + IEventStore eventStore) + : base(identity) { + this.eventFormatter = eventFormatter; this.eventStore = eventStore; - this.eventDataFormatter = eventDataFormatter; } - protected override async Task OnActivateAsync(string key) + public override async Task OnActivateAsync() { - streamName = $"comments-{key}"; + streamName = $"comments-{Key}"; var storedEvents = await eventStore.QueryReverseAsync(streamName, 100); foreach (var @event in storedEvents) { - var parsedEvent = eventDataFormatter.Parse(@event); + var parsedEvent = eventFormatter.Parse(@event); version = @event.EventStreamNumber; @@ -54,14 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject } } - public async Task> ExecuteAsync(J command) - { - var result = await ExecuteAsync(command.Value); - - return result.AsJ(); - } - - private Task ExecuteAsync(CommentsCommand command) + public Task ExecuteAsync(CommentsCommand command) { switch (command) { @@ -76,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject case UpdateComment updateComment: return Upsert(updateComment, c => { - GuardComments.CanUpdate(c, Key, events); + GuardComments.CanUpdate(c, Key.ToString(), events); Update(c); }); @@ -84,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject case DeleteComment deleteComment: return Upsert(deleteComment, c => { - GuardComments.CanDelete(c, Key, events); + GuardComments.CanDelete(c, Key.ToString(), events); Delete(c); }); @@ -102,7 +99,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject if (command.ExpectedVersion > EtagVersion.Any && command.ExpectedVersion != Version) { - throw new DomainObjectVersionException(Key, Version, command.ExpectedVersion); + throw new DomainObjectVersionException(Key.ToString(), Version, command.ExpectedVersion); } var previousVersion = version; @@ -115,14 +112,14 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject { var commitId = Guid.NewGuid(); - var eventData = uncommittedEvents.Select(x => eventDataFormatter.ToEventData(x, commitId)).ToList(); + var eventData = uncommittedEvents.Select(x => eventFormatter.ToEventData(x, commitId)).ToList(); await eventStore.AppendAsync(commitId, streamName, previousVersion, eventData); } events.AddRange(uncommittedEvents); - return CommandResult.Empty(DomainId.Create(Key), Version, previousVersion); + return CommandResult.Empty(Key, Version, previousVersion); } catch { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/ICommentsGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/ICommentsGrain.cs index 4dfb46729..978933d09 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/ICommentsGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Comments/DomainObject/ICommentsGrain.cs @@ -9,13 +9,12 @@ using Orleans; using Squidex.Domain.Apps.Entities.Comments.Commands; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Comments.DomainObject { public interface ICommentsGrain : IGrainWithStringKey { - Task> ExecuteAsync(J command); + Task ExecuteAsync(CommentsCommand command); Task GetCommentsAsync(long sinceVersion = EtagVersion.Any); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Comments/WatchingGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Comments/WatchingGrain.cs index 35b8c799a..c02889785 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Comments/WatchingGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Comments/WatchingGrain.cs @@ -6,6 +6,7 @@ // ========================================================================== using NodaTime; +using Orleans.Core; using Squidex.Infrastructure; using Squidex.Infrastructure.Orleans; @@ -17,7 +18,8 @@ namespace Squidex.Domain.Apps.Entities.Comments private readonly Dictionary> users = new Dictionary>(); private readonly IClock clock; - public WatchingGrain(IClock clock) + public WatchingGrain(IGrainIdentity grainIdentity, IClock clock) + : base(grainIdentity) { this.clock = clock; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs index 611052958..62bc2fc11 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs @@ -41,7 +41,6 @@ namespace Squidex.Domain.Apps.Entities.Contents public BackupContents(Rebuilder rebuilder, IUrlGenerator urlGenerator) { this.rebuilder = rebuilder; - this.urlGenerator = urlGenerator; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Counter/CounterGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Counter/CounterGrain.cs index b97549060..7048742ee 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Counter/CounterGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Counter/CounterGrain.cs @@ -5,12 +5,13 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities.Contents.Counter { - public sealed class CounterGrain : GrainOfString, ICounterGrain + public sealed class CounterGrain : GrainBase, ICounterGrain { private readonly IGrainState state; @@ -20,7 +21,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Counter public Dictionary Counters { get; set; } = new Dictionary(); } - public CounterGrain(IGrainState state) + public CounterGrain(IGrainIdentity identity, IGrainState state) + : base(identity) { this.state = state; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs index 58b51a776..340d730c5 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs @@ -28,9 +28,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject { private readonly IServiceProvider serviceProvider; - public ContentDomainObject(IPersistenceFactory persistence, ILogger log, + public ContentDomainObject(DomainId id, IPersistenceFactory persistence, ILogger log, IServiceProvider serviceProvider) - : base(persistence, log) + : base(id, persistence, log) { this.serviceProvider = serviceProvider; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObjectGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObjectGrain.cs index 3ec7fb4a1..925beb62e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObjectGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObjectGrain.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; @@ -14,13 +15,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject { private static readonly TimeSpan Lifetime = TimeSpan.FromMinutes(5); - public ContentDomainObjectGrain(IServiceProvider serviceProvider, IActivationLimit limit) - : base(serviceProvider) + public ContentDomainObjectGrain(IGrainIdentity identity, IDomainObjectFactory factory, IActivationLimit limit) + : base(identity, factory) { limit?.SetLimit(5000, Lifetime); } - public async Task> GetStateAsync(long version = -2) + public async Task GetStateAsync(long version = -2) { await DomainObject.EnsureLoadedAsync(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/IContentGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/IContentGrain.cs index 540697bc8..4f4009f95 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/IContentGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/IContentGrain.cs @@ -7,12 +7,11 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Contents.DomainObject { public interface IContentGrain : IDomainObjectGrain { - Task> GetStateAsync(long version = EtagVersion.Any); + Task GetStateAsync(long version = EtagVersion.Any); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentLoader.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentLoader.cs index 9796dd9d8..489866513 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentLoader.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentLoader.cs @@ -29,14 +29,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries var contentGrain = grainFactory.GetGrain(key); var contentState = await contentGrain.GetStateAsync(version); - var content = contentState.Value; - - if (content == null || content.Version <= EtagVersion.Empty || (version > EtagVersion.Any && content.Version != version)) + if (contentState == null || contentState.Version <= EtagVersion.Empty || (version > EtagVersion.Any && contentState.Version != version)) { return null; } - return content; + return contentState; } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/IRuleGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/IRuleGrain.cs index 6d5d0f264..7a6ed1870 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/IRuleGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/IRuleGrain.cs @@ -6,12 +6,11 @@ // ========================================================================== using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Rules.DomainObject { public interface IRuleGrain : IDomainObjectGrain { - Task> GetStateAsync(); + Task GetStateAsync(); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/RuleDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/RuleDomainObject.cs index 71024dc95..676d02b27 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/RuleDomainObject.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/RuleDomainObject.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards; @@ -22,15 +23,13 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject { public sealed partial class RuleDomainObject : DomainObject { - private readonly IAppProvider appProvider; - private readonly IRuleEnqueuer ruleEnqueuer; + private readonly IServiceProvider serviceProvider; - public RuleDomainObject(IPersistenceFactory factory, ILogger log, - IAppProvider appProvider, IRuleEnqueuer ruleEnqueuer) - : base(factory, log) + public RuleDomainObject(DomainId id, IPersistenceFactory persistence, ILogger log, + IServiceProvider serviceProvider) + : base(id, persistence, log) { - this.appProvider = appProvider; - this.ruleEnqueuer = ruleEnqueuer; + this.serviceProvider = serviceProvider; } protected override bool IsDeleted(State snapshot) @@ -57,7 +56,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject case CreateRule createRule: return CreateReturnAsync(createRule, async c => { - await GuardRule.CanCreate(c, appProvider); + await GuardRule.CanCreate(c, AppProvider()); Create(c); @@ -67,7 +66,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject case UpdateRule updateRule: return UpdateReturnAsync(updateRule, async c => { - await GuardRule.CanUpdate(c, Snapshot, appProvider); + await GuardRule.CanUpdate(c, Snapshot, AppProvider()); Update(c); @@ -117,7 +116,12 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject SimpleMapper.Map(command, @event); SimpleMapper.Map(Snapshot, @event); - await ruleEnqueuer.EnqueueAsync(Snapshot.RuleDef, Snapshot.Id, Envelope.Create(@event)); + await RuleEnqueuer().EnqueueAsync(Snapshot.RuleDef, Snapshot.Id, Envelope.Create(@event)); + } + + private IRuleEnqueuer RuleEnqueuer() + { + return serviceProvider.GetRequiredService(); } private void Create(CreateRule command) @@ -149,5 +153,10 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject { RaiseEvent(Envelope.Create(SimpleMapper.Map(command, @event))); } + + private IAppProvider AppProvider() + { + return serviceProvider.GetRequiredService(); + } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/RuleDomainObjectGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/RuleDomainObjectGrain.cs index 40e39bf6f..c49887ae7 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/RuleDomainObjectGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/RuleDomainObjectGrain.cs @@ -5,19 +5,19 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Rules.DomainObject { public sealed class RuleDomainObjectGrain : DomainObjectGrain, IRuleGrain { - public RuleDomainObjectGrain(IServiceProvider serviceProvider) - : base(serviceProvider) + public RuleDomainObjectGrain(IGrainIdentity identity, IDomainObjectFactory factory) + : base(identity, factory) { } - public async Task> GetStateAsync() + public async Task GetStateAsync() { await DomainObject.EnsureLoadedAsync(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesCacheGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesCacheGrain.cs index bb0cac147..879b441a6 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesCacheGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesCacheGrain.cs @@ -6,6 +6,7 @@ // ========================================================================== using Orleans.Concurrency; +using Orleans.Core; using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Infrastructure; using Squidex.Infrastructure.Orleans; @@ -13,14 +14,13 @@ using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Rules.Indexes { [Reentrant] - public sealed class RulesCacheGrain : GrainOfString, IRulesCacheGrain + public sealed class RulesCacheGrain : GrainBase, IRulesCacheGrain { private readonly IRuleRepository ruleRepository; private List? ruleIds; - private DomainId AppId => DomainId.Create(Key); - - public RulesCacheGrain(IRuleRepository ruleRepository) + public RulesCacheGrain(IGrainIdentity grainIdentity, IRuleRepository ruleRepository) + : base(grainIdentity) { this.ruleRepository = ruleRepository; } @@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes if (ids == null) { - ids = await ruleRepository.QueryIdsAsync(AppId); + ids = await ruleRepository.QueryIdsAsync(Key); ruleIds = ids; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesIndex.cs index 45189ce69..4e73bb7de 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesIndex.cs @@ -80,7 +80,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes private async Task GetRuleCoreAsync(DomainId id) { - var rule = (await grainFactory.GetGrain(id.ToString()).GetStateAsync()).Value; + var rule = await grainFactory.GetGrain(id.ToString()).GetStateAsync(); if (rule.Version <= EtagVersion.Empty || rule.IsDeleted) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/DefaultRuleRunnerService.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/DefaultRuleRunnerService.cs index 6e9135e8b..db0209a61 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/DefaultRuleRunnerService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/DefaultRuleRunnerService.cs @@ -19,18 +19,18 @@ namespace Squidex.Domain.Apps.Entities.Rules.Runner public sealed class DefaultRuleRunnerService : IRuleRunnerService { private const int MaxSimulatedEvents = 100; - private readonly IEventDataFormatter eventDataFormatter; + private readonly IEventFormatter eventFormatter; private readonly IEventStore eventStore; private readonly IGrainFactory grainFactory; private readonly IRuleService ruleService; public DefaultRuleRunnerService(IGrainFactory grainFactory, + IEventFormatter eventFormatter, IEventStore eventStore, - IEventDataFormatter eventDataFormatter, IRuleService ruleService) { this.grainFactory = grainFactory; - this.eventDataFormatter = eventDataFormatter; + this.eventFormatter = eventFormatter; this.eventStore = eventStore; this.ruleService = ruleService; } @@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Runner await foreach (var storedEvent in eventStore.QueryAllReverseAsync($"^([a-zA-Z0-9]+)\\-{appId.Id}", fromNow, MaxSimulatedEvents, ct)) { - var @event = eventDataFormatter.ParseIfKnown(storedEvent); + var @event = eventFormatter.ParseIfKnown(storedEvent); if (@event?.Payload is AppEvent appEvent) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerGrain.cs index ecfe162dc..8df40787c 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerGrain.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging; using Orleans; +using Orleans.Core; using Orleans.Runtime; using Squidex.Caching; using Squidex.Domain.Apps.Core.HandleRules; @@ -22,14 +23,14 @@ using TaskExtensions = Squidex.Infrastructure.Tasks.TaskExtensions; namespace Squidex.Domain.Apps.Entities.Rules.Runner { - public sealed class RuleRunnerGrain : GrainOfString, IRuleRunnerGrain, IRemindable + public sealed class RuleRunnerGrain : GrainBase, IRuleRunnerGrain, IRemindable { private const int MaxErrors = 10; - private readonly IGrainState state; private readonly IAppProvider appProvider; - private readonly ILocalCache localCache; + private readonly IEventFormatter eventFormatter; private readonly IEventStore eventStore; - private readonly IEventDataFormatter eventDataFormatter; + private readonly IGrainState state; + private readonly ILocalCache localCache; private readonly IRuleEventRepository ruleEventRepository; private readonly IRuleService ruleService; private readonly ILogger log; @@ -47,27 +48,28 @@ namespace Squidex.Domain.Apps.Entities.Rules.Runner public bool RunFromSnapshots { get; set; } } - public RuleRunnerGrain( - IGrainState state, + public RuleRunnerGrain(IGrainIdentity identity, IAppProvider appProvider, - ILocalCache localCache, + IEventFormatter eventFormatter, IEventStore eventStore, - IEventDataFormatter eventDataFormatter, + IGrainState state, + ILocalCache localCache, IRuleEventRepository ruleEventRepository, IRuleService ruleService, ILogger log) + : base(identity) { this.state = state; this.appProvider = appProvider; this.localCache = localCache; this.eventStore = eventStore; - this.eventDataFormatter = eventDataFormatter; + this.eventFormatter = eventFormatter; this.ruleEventRepository = ruleEventRepository; this.ruleService = ruleService; this.log = log; } - protected override Task OnActivateAsync(string key) + public override Task OnActivateAsync() { return EnsureIsRunningAsync(true); } @@ -154,7 +156,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Runner { currentReminder = await RegisterOrUpdateReminder("KeepAlive", TimeSpan.Zero, TimeSpan.FromMinutes(2)); - var rule = await appProvider.GetRuleAsync(DomainId.Create(Key), currentState.RuleId!.Value, ct); + var rule = await appProvider.GetRuleAsync(Key, currentState.RuleId!.Value, ct); if (rule == null) { @@ -247,7 +249,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Runner { try { - var @event = eventDataFormatter.ParseIfKnown(storedEvent); + var @event = eventFormatter.ParseIfKnown(storedEvent); if (@event != null) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs index 1e465b0b0..d02fdd96d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs @@ -7,6 +7,7 @@ using Orleans; using Orleans.Concurrency; +using Orleans.Core; using Orleans.Runtime; using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; @@ -18,7 +19,7 @@ using Squidex.Infrastructure.UsageTracking; namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking { [Reentrant] - public sealed class UsageTrackerGrain : GrainOfString, IRemindable, IUsageTrackerGrain + public sealed class UsageTrackerGrain : GrainBase, IRemindable, IUsageTrackerGrain { private readonly IGrainState state; private readonly IApiUsageTracker usageTracker; @@ -61,14 +62,14 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking public Dictionary Targets { get; set; } = new Dictionary(); } - public UsageTrackerGrain(IGrainState state, IApiUsageTracker usageTracker) + public UsageTrackerGrain(IGrainIdentity identity, IGrainState state, IApiUsageTracker usageTracker) + : base(identity) { this.state = state; - this.usageTracker = usageTracker; } - protected override Task OnActivateAsync(string key) + public override Task OnActivateAsync() { DelayDeactivation(TimeSpan.FromDays(1)); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/ISchemaGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/ISchemaGrain.cs index 3d3a0fe84..b9369c01a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/ISchemaGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/ISchemaGrain.cs @@ -6,12 +6,11 @@ // ========================================================================== using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject { public interface ISchemaGrain : IDomainObjectGrain { - Task> GetStateAsync(); + Task GetStateAsync(); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs index 143e70bb4..a67edee0a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs @@ -15,7 +15,6 @@ using Squidex.Domain.Apps.Events.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.EventSourcing; -using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.States; @@ -25,8 +24,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject { public sealed partial class SchemaDomainObject : DomainObject { - public SchemaDomainObject(IPersistenceFactory persistence, ILogger log) - : base(persistence, log) + public SchemaDomainObject(DomainId id, IPersistenceFactory persistence, ILogger log) + : base(id, persistence, log) { } @@ -407,9 +406,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject return NamedId.Of(Snapshot.SchemaFieldsTotal + 1, command.Name); } - public Task> GetStateAsync() + public Task GetStateAsync() { - return J.AsTask(Snapshot); + return Task.FromResult(Snapshot); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObjectGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObjectGrain.cs index 6c906605a..43c4de63e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObjectGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObjectGrain.cs @@ -5,19 +5,19 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject { public sealed class SchemaDomainObjectGrain : DomainObjectGrain, ISchemaGrain { - public SchemaDomainObjectGrain(IServiceProvider serviceProvider) - : base(serviceProvider) + public SchemaDomainObjectGrain(IGrainIdentity identity, IDomainObjectFactory factory) + : base(identity, factory) { } - public async Task> GetStateAsync() + public async Task GetStateAsync() { await DomainObject.EnsureLoadedAsync(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasCacheGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasCacheGrain.cs index a72909037..ec83d6b41 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasCacheGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasCacheGrain.cs @@ -6,6 +6,7 @@ // ========================================================================== using Orleans.Concurrency; +using Orleans.Core; using Squidex.Domain.Apps.Entities.Schemas.Repositories; using Squidex.Infrastructure; using Squidex.Infrastructure.Orleans.Indexes; @@ -18,9 +19,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes private readonly ISchemaRepository schemaRepository; private Dictionary? schemaIds; - private DomainId AppId => DomainId.Create(Key); - - public SchemasCacheGrain(ISchemaRepository schemaRepository) + public SchemasCacheGrain(IGrainIdentity identity, ISchemaRepository schemaRepository) + : base(identity) { this.schemaRepository = schemaRepository; } @@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes if (ids == null) { - ids = await schemaRepository.QueryIdsAsync(AppId); + ids = await schemaRepository.QueryIdsAsync(Key); schemaIds = ids; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs index f1551d491..85b8e9aa3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs @@ -205,7 +205,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes private async Task GetSchemaCoreAsync(DomainId id, bool allowDeleted = false) { - var schema = (await grainFactory.GetGrain(id.ToString()).GetStateAsync()).Value; + var schema = await grainFactory.GetGrain(id.ToString()).GetStateAsync(); if (schema.Version <= EtagVersion.Empty || (schema.IsDeleted && !allowDeleted)) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs index ad4be99c0..1457f4d8c 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; using Squidex.Domain.Apps.Core.Tags; using Squidex.Infrastructure; using Squidex.Infrastructure.Orleans; @@ -12,35 +13,42 @@ using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities.Tags { - public sealed class TagGrain : GrainOfString, ITagGrain + public sealed class TagGrain : GrainBase, ITagGrain { - private readonly IGrainState state; + private readonly IGrainState grainState; [CollectionName("Index_Tags")] public sealed class State : TagsExport { } - private Dictionary Tags => state.Value.Tags ??= new Dictionary(); + private Dictionary Tags + { + get => grainState.Value.Tags ??= new Dictionary(); + } - private Dictionary Alias => state.Value.Alias ??= new Dictionary(); + private Dictionary Alias + { + get => grainState.Value.Alias ??= new Dictionary(); + } - public TagGrain(IGrainState state) + public TagGrain(IGrainIdentity grainIdentity, IGrainState grainState) + : base(grainIdentity) { - this.state = state; + this.grainState = grainState; } public Task ClearAsync() { - return state.ClearAsync(); + return grainState.ClearAsync(); } public Task RebuildAsync(TagsExport export) { - state.Value.Tags = export.Tags; - state.Value.Alias = export.Alias; + grainState.Value.Tags = export.Tags; + grainState.Value.Alias = export.Alias; - return state.WriteAsync(); + return grainState.WriteAsync(); } public Task RenameTagAsync(string name, string newName) @@ -73,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.Tags Alias[name] = newName; - return state.WriteAsync(); + return grainState.WriteAsync(); } public async Task> NormalizeTagsAsync(HashSet? names, HashSet? ids) @@ -112,7 +120,7 @@ namespace Squidex.Domain.Apps.Entities.Tags } } - await state.WriteAsync(); + await grainState.WriteAsync(); return result; } @@ -157,12 +165,12 @@ namespace Squidex.Domain.Apps.Entities.Tags { var tags = Tags.Values.ToDictionary(x => x.Name, x => x.Count); - return Task.FromResult(new TagsSet(tags, state.Version)); + return Task.FromResult(new TagsSet(tags, grainState.Version)); } public Task GetExportableTagsAsync() { - return Task.FromResult(state.Value.Clone()); + return Task.FromResult(grainState.Value.Clone()); } private string GetId(string name, HashSet? ids) diff --git a/backend/src/Squidex.Infrastructure/Commands/DefaultDomainObjectFactory.cs b/backend/src/Squidex.Infrastructure/Commands/DefaultDomainObjectFactory.cs new file mode 100644 index 000000000..40c511592 --- /dev/null +++ b/backend/src/Squidex.Infrastructure/Commands/DefaultDomainObjectFactory.cs @@ -0,0 +1,56 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.DependencyInjection; +using Squidex.Infrastructure.States; + +#pragma warning disable RECS0108 // Warns about static fields in generic types + +namespace Squidex.Infrastructure.Commands +{ + public sealed class DefaultDomainObjectFactory : IDomainObjectFactory + { + private readonly IServiceProvider serviceProvider; + + private static class DefaultFactory + { + private static readonly ObjectFactory ObjectFactory = + ActivatorUtilities.CreateFactory(typeof(T), new[] { typeof(DomainId) }); + + public static T Create(IServiceProvider serviceProvider, DomainId id) + { + return (T)ObjectFactory(serviceProvider, new object[] { id }); + } + } + + private static class PersistenceFactory + { + private static readonly ObjectFactory ObjectFactory = + ActivatorUtilities.CreateFactory(typeof(T), new[] { typeof(DomainId), typeof(IPersistenceFactory) }); + + public static T Create(IServiceProvider serviceProvider, DomainId id, IPersistenceFactory persistenceFactory) + { + return (T)ObjectFactory(serviceProvider, new object[] { id, persistenceFactory }); + } + } + + public DefaultDomainObjectFactory(IServiceProvider serviceProvider) + { + this.serviceProvider = serviceProvider; + } + + public T Create(DomainId id) + { + return DefaultFactory.Create(serviceProvider, id); + } + + public T Create(DomainId id, IPersistenceFactory factory) + { + return PersistenceFactory.Create(serviceProvider, id, factory); + } + } +} diff --git a/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs b/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs index d1f16772b..c09af3fd5 100644 --- a/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs +++ b/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs @@ -15,11 +15,11 @@ namespace Squidex.Infrastructure.Commands { private readonly List> uncomittedEvents = new List>(); private readonly SnapshotList snapshots = new SnapshotList(); - private readonly IPersistenceFactory factory; private readonly ILogger log; - private IPersistence? persistence; + private readonly IPersistenceFactory factory; + private readonly IPersistence persistence; + private readonly DomainId uniqueId; private bool isLoaded; - private DomainId uniqueId; public DomainId UniqueId { @@ -42,14 +42,29 @@ namespace Squidex.Infrastructure.Commands set => snapshots.Capacity = value; } - protected DomainObject(IPersistenceFactory factory, + protected DomainObject(DomainId uniqueId, IPersistenceFactory factory, ILogger log) { Guard.NotNull(factory); Guard.NotNull(log); + this.uniqueId = uniqueId; this.factory = factory; + persistence = factory.WithSnapshotsAndEventSourcing(GetType(), UniqueId, + new HandleSnapshot((snapshot, version) => + { + snapshot.Version = version; + snapshots.Add(snapshot, version, true); + }), + @event => + { + // Some migrations needs the current state. + @event = @event.Migrate(Snapshot); + + return ApplyEvent(@event, true, Snapshot, Version, true).Success; + }); + this.log = log; } @@ -68,6 +83,7 @@ namespace Squidex.Infrastructure.Commands var allEvents = factory.WithEventSourcing(GetType(), UniqueId, @event => { + // Some migrations needs the current state. @event = @event.Migrate(snapshot); var (newSnapshot, isChanged) = ApplyEvent(@event, true, snapshot, snapshot.Version, false); @@ -90,45 +106,24 @@ namespace Squidex.Infrastructure.Commands return result ?? new T { Version = EtagVersion.Empty }; } - public virtual void Setup(DomainId uniqueId) - { - this.uniqueId = uniqueId; - - persistence = factory.WithSnapshotsAndEventSourcing(GetType(), UniqueId, - new HandleSnapshot((snapshot, version) => - { - snapshot.Version = version; - snapshots.Add(snapshot, version, true); - }), - @event => - { - @event = @event.Migrate(Snapshot); - - return ApplyEvent(@event, true, Snapshot, Version, true).Success; - }); - } - - public virtual async Task EnsureLoadedAsync(bool silent = false) + public virtual async Task EnsureLoadedAsync() { if (isLoaded) { return; } - if (silent) - { - await ReadAsync(); - } - else + await persistence.ReadAsync(); + + if (persistence.IsSnapshotStale) { - var watch = ValueStopwatch.StartNew(); try { - await ReadAsync(); + await persistence.WriteSnapshotAsync(Snapshot); } - finally + catch (Exception ex) { - log.LogInformation("Activated domain object of type {type} with ID {id} in {time}.", GetType(), UniqueId, watch.Stop()); + log.LogError(ex, "Failed to repair snapshot for domain object of type {type} with ID {id}.", GetType(), UniqueId); } } @@ -179,14 +174,11 @@ namespace Squidex.Infrastructure.Commands var deletedId = DomainId.Combine(UniqueId, DomainId.Create("deleted")); var deletedStream = factory.WithEventSourcing(GetType(), deletedId, null); + // Write to the deleted stream first so we never loose this information. await deletedStream.WriteEventsAsync(events); - if (persistence != null) - { - await persistence.DeleteAsync(); - - Setup(uniqueId); - } + // Cleanup the secondary stream first. + await persistence.DeleteAsync(); snapshots.Clear(); } @@ -225,7 +217,7 @@ namespace Squidex.Infrastructure.Commands } catch (InconsistentStateException) { - await EnsureLoadedAsync(true); + await EnsureLoadedAsync(); if (wasDeleted) { @@ -334,48 +326,27 @@ namespace Squidex.Infrastructure.Commands return (newSnapshot, isChanged); } - private async Task ReadAsync() - { - if (persistence != null) - { - await persistence.ReadAsync(); - - if (persistence.IsSnapshotStale) - { - try - { - await persistence.WriteSnapshotAsync(Snapshot); - } - catch (Exception ex) - { - log.LogError(ex, "Failed to repair snapshot for domain object of type {type} with ID {id}.", GetType(), UniqueId); - } - } - } - } - private async Task WriteAsync(Envelope[] newEvents) { - if (newEvents.Length > 0 && persistence != null) + if (newEvents.Length == 0) { - await persistence.WriteEventsAsync(newEvents); - await persistence.WriteSnapshotAsync(Snapshot); + return; } + + await persistence.WriteEventsAsync(newEvents); + await persistence.WriteSnapshotAsync(Snapshot); } public async Task RebuildStateAsync() { - await EnsureLoadedAsync(true); + await EnsureLoadedAsync(); if (Version <= EtagVersion.Empty) { throw new DomainObjectNotFoundException(UniqueId.ToString()); } - if (persistence != null) - { - await persistence.WriteSnapshotAsync(Snapshot); - } + await persistence.WriteSnapshotAsync(Snapshot); } protected virtual T Apply(T snapshot, Envelope @event) diff --git a/backend/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs b/backend/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs index f8ae1f87c..89bf52707 100644 --- a/backend/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs +++ b/backend/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs @@ -5,12 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Microsoft.Extensions.DependencyInjection; +using Orleans.Core; using Squidex.Infrastructure.Orleans; namespace Squidex.Infrastructure.Commands { - public abstract class DomainObjectGrain : GrainOfString where T : DomainObject where TState : class, IDomainState, new() + public abstract class DomainObjectGrain : GrainBase where T : DomainObject where TState : class, IDomainState, new() { private readonly T domainObject; @@ -24,27 +24,15 @@ namespace Squidex.Infrastructure.Commands get => domainObject; } - protected DomainObjectGrain(IServiceProvider serviceProvider) + protected DomainObjectGrain(IGrainIdentity identity, IDomainObjectFactory factory) + : base(identity) { - Guard.NotNull(serviceProvider); - - domainObject = serviceProvider.GetRequiredService(); + domainObject = factory.Create(DomainId.Create(identity.PrimaryKeyString)); } - protected override Task OnActivateAsync(string key) + public Task ExecuteAsync(IAggregateCommand command) { - domainObject.Setup(DomainId.Create(key)); - - return base.OnActivateAsync(key); - } - - public async Task> ExecuteAsync(J request) - { - request.Value.ApplyContext(); - - var result = await domainObject.ExecuteAsync(request.Value.Command); - - return result; + return domainObject.ExecuteAsync(command); } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Infrastructure/Commands/GrainCommandMiddleware.cs b/backend/src/Squidex.Infrastructure/Commands/GrainCommandMiddleware.cs index c74705642..3a12ac617 100644 --- a/backend/src/Squidex.Infrastructure/Commands/GrainCommandMiddleware.cs +++ b/backend/src/Squidex.Infrastructure/Commands/GrainCommandMiddleware.cs @@ -42,13 +42,11 @@ namespace Squidex.Infrastructure.Commands return Task.FromResult(result.Payload is None ? result : result.Payload); } - private async Task ExecuteCommandAsync(TCommand typedCommand) + private Task ExecuteCommandAsync(TCommand typedCommand) { var grain = grainFactory.GetGrain(typedCommand.AggregateId.ToString()); - var result = await grain.ExecuteAsync(CommandRequest.Create(typedCommand)); - - return result.Value; + return grain.ExecuteAsync(typedCommand); } } } diff --git a/backend/src/Squidex.Infrastructure/Commands/IDomainObjectFactory.cs b/backend/src/Squidex.Infrastructure/Commands/IDomainObjectFactory.cs new file mode 100644 index 000000000..abfea658a --- /dev/null +++ b/backend/src/Squidex.Infrastructure/Commands/IDomainObjectFactory.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.States; + +namespace Squidex.Infrastructure.Commands +{ + public interface IDomainObjectFactory + { + T Create(DomainId id); + + T Create(DomainId id, IPersistenceFactory factory); + } +} diff --git a/backend/src/Squidex.Infrastructure/Commands/IDomainObjectGrain.cs b/backend/src/Squidex.Infrastructure/Commands/IDomainObjectGrain.cs index faae259da..8a921cd4f 100644 --- a/backend/src/Squidex.Infrastructure/Commands/IDomainObjectGrain.cs +++ b/backend/src/Squidex.Infrastructure/Commands/IDomainObjectGrain.cs @@ -6,12 +6,11 @@ // ========================================================================== using Orleans; -using Squidex.Infrastructure.Orleans; namespace Squidex.Infrastructure.Commands { public interface IDomainObjectGrain : IGrainWithStringKey { - Task> ExecuteAsync(J request); + Task ExecuteAsync(IAggregateCommand command); } } diff --git a/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs b/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs index 45a782a9e..e4aefdf22 100644 --- a/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs +++ b/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs @@ -13,28 +13,18 @@ using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.States; using Squidex.Infrastructure.Tasks; -#pragma warning disable RECS0108 // Warns about static fields in generic types - namespace Squidex.Infrastructure.Commands { public class Rebuilder { + private readonly IDomainObjectFactory domainObjectFactory; private readonly ILocalCache localCache; private readonly IEventStore eventStore; private readonly IServiceProvider serviceProvider; private readonly ILogger log; - private static class Factory where T : DomainObject where TState : class, IDomainState, new() - { - private static readonly ObjectFactory ObjectFactory = ActivatorUtilities.CreateFactory(typeof(T), new[] { typeof(IPersistenceFactory) }); - - public static T Create(IServiceProvider serviceProvider, IPersistenceFactory persistenceFactory) - { - return (T)ObjectFactory(serviceProvider, new object[] { persistenceFactory }); - } - } - public Rebuilder( + IDomainObjectFactory domainObjectFactory, ILocalCache localCache, IEventStore eventStore, IServiceProvider serviceProvider, @@ -42,6 +32,7 @@ namespace Squidex.Infrastructure.Commands { this.eventStore = eventStore; this.serviceProvider = serviceProvider; + this.domainObjectFactory = domainObjectFactory; this.localCache = localCache; this.log = log; } @@ -107,9 +98,7 @@ namespace Squidex.Infrastructure.Commands { try { - var domainObject = Factory.Create(serviceProvider, context); - - domainObject.Setup(id); + var domainObject = domainObjectFactory.Create(id, context); await domainObject.RebuildStateAsync(); } diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventConsumerFactory.cs b/backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventConsumerFactory.cs new file mode 100644 index 000000000..2c07eec35 --- /dev/null +++ b/backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventConsumerFactory.cs @@ -0,0 +1,31 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.EventSourcing +{ + public sealed class DefaultEventConsumerFactory : IEventConsumerFactory + { + private readonly Dictionary eventConsumers; + + public DefaultEventConsumerFactory(IEnumerable eventConsumers) + { + this.eventConsumers = eventConsumers.ToDictionary(x => x.Name); + } + + public IEventConsumer Create(string name) + { + Guard.NotNullOrEmpty(name); + + if (!eventConsumers.TryGetValue(name, out var eventConsumer)) + { + throw new ArgumentException($"Cannot find event consuemr with name '{name}'", nameof(name)); + } + + return eventConsumer; + } + } +} diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs b/backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventFormatter.cs similarity index 93% rename from backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs rename to backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventFormatter.cs index d2665001a..746d70258 100644 --- a/backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs +++ b/backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventFormatter.cs @@ -11,12 +11,12 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Infrastructure.EventSourcing { - public sealed class DefaultEventDataFormatter : IEventDataFormatter + public sealed class DefaultEventFormatter : IEventFormatter { private readonly IJsonSerializer serializer; private readonly TypeNameRegistry typeNameRegistry; - public DefaultEventDataFormatter(TypeNameRegistry typeNameRegistry, IJsonSerializer serializer) + public DefaultEventFormatter(TypeNameRegistry typeNameRegistry, IJsonSerializer serializer) { this.typeNameRegistry = typeNameRegistry; diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/EventCommit.cs b/backend/src/Squidex.Infrastructure/EventSourcing/EventCommit.cs index 69a787f64..2a3c37871 100644 --- a/backend/src/Squidex.Infrastructure/EventSourcing/EventCommit.cs +++ b/backend/src/Squidex.Infrastructure/EventSourcing/EventCommit.cs @@ -16,7 +16,7 @@ namespace Squidex.Infrastructure.EventSourcing return new EventCommit(id, streamName, offset, new List { @event }); } - public static EventCommit Create(string streamName, long offset, Envelope envelope, IEventDataFormatter eventDataFormatter) + public static EventCommit Create(string streamName, long offset, Envelope envelope, IEventFormatter eventDataFormatter) { var id = Guid.NewGuid(); diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/Grains/BatchSubscriber.cs b/backend/src/Squidex.Infrastructure/EventSourcing/Grains/BatchSubscriber.cs index a1733f2aa..45a0897ea 100644 --- a/backend/src/Squidex.Infrastructure/EventSourcing/Grains/BatchSubscriber.cs +++ b/backend/src/Squidex.Infrastructure/EventSourcing/Grains/BatchSubscriber.cs @@ -33,7 +33,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains public BatchSubscriber( EventConsumerGrain grain, - IEventDataFormatter eventDataFormatter, + IEventFormatter eventDataFormatter, IEventConsumer eventConsumer, Func factory) { diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs b/backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs index e514ad4c1..ef1a08e6c 100644 --- a/backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs +++ b/backend/src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs @@ -7,21 +7,21 @@ using System.Runtime.CompilerServices; using Microsoft.Extensions.Logging; +using Orleans.Core; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Tasks; namespace Squidex.Infrastructure.EventSourcing.Grains { - public class EventConsumerGrain : GrainOfString, IEventConsumerGrain + public class EventConsumerGrain : GrainBase, IEventConsumerGrain { - private readonly EventConsumerFactory eventConsumerFactory; private readonly IGrainState state; - private readonly IEventDataFormatter eventDataFormatter; + private readonly IEventConsumer eventConsumer; + private readonly IEventFormatter eventFormatter; private readonly IEventStore eventStore; private readonly ILogger log; private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1); private IEventSubscription? currentSubscription; - private IEventConsumer? eventConsumer; private EventConsumerState State { @@ -30,27 +30,21 @@ namespace Squidex.Infrastructure.EventSourcing.Grains } public EventConsumerGrain( - EventConsumerFactory eventConsumerFactory, + IGrainIdentity identity, IGrainState state, + IEventConsumerFactory eventConsumerFactory, + IEventFormatter eventFormatter, IEventStore eventStore, - IEventDataFormatter eventDataFormatter, ILogger log) + : base(identity) { + this.eventConsumer = eventConsumerFactory.Create(identity.PrimaryKeyString); + this.eventFormatter = eventFormatter; this.eventStore = eventStore; - this.eventDataFormatter = eventDataFormatter; - this.eventConsumerFactory = eventConsumerFactory; this.state = state; - this.log = log; } - protected override Task OnActivateAsync(string key) - { - eventConsumer = eventConsumerFactory(key); - - return Task.CompletedTask; - } - public override Task OnDeactivateAsync() { CompleteAsync().Forget(); @@ -276,7 +270,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains private BatchSubscriber CreateSubscription() { - return new BatchSubscriber(this, eventDataFormatter, eventConsumer!, CreateRetrySubscription); + return new BatchSubscriber(this, eventFormatter, eventConsumer!, CreateRetrySubscription); } protected virtual IEventSubscription CreateRetrySubscription(IEventSubscriber subscriber) diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/IEventConsumer.cs b/backend/src/Squidex.Infrastructure/EventSourcing/IEventConsumer.cs index a34c3f6ea..7c4003427 100644 --- a/backend/src/Squidex.Infrastructure/EventSourcing/IEventConsumer.cs +++ b/backend/src/Squidex.Infrastructure/EventSourcing/IEventConsumer.cs @@ -5,12 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -#pragma warning disable MA0048 // File name must match type name - namespace Squidex.Infrastructure.EventSourcing { - public delegate IEventConsumer EventConsumerFactory(string name); - public interface IEventConsumer { int BatchDelay => 500; diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/IEventConsumerFactory.cs b/backend/src/Squidex.Infrastructure/EventSourcing/IEventConsumerFactory.cs new file mode 100644 index 000000000..1d89f2612 --- /dev/null +++ b/backend/src/Squidex.Infrastructure/EventSourcing/IEventConsumerFactory.cs @@ -0,0 +1,14 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.EventSourcing +{ + public interface IEventConsumerFactory + { + IEventConsumer Create(string name); + } +} diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs b/backend/src/Squidex.Infrastructure/EventSourcing/IEventFormatter.cs similarity index 94% rename from backend/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs rename to backend/src/Squidex.Infrastructure/EventSourcing/IEventFormatter.cs index 5a7b9deff..c1bb31a6d 100644 --- a/backend/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs +++ b/backend/src/Squidex.Infrastructure/EventSourcing/IEventFormatter.cs @@ -7,7 +7,7 @@ namespace Squidex.Infrastructure.EventSourcing { - public interface IEventDataFormatter + public interface IEventFormatter { Envelope Parse(StoredEvent storedEvent); diff --git a/backend/src/Squidex.Infrastructure/Orleans/ActivityPropagationGrainCallFilter.cs b/backend/src/Squidex.Infrastructure/Orleans/ActivityPropagationFilter.cs similarity index 76% rename from backend/src/Squidex.Infrastructure/Orleans/ActivityPropagationGrainCallFilter.cs rename to backend/src/Squidex.Infrastructure/Orleans/ActivityPropagationFilter.cs index 1bad32e6c..fc9915548 100644 --- a/backend/src/Squidex.Infrastructure/Orleans/ActivityPropagationGrainCallFilter.cs +++ b/backend/src/Squidex.Infrastructure/Orleans/ActivityPropagationFilter.cs @@ -9,18 +9,57 @@ using System.Diagnostics; using Orleans; using Orleans.Runtime; -#pragma warning disable MA0048 // File name must match type name - namespace Squidex.Infrastructure.Orleans { - public abstract class ActivityPropagationGrainCallFilter + public sealed class ActivityPropagationFilter : IOutgoingGrainCallFilter, IIncomingGrainCallFilter { - public const string ActivityNameIn = "Orleans.Runtime.GrainCall.In"; - public const string ActivityNameOut = "Orleans.Runtime.GrainCall.Out"; - protected const string TraceParentHeaderName = "traceparent"; - protected const string TraceStateHeaderName = "tracestate"; + private const string ActivityNameIn = "Orleans.Runtime.GrainCall.In"; + private const string ActivityNameOut = "Orleans.Runtime.GrainCall.Out"; + private const string TraceParentHeaderName = "traceparent"; + private const string TraceStateHeaderName = "tracestate"; + + public Task Invoke(IOutgoingGrainCallContext context) + { + if (Activity.Current != null) + { + return ProcessCurrentActivity(context); + } + + return ProcessNewActivity(context, ActivityNameOut, ActivityKind.Client, default); + } - protected static async Task ProcessNewActivity(IGrainCallContext context, string activityName, ActivityKind activityKind, ActivityContext activityContext) + public Task Invoke(IIncomingGrainCallContext context) + { + var traceParent = RequestContext.Get(TraceParentHeaderName) as string; + var traceState = RequestContext.Get(TraceStateHeaderName) as string; + var parentContext = default(ActivityContext); + + if (traceParent is not null) + { + parentContext = ActivityContext.Parse(traceParent, traceState); + } + + return ProcessNewActivity(context, ActivityNameIn, ActivityKind.Server, parentContext); + } + + private static Task ProcessCurrentActivity(IOutgoingGrainCallContext context) + { + var currentActivity = Activity.Current; + + if (currentActivity != null && currentActivity.IdFormat == ActivityIdFormat.W3C) + { + RequestContext.Set(TraceParentHeaderName, currentActivity.Id); + + if (currentActivity.TraceStateString is not null) + { + RequestContext.Set(TraceStateHeaderName, currentActivity.TraceStateString); + } + } + + return context.Invoke(); + } + + private static async Task ProcessNewActivity(IGrainCallContext context, string activityName, ActivityKind activityKind, ActivityContext activityContext) { ActivityTagsCollection? tags = null; @@ -37,7 +76,7 @@ namespace Squidex.Infrastructure.Orleans using (var activity = Telemetry.Activities.StartActivity(activityName, activityKind, activityContext, tags)) { - if (activity is not null) + if (activity != null) { RequestContext.Set(TraceParentHeaderName, activity.Id); } @@ -46,7 +85,7 @@ namespace Squidex.Infrastructure.Orleans { await context.Invoke(); - if (activity is not null && activity.IsAllDataRequested) + if (activity != null && activity.IsAllDataRequested) { activity.SetTag("status", "Ok"); } @@ -68,51 +107,4 @@ namespace Squidex.Infrastructure.Orleans } } } - - public sealed class ActivityPropagationOutgoingGrainCallFilter : ActivityPropagationGrainCallFilter, IOutgoingGrainCallFilter - { - public Task Invoke(IOutgoingGrainCallContext context) - { - if (Activity.Current != null) - { - return ProcessCurrentActivity(context); - } - - return ProcessNewActivity(context, ActivityNameOut, ActivityKind.Client, default); - } - - private static Task ProcessCurrentActivity(IOutgoingGrainCallContext context) - { - var currentActivity = Activity.Current; - - if (currentActivity is not null && currentActivity.IdFormat == ActivityIdFormat.W3C) - { - RequestContext.Set(TraceParentHeaderName, currentActivity.Id); - - if (currentActivity.TraceStateString is not null) - { - RequestContext.Set(TraceStateHeaderName, currentActivity.TraceStateString); - } - } - - return context.Invoke(); - } - } - - public sealed class ActivityPropagationIncomingGrainCallFilter : ActivityPropagationGrainCallFilter, IIncomingGrainCallFilter - { - public Task Invoke(IIncomingGrainCallContext context) - { - var traceParent = RequestContext.Get(TraceParentHeaderName) as string; - var traceState = RequestContext.Get(TraceStateHeaderName) as string; - var parentContext = default(ActivityContext); - - if (traceParent is not null) - { - parentContext = ActivityContext.Parse(traceParent, traceState); - } - - return ProcessNewActivity(context, ActivityNameIn, ActivityKind.Server, parentContext); - } - } } diff --git a/backend/src/Squidex.Infrastructure/Orleans/CultureFilter.cs b/backend/src/Squidex.Infrastructure/Orleans/CultureFilter.cs index 8f5bf2a9d..13c52ec34 100644 --- a/backend/src/Squidex.Infrastructure/Orleans/CultureFilter.cs +++ b/backend/src/Squidex.Infrastructure/Orleans/CultureFilter.cs @@ -21,7 +21,7 @@ namespace Squidex.Infrastructure.Orleans return context.Invoke(); } - public Task Invoke(IIncomingGrainCallContext context) + public async Task Invoke(IIncomingGrainCallContext context) { if (RequestContext.Get("Culture") is string culture) { @@ -33,7 +33,7 @@ namespace Squidex.Infrastructure.Orleans CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo(cultureUI); } - return context.Invoke(); + await context.Invoke(); } } } diff --git a/backend/src/Squidex.Infrastructure/Orleans/GrainBase.cs b/backend/src/Squidex.Infrastructure/Orleans/GrainBase.cs index 6279d52ae..f24a82849 100644 --- a/backend/src/Squidex.Infrastructure/Orleans/GrainBase.cs +++ b/backend/src/Squidex.Infrastructure/Orleans/GrainBase.cs @@ -14,13 +14,14 @@ namespace Squidex.Infrastructure.Orleans { public abstract class GrainBase : Grain { - protected GrainBase() - { - } + public DomainId Key { get; private set; } - protected GrainBase(IGrainIdentity? identity, IGrainRuntime? runtime) + protected GrainBase(IGrainIdentity identity, IGrainRuntime? runtime = null) : base(identity, runtime) { + Guard.NotNull(identity); + + Key = DomainId.Create(identity.PrimaryKeyString); } public void ReportIAmAlive() diff --git a/backend/src/Squidex.Infrastructure/Orleans/GrainOfString.cs b/backend/src/Squidex.Infrastructure/Orleans/GrainOfString.cs deleted file mode 100644 index 54e8506f5..000000000 --- a/backend/src/Squidex.Infrastructure/Orleans/GrainOfString.cs +++ /dev/null @@ -1,50 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Orleans; -using Orleans.Core; -using Orleans.Runtime; - -namespace Squidex.Infrastructure.Orleans -{ - public abstract class GrainOfString : GrainBase - { - public string Key { get; private set; } - - protected GrainOfString() - { - } - - protected GrainOfString(IGrainIdentity identity, IGrainRuntime runtime) - : base(identity, runtime) - { - } - - public sealed override Task OnActivateAsync() - { - return ActivateAsync(this.GetPrimaryKeyString()); - } - - public async Task ActivateAsync(string key) - { - Key = key; - - await OnLoadAsync(key); - await OnActivateAsync(key); - } - - protected virtual Task OnLoadAsync(string key) - { - return Task.CompletedTask; - } - - protected virtual Task OnActivateAsync(string key) - { - return Task.CompletedTask; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Orleans/Indexes/UniqueNameGrain.cs b/backend/src/Squidex.Infrastructure/Orleans/Indexes/UniqueNameGrain.cs index 517dd2d90..2b7a6864d 100644 --- a/backend/src/Squidex.Infrastructure/Orleans/Indexes/UniqueNameGrain.cs +++ b/backend/src/Squidex.Infrastructure/Orleans/Indexes/UniqueNameGrain.cs @@ -5,12 +5,19 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Orleans.Core; + namespace Squidex.Infrastructure.Orleans.Indexes { - public class UniqueNameGrain : GrainOfString, IUniqueNameGrain + public class UniqueNameGrain : GrainBase, IUniqueNameGrain { private readonly Dictionary reservations = new Dictionary(); + public UniqueNameGrain(IGrainIdentity identity) + : base(identity) + { + } + public virtual Task ReserveAsync(T id, string name) { string? token = null; diff --git a/backend/src/Squidex.Infrastructure/Orleans/J.cs b/backend/src/Squidex.Infrastructure/Orleans/J.cs deleted file mode 100644 index 4238e8c63..000000000 --- a/backend/src/Squidex.Infrastructure/Orleans/J.cs +++ /dev/null @@ -1,34 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Squidex.Infrastructure.Json; - -#pragma warning disable SA1401 // Fields must be private -#pragma warning disable CA2211 // Non-constant fields should not be visible - -namespace Squidex.Infrastructure.Orleans -{ - public static class J - { - public static IJsonSerializer DefaultSerializer; - - public static J AsJ(this T value) - { - return new J(value); - } - - public static J Of(T value) - { - return value; - } - - public static Task> AsTask(T value) - { - return Task.FromResult>(value); - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Orleans/JsonSerializer.cs b/backend/src/Squidex.Infrastructure/Orleans/JsonSerializer.cs new file mode 100644 index 000000000..5937b00ff --- /dev/null +++ b/backend/src/Squidex.Infrastructure/Orleans/JsonSerializer.cs @@ -0,0 +1,46 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Orleans.Serialization; +using Squidex.Infrastructure.Json; + +namespace Squidex.Infrastructure.Orleans +{ + public sealed class JsonSerializer : IExternalSerializer + { + private readonly IJsonSerializer jsonSerializer; + + public JsonSerializer(IJsonSerializer jsonSerializer) + { + this.jsonSerializer = jsonSerializer; + } + + public bool IsSupportedType(Type itemType) + { + return itemType.Namespace?.StartsWith("Squidex", StringComparison.OrdinalIgnoreCase) == true; + } + + public object DeepCopy(object source, ICopyContext context) + { + return source; + } + + public object Deserialize(Type expectedType, IDeserializationContext context) + { + var stream = new StreamReaderWrapper(context.StreamReader); + + return jsonSerializer.Deserialize(stream, expectedType); + } + + public void Serialize(object item, ISerializationContext context, Type expectedType) + { + var stream = new StreamWriterWrapper(context.StreamWriter); + + jsonSerializer.Serialize(item, stream); + } + } +} diff --git a/backend/src/Squidex.Infrastructure/Orleans/J{T}.cs b/backend/src/Squidex.Infrastructure/Orleans/J{T}.cs deleted file mode 100644 index 26bbe9214..000000000 --- a/backend/src/Squidex.Infrastructure/Orleans/J{T}.cs +++ /dev/null @@ -1,82 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Microsoft.Extensions.DependencyInjection; -using Orleans.CodeGeneration; -using Orleans.Concurrency; -using Orleans.Serialization; -using Squidex.Infrastructure.Json; - -#pragma warning disable IDE0060 // Remove unused parameter - -namespace Squidex.Infrastructure.Orleans -{ - [Immutable] - public readonly struct J - { - public T Value { get; } - - public J(T value) - { - Value = value; - } - - public static implicit operator T(J value) - { - return value.Value; - } - - public static implicit operator J(T d) - { - return new J(d); - } - - public override string ToString() - { - return Value?.ToString() ?? string.Empty; - } - - public static Task> AsTask(T value) - { - return Task.FromResult>(value); - } - - [CopierMethod] - public static object? Copy(object? input, ICopyContext? context) - { - return input; - } - - [SerializerMethod] - public static void Serialize(object? input, ISerializationContext context, Type? expected) - { - var stream = new StreamWriterWrapper(context.StreamWriter); - - GetSerializer(context).Serialize(input, stream); - } - - [DeserializerMethod] - public static object? Deserialize(Type expected, IDeserializationContext context) - { - var stream = new StreamReaderWrapper(context.StreamReader); - - return GetSerializer(context).Deserialize(stream, expected); - } - - private static IJsonSerializer GetSerializer(ISerializerContext context) - { - try - { - return context?.ServiceProvider?.GetRequiredService() ?? J.DefaultSerializer; - } - catch - { - return J.DefaultSerializer; - } - } - } -} diff --git a/backend/src/Squidex.Infrastructure/States/BatchContext.cs b/backend/src/Squidex.Infrastructure/States/BatchContext.cs index c5724e987..d64a5c827 100644 --- a/backend/src/Squidex.Infrastructure/States/BatchContext.cs +++ b/backend/src/Squidex.Infrastructure/States/BatchContext.cs @@ -16,24 +16,24 @@ namespace Squidex.Infrastructure.States private static readonly List> EmptyStream = new List>(); private readonly Type owner; private readonly ISnapshotStore snapshotStore; + private readonly IEventFormatter eventDataFormatter; private readonly IEventStore eventStore; - private readonly IEventDataFormatter eventDataFormatter; - private readonly IStreamNameResolver streamNameResolver; + private readonly IEventStreamNames eventStreams; private readonly Dictionary>)> @events = new Dictionary>)>(); private Dictionary>? snapshots; internal BatchContext( Type owner, - ISnapshotStore snapshotStore, + IEventFormatter eventDataFormatter, IEventStore eventStore, - IEventDataFormatter eventDataFormatter, - IStreamNameResolver streamNameResolver) + IEventStreamNames streamNameResolver, + ISnapshotStore snapshotStore) { this.owner = owner; this.snapshotStore = snapshotStore; this.eventStore = eventStore; this.eventDataFormatter = eventDataFormatter; - this.streamNameResolver = streamNameResolver; + this.eventStreams = streamNameResolver; } internal void Add(DomainId key, T snapshot, long version) @@ -48,7 +48,7 @@ namespace Squidex.Infrastructure.States public async Task LoadAsync(IEnumerable ids) { - var streamNames = ids.ToDictionary(x => x, x => streamNameResolver.GetStreamName(owner, x.ToString())); + var streamNames = ids.ToDictionary(x => x, x => eventStreams.GetStreamName(owner, x.ToString())); if (streamNames.Count == 0) { diff --git a/backend/src/Squidex.Infrastructure/States/DefaultStreamNameResolver.cs b/backend/src/Squidex.Infrastructure/States/DefaultEventStreamNames.cs similarity index 91% rename from backend/src/Squidex.Infrastructure/States/DefaultStreamNameResolver.cs rename to backend/src/Squidex.Infrastructure/States/DefaultEventStreamNames.cs index 039963583..b2a252571 100644 --- a/backend/src/Squidex.Infrastructure/States/DefaultStreamNameResolver.cs +++ b/backend/src/Squidex.Infrastructure/States/DefaultEventStreamNames.cs @@ -9,7 +9,7 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Infrastructure.States { - public sealed class DefaultStreamNameResolver : IStreamNameResolver + public sealed class DefaultEventStreamNames : IEventStreamNames { private static readonly string[] Suffixes = { "Grain", "DomainObject", "State" }; diff --git a/backend/src/Squidex.Infrastructure/States/IBatchContext.cs b/backend/src/Squidex.Infrastructure/States/IBatchContext.cs index fcc46b8ca..204817537 100644 --- a/backend/src/Squidex.Infrastructure/States/IBatchContext.cs +++ b/backend/src/Squidex.Infrastructure/States/IBatchContext.cs @@ -13,4 +13,4 @@ namespace Squidex.Infrastructure.States Task LoadAsync(IEnumerable ids); } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Infrastructure/States/IStreamNameResolver.cs b/backend/src/Squidex.Infrastructure/States/IEventStreamNames.cs similarity index 92% rename from backend/src/Squidex.Infrastructure/States/IStreamNameResolver.cs rename to backend/src/Squidex.Infrastructure/States/IEventStreamNames.cs index 43bc855cd..d4a2d8c62 100644 --- a/backend/src/Squidex.Infrastructure/States/IStreamNameResolver.cs +++ b/backend/src/Squidex.Infrastructure/States/IEventStreamNames.cs @@ -7,7 +7,7 @@ namespace Squidex.Infrastructure.States { - public interface IStreamNameResolver + public interface IEventStreamNames { string GetStreamName(Type aggregateType, string id); } diff --git a/backend/src/Squidex.Infrastructure/States/Persistence.cs b/backend/src/Squidex.Infrastructure/States/Persistence.cs index ed6cc4ba2..72eb21db9 100644 --- a/backend/src/Squidex.Infrastructure/States/Persistence.cs +++ b/backend/src/Squidex.Infrastructure/States/Persistence.cs @@ -14,13 +14,13 @@ namespace Squidex.Infrastructure.States internal sealed class Persistence : IPersistence { private readonly DomainId ownerKey; - private readonly ISnapshotStore snapshotStore; - private readonly IEventStore eventStore; - private readonly IEventDataFormatter eventDataFormatter; - private readonly PersistenceMode persistenceMode; - private readonly HandleSnapshot? applyState; private readonly HandleEvent? applyEvent; + private readonly HandleSnapshot? applyState; + private readonly IEventFormatter eventFormatter; + private readonly IEventStore eventStore; + private readonly ISnapshotStore snapshotStore; private readonly Lazy streamName; + private readonly PersistenceMode persistenceMode; private long versionSnapshot = EtagVersion.Empty; private long versionEvents = EtagVersion.Empty; private long version = EtagVersion.Empty; @@ -46,23 +46,23 @@ namespace Squidex.Infrastructure.States } public Persistence(DomainId ownerKey, Type ownerType, - ISnapshotStore snapshotStore, - IEventStore eventStore, - IEventDataFormatter eventDataFormatter, - IStreamNameResolver streamNameResolver, PersistenceMode persistenceMode, + IEventFormatter eventFormatter, + IEventStore eventStore, + IEventStreamNames eventStreams, + ISnapshotStore snapshotStore, HandleSnapshot? applyState, HandleEvent? applyEvent) { - this.ownerKey = ownerKey; - this.applyState = applyState; this.applyEvent = applyEvent; + this.applyState = applyState; + this.eventFormatter = eventFormatter; this.eventStore = eventStore; - this.eventDataFormatter = eventDataFormatter; + this.ownerKey = ownerKey; this.persistenceMode = persistenceMode; this.snapshotStore = snapshotStore; - streamName = new Lazy(() => streamNameResolver.GetStreamName(ownerType, ownerKey.ToString()!)); + streamName = new Lazy(() => eventStreams.GetStreamName(ownerType, ownerKey.ToString()!)); } public async Task DeleteAsync( @@ -74,6 +74,8 @@ namespace Squidex.Infrastructure.States { await snapshotStore.RemoveAsync(ownerKey, ct); } + + versionSnapshot = EtagVersion.Empty; } if (UseEventSourcing) @@ -82,7 +84,11 @@ namespace Squidex.Infrastructure.States { await eventStore.DeleteStreamAsync(streamName.Value, ct); } + + versionEvents = EtagVersion.Empty; } + + UpdateVersion(); } public async Task ReadAsync(long expectedVersion = EtagVersion.Any, @@ -153,7 +159,7 @@ namespace Squidex.Infrastructure.States if (!isStopped) { - var parsedEvent = eventDataFormatter.ParseIfKnown(@event); + var parsedEvent = eventFormatter.ParseIfKnown(@event); if (applyEvent != null && parsedEvent != null) { @@ -210,7 +216,7 @@ namespace Squidex.Infrastructure.States } var eventCommitId = Guid.NewGuid(); - var eventData = events.Select(x => eventDataFormatter.ToEventData(x, eventCommitId, true)).ToArray(); + var eventData = events.Select(x => eventFormatter.ToEventData(x, eventCommitId, true)).ToArray(); try { diff --git a/backend/src/Squidex.Infrastructure/States/Store.cs b/backend/src/Squidex.Infrastructure/States/Store.cs index 194843e34..650f65850 100644 --- a/backend/src/Squidex.Infrastructure/States/Store.cs +++ b/backend/src/Squidex.Infrastructure/States/Store.cs @@ -11,21 +11,21 @@ namespace Squidex.Infrastructure.States { public sealed class Store : IStore { - private readonly IStreamNameResolver streamNameResolver; - private readonly ISnapshotStore snapshotStore; + private readonly IEventFormatter eventFormatter; private readonly IEventStore eventStore; - private readonly IEventDataFormatter eventDataFormatter; + private readonly IEventStreamNames eventStreamNames; + private readonly ISnapshotStore snapshotStore; public Store( - ISnapshotStore snapshotStore, + IEventFormatter eventFormatter, IEventStore eventStore, - IEventDataFormatter eventDataFormatter, - IStreamNameResolver streamNameResolver) + IEventStreamNames eventStreamNames, + ISnapshotStore snapshotStore) { - this.snapshotStore = snapshotStore; + this.eventFormatter = eventFormatter; this.eventStore = eventStore; - this.eventDataFormatter = eventDataFormatter; - this.streamNameResolver = streamNameResolver; + this.eventStreamNames = eventStreamNames; + this.snapshotStore = snapshotStore; } public Task ClearSnapshotsAsync() @@ -36,10 +36,10 @@ namespace Squidex.Infrastructure.States public IBatchContext WithBatchContext(Type owner) { return new BatchContext(owner, - snapshotStore, + eventFormatter, eventStore, - eventDataFormatter, - streamNameResolver); + eventStreamNames, + snapshotStore); } public IPersistence WithEventSourcing(Type owner, DomainId key, HandleEvent? applyEvent) @@ -62,11 +62,11 @@ namespace Squidex.Infrastructure.States Guard.NotNull(key); return new Persistence(key, owner, - snapshotStore, - eventStore, - eventDataFormatter, - streamNameResolver, mode, + eventFormatter, + eventStore, + eventStreamNames, + snapshotStore, applySnapshot, applyEvent); } diff --git a/backend/src/Squidex.Web/Pipeline/ActionContextLogAppender.cs b/backend/src/Squidex.Web/Pipeline/ActionContextLogAppender.cs index e28772c28..db2eea568 100644 --- a/backend/src/Squidex.Web/Pipeline/ActionContextLogAppender.cs +++ b/backend/src/Squidex.Web/Pipeline/ActionContextLogAppender.cs @@ -8,35 +8,34 @@ using System.Diagnostics; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.Extensions.DependencyInjection; using Squidex.Log; namespace Squidex.Web.Pipeline { public sealed class ActionContextLogAppender : ILogAppender { - private readonly IServiceProvider services; + private readonly IHttpContextAccessor httpContextAccessor; + private readonly IActionContextAccessor actionContextAccessor; - public ActionContextLogAppender(IServiceProvider services) + public ActionContextLogAppender(IHttpContextAccessor httpContextAccessor, IActionContextAccessor actionContextAccessor) { - this.services = services; + this.httpContextAccessor = httpContextAccessor; + this.actionContextAccessor = actionContextAccessor; } public void Append(IObjectWriter writer, SemanticLogLevel logLevel, Exception? exception) { - var httpContextAccessor = services.GetService(); + var httpContext = httpContextAccessor.HttpContext; - if (string.IsNullOrEmpty(httpContextAccessor?.HttpContext?.Request?.Method)) + if (string.IsNullOrEmpty(httpContext?.Request?.Method)) { return; } - var actionContext = services.GetRequiredService()?.ActionContext; + var actionContext = actionContextAccessor.ActionContext; try { - var httpContext = httpContextAccessor.HttpContext; - if (string.IsNullOrEmpty(httpContext?.Request?.Method)) { return; diff --git a/backend/src/Squidex/Areas/IdentityServer/Config/TokenStoreInitializer.cs b/backend/src/Squidex/Areas/IdentityServer/Config/TokenStoreInitializer.cs index 15bc5cb8f..cc3571b6b 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Config/TokenStoreInitializer.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Config/TokenStoreInitializer.cs @@ -21,11 +21,9 @@ namespace Squidex.Areas.IdentityServer.Config private readonly IServiceProvider serviceProvider; private CompletionTimer timer; - public TokenStoreInitializer(IOptions options, - IServiceProvider serviceProvider) + public TokenStoreInitializer(IOptions options, IServiceProvider serviceProvider) { this.options = options.Value; - this.serviceProvider = serviceProvider; } diff --git a/backend/src/Squidex/Config/Domain/AppsServices.cs b/backend/src/Squidex/Config/Domain/AppsServices.cs index 60d49f27d..18b2aa681 100644 --- a/backend/src/Squidex/Config/Domain/AppsServices.cs +++ b/backend/src/Squidex/Config/Domain/AppsServices.cs @@ -10,7 +10,6 @@ using Squidex.Areas.Api.Controllers.UI; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Apps; -using Squidex.Domain.Apps.Entities.Apps.DomainObject; using Squidex.Domain.Apps.Entities.History; using Squidex.Domain.Apps.Entities.Search; using Squidex.Infrastructure.Collections; @@ -28,9 +27,6 @@ namespace Squidex.Config.Domain .As(); } - services.AddTransientAs() - .AsSelf(); - services.AddSingletonAs() .AsSelf(); diff --git a/backend/src/Squidex/Config/Domain/AssetServices.cs b/backend/src/Squidex/Config/Domain/AssetServices.cs index 91007e2fb..3b3a8eb3b 100644 --- a/backend/src/Squidex/Config/Domain/AssetServices.cs +++ b/backend/src/Squidex/Config/Domain/AssetServices.cs @@ -10,7 +10,6 @@ using MongoDB.Driver.GridFS; using Squidex.Assets; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Assets; -using Squidex.Domain.Apps.Entities.Assets.DomainObject; using Squidex.Domain.Apps.Entities.Assets.Queries; using Squidex.Domain.Apps.Entities.History; using Squidex.Domain.Apps.Entities.Search; @@ -40,12 +39,6 @@ namespace Squidex.Config.Domain .As(); } - services.AddTransientAs() - .AsSelf(); - - services.AddTransientAs() - .AsSelf(); - services.AddSingletonAs() .AsSelf(); diff --git a/backend/src/Squidex/Config/Domain/CommandsServices.cs b/backend/src/Squidex/Config/Domain/CommandsServices.cs index 18cd7c887..dc650b5b3 100644 --- a/backend/src/Squidex/Config/Domain/CommandsServices.cs +++ b/backend/src/Squidex/Config/Domain/CommandsServices.cs @@ -117,6 +117,9 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As(); + + services.AddSingletonAs() + .As(); } } } diff --git a/backend/src/Squidex/Config/Domain/ContentsServices.cs b/backend/src/Squidex/Config/Domain/ContentsServices.cs index 04fdef488..941efd7d4 100644 --- a/backend/src/Squidex/Config/Domain/ContentsServices.cs +++ b/backend/src/Squidex/Config/Domain/ContentsServices.cs @@ -10,7 +10,6 @@ using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Apps.Templates; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents.Counter; -using Squidex.Domain.Apps.Entities.Contents.DomainObject; using Squidex.Domain.Apps.Entities.Contents.Queries; using Squidex.Domain.Apps.Entities.Contents.Queries.Steps; using Squidex.Domain.Apps.Entities.Contents.Text; @@ -38,9 +37,6 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .AsSelf(); - services.AddTransientAs() - .AsSelf(); - services.AddTransientAs() .As(); diff --git a/backend/src/Squidex/Config/Domain/EventSourcingServices.cs b/backend/src/Squidex/Config/Domain/EventSourcingServices.cs index 2c6e86ad5..436dc1e2a 100644 --- a/backend/src/Squidex/Config/Domain/EventSourcingServices.cs +++ b/backend/src/Squidex/Config/Domain/EventSourcingServices.cs @@ -6,7 +6,6 @@ // ========================================================================== using EventStore.Client; -using MongoDB.Driver; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Diagnostics; using Squidex.Infrastructure.EventSourcing; @@ -56,18 +55,14 @@ namespace Squidex.Config.Domain services.AddTransientAs() .AsSelf(); - services.AddSingletonAs() - .As(); + services.AddSingletonAs() + .As(); - services.AddSingletonAs() - .As(); + services.AddSingletonAs() + .As(); - services.AddSingletonAs(c => - { - var allEventConsumers = c.GetServices(); - - return new EventConsumerFactory(n => allEventConsumers.First(x => x.Name == n)); - }); + services.AddSingletonAs() + .As(); } } } diff --git a/backend/src/Squidex/Config/Domain/RuleServices.cs b/backend/src/Squidex/Config/Domain/RuleServices.cs index ba3c76864..b99979298 100644 --- a/backend/src/Squidex/Config/Domain/RuleServices.cs +++ b/backend/src/Squidex/Config/Domain/RuleServices.cs @@ -14,7 +14,6 @@ using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Comments; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Rules; -using Squidex.Domain.Apps.Entities.Rules.DomainObject; using Squidex.Domain.Apps.Entities.Rules.Queries; using Squidex.Domain.Apps.Entities.Rules.Runner; using Squidex.Domain.Apps.Entities.Rules.UsageTracking; @@ -32,9 +31,6 @@ namespace Squidex.Config.Domain services.Configure(config, "rules"); - services.AddTransientAs() - .AsSelf(); - services.AddSingletonAs() .As(); diff --git a/backend/src/Squidex/Config/Domain/SchemasServices.cs b/backend/src/Squidex/Config/Domain/SchemasServices.cs index 8816b6f8f..0ad86e2f5 100644 --- a/backend/src/Squidex/Config/Domain/SchemasServices.cs +++ b/backend/src/Squidex/Config/Domain/SchemasServices.cs @@ -1,4 +1,4 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) @@ -7,7 +7,6 @@ using Squidex.Domain.Apps.Entities.History; using Squidex.Domain.Apps.Entities.Schemas; -using Squidex.Domain.Apps.Entities.Schemas.DomainObject; using Squidex.Domain.Apps.Entities.Search; namespace Squidex.Config.Domain @@ -16,9 +15,6 @@ namespace Squidex.Config.Domain { public static void AddSquidexSchemas(this IServiceCollection services) { - services.AddTransientAs() - .AsSelf(); - services.AddTransientAs() .As(); @@ -26,4 +22,4 @@ namespace Squidex.Config.Domain .As(); } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex/Config/Orleans/OrleansServices.cs b/backend/src/Squidex/Config/Orleans/OrleansServices.cs index f2e4e294f..99dd26f1b 100644 --- a/backend/src/Squidex/Config/Orleans/OrleansServices.cs +++ b/backend/src/Squidex/Config/Orleans/OrleansServices.cs @@ -13,11 +13,11 @@ using Orleans.Configuration; using Orleans.Hosting; using Orleans.Providers.MongoDB.Configuration; using Orleans.Providers.MongoDB.Utils; +using Orleans.Runtime; using OrleansDashboard; using Squidex.Domain.Apps.Entities; using Squidex.Hosting.Configuration; using Squidex.Infrastructure; -using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Orleans; using Squidex.Web; @@ -42,10 +42,7 @@ namespace Squidex.Config.Orleans services.AddScopedAs() .As(); - services.AddInitializer("Serializer (Orleans)", serializer => - { - J.DefaultSerializer = serializer; - }, -1); + services.AddScoped(x => x.GetRequiredService().GrainIdentity); }); builder.ConfigureApplicationParts(parts => @@ -54,6 +51,11 @@ namespace Squidex.Config.Orleans parts.AddApplicationPart(SquidexInfrastructure.Assembly); }); + builder.Configure(options => + { + options.SerializationProviders.Add(typeof(JsonSerializer)); + }); + builder.Configure(options => { options.TurnWarningLengthThreshold = TimeSpan.FromSeconds(5); @@ -75,10 +77,12 @@ namespace Squidex.Config.Orleans options.HostSelf = false; }); - builder.AddOutgoingGrainCallFilter(); + builder.AddOutgoingGrainCallFilter(); + builder.AddOutgoingGrainCallFilter(); builder.AddIncomingGrainCallFilter(); - builder.AddIncomingGrainCallFilter(); + builder.AddIncomingGrainCallFilter(); builder.AddIncomingGrainCallFilter(); + builder.AddIncomingGrainCallFilter(); builder.AddIncomingGrainCallFilter(); builder.AddIncomingGrainCallFilter(); builder.AddIncomingGrainCallFilter(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppPermanentDeleterTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppPermanentDeleterTests.cs index b75bc933e..1d0fc3ed4 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppPermanentDeleterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppPermanentDeleterTests.cs @@ -12,7 +12,6 @@ using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Events.Apps; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; -using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Reflection; using Xunit; @@ -117,7 +116,7 @@ namespace Squidex.Domain.Apps.Entities.Apps var grain = A.Fake(); A.CallTo(() => grain.GetStateAsync()) - .Returns(app.AsJ()); + .Returns(app); A.CallTo(() => grainFactory.GetGrain(app.Id.ToString(), null)) .Returns(grain); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs index c2897ffdd..cfc6effb6 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using FakeItEasy; +using Orleans.Core; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Orleans; using Xunit; @@ -14,50 +15,51 @@ namespace Squidex.Domain.Apps.Entities.Apps { public sealed class AppUISettingsGrainTests { - private readonly IGrainState grainState = A.Fake>(); + private readonly IGrainIdentity identity = A.Fake(); + private readonly IGrainState state = A.Fake>(); private readonly AppUISettingsGrain sut; public AppUISettingsGrainTests() { - sut = new AppUISettingsGrain(grainState); + sut = new AppUISettingsGrain(identity, state); } [Fact] public async Task Should_set_setting() { - await sut.SetAsync(new JsonObject().Add("key", 15).AsJ()); + await sut.SetAsync(new JsonObject().Add("key", 15)); var actual = await sut.GetAsync(); var expected = new JsonObject().Add("key", 15); - Assert.Equal(expected.ToString(), actual.Value.ToString()); + Assert.Equal(expected.ToString(), actual.ToString()); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappened(); } [Fact] public async Task Should_set_root_value() { - await sut.SetAsync("key", JsonValue.Create(123).AsJ()); + await sut.SetAsync("key", JsonValue.Create(123)); var actual = await sut.GetAsync(); var expected = new JsonObject().Add("key", 123); - Assert.Equal(expected.ToString(), actual.Value.ToString()); + Assert.Equal(expected.ToString(), actual.ToString()); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappened(); } [Fact] public async Task Should_remove_root_value() { - await sut.SetAsync("key", JsonValue.Create(123).AsJ()); + await sut.SetAsync("key", JsonValue.Create(123)); await sut.RemoveAsync("key"); @@ -65,16 +67,16 @@ namespace Squidex.Domain.Apps.Entities.Apps var expected = new JsonObject(); - Assert.Equal(expected.ToString(), actual.Value.ToString()); + Assert.Equal(expected.ToString(), actual.ToString()); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedTwiceExactly(); } [Fact] public async Task Should_set_nested_value() { - await sut.SetAsync("root.nested", JsonValue.Create(123).AsJ()); + await sut.SetAsync("root.nested", JsonValue.Create(123)); var actual = await sut.GetAsync(); @@ -82,16 +84,16 @@ namespace Squidex.Domain.Apps.Entities.Apps new JsonObject().Add("root", new JsonObject().Add("nested", 123)); - Assert.Equal(expected.ToString(), actual.Value.ToString()); + Assert.Equal(expected.ToString(), actual.ToString()); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappened(); } [Fact] public async Task Should_remove_nested_value() { - await sut.SetAsync("root.nested", JsonValue.Create(123).AsJ()); + await sut.SetAsync("root.nested", JsonValue.Create(123)); await sut.RemoveAsync("root.nested"); @@ -101,18 +103,18 @@ namespace Squidex.Domain.Apps.Entities.Apps new JsonObject().Add("root", new JsonObject()); - Assert.Equal(expected.ToString(), actual.Value.ToString()); + Assert.Equal(expected.ToString(), actual.ToString()); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedTwiceExactly(); } [Fact] public async Task Should_throw_exception_if_nested_not_an_object() { - await sut.SetAsync("root.nested", JsonValue.Create(123).AsJ()); + await sut.SetAsync("root.nested", JsonValue.Create(123)); - await Assert.ThrowsAsync(() => sut.SetAsync("root.nested.value", JsonValue.Create(123).AsJ())); + await Assert.ThrowsAsync(() => sut.SetAsync("root.nested.value", JsonValue.Create(123))); } [Fact] @@ -120,7 +122,7 @@ namespace Squidex.Domain.Apps.Entities.Apps { await sut.RemoveAsync("root.nested"); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustNotHaveHappened(); } @@ -129,7 +131,7 @@ namespace Squidex.Domain.Apps.Entities.Apps { await sut.RemoveAsync("root"); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustNotHaveHappened(); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsTests.cs index 2c767f22a..6b94aa74b 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsTests.cs @@ -10,7 +10,6 @@ using Orleans; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; -using Squidex.Infrastructure.Orleans; using Xunit; namespace Squidex.Domain.Apps.Entities.Apps @@ -35,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Apps var settings = new JsonObject(); A.CallTo(() => grain.GetAsync()) - .Returns(settings.AsJ()); + .Returns(settings); var result = await sut.GetAsync(DomainId.NewGuid(), "user"); @@ -49,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Apps await sut.SetAsync(DomainId.NewGuid(), "user", "the.path", value); - A.CallTo(() => grain.SetAsync("the.path", A>.That.Matches(x => x.Value == value))) + A.CallTo(() => grain.SetAsync("the.path", value)) .MustHaveHappened(); } @@ -60,7 +59,7 @@ namespace Squidex.Domain.Apps.Entities.Apps await sut.SetAsync(DomainId.NewGuid(), "user", value); - A.CallTo(() => grain.SetAsync(A>.That.Matches(x => x.Value == value))) + A.CallTo(() => grain.SetAsync(value)) .MustHaveHappened(); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppCommandMiddlewareTests.cs index 0a18ddf28..0c5f39f75 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppCommandMiddlewareTests.cs @@ -12,7 +12,6 @@ using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Validation; using Xunit; @@ -90,7 +89,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject var grain = A.Fake(); - A.CallTo(() => grain.ExecuteAsync(A>._)) + A.CallTo(() => grain.ExecuteAsync(A._)) .Returns(new CommandResult(command.AggregateId, 1, 0, result)); A.CallTo(() => grainFactory.GetGrain(command.AggregateId.ToString(), null)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs index 8d4062283..f77a260a4 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using FakeItEasy; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; @@ -67,15 +68,18 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject } }; + var serviceProvider = + new ServiceCollection() + .AddSingleton(initialSettings) + .AddSingleton(appPlansProvider) + .AddSingleton(appPlansBillingManager) + .AddSingleton(userResolver) + .BuildServiceProvider(); + var log = A.Fake>(); - sut = new AppDomainObject(PersistenceFactory, log, - initialSettings, - appPlansProvider, - appPlansBillingManager, - userResolver); #pragma warning disable MA0056 // Do not call overridable members in constructor - sut.Setup(Id); + sut = new AppDomainObject(Id, PersistenceFactory, log, serviceProvider); #pragma warning restore MA0056 // Do not call overridable members in constructor } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsCacheGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsCacheGrainTests.cs index f39e26e48..0bd4b57ed 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsCacheGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsCacheGrainTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using FakeItEasy; +using Orleans.Core; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Entities.Apps.Repositories; using Squidex.Infrastructure; @@ -15,14 +16,17 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes { public class AppsCacheGrainTests { + private readonly IGrainIdentity identity = A.Fake(); private readonly IAppRepository appRepository = A.Fake(); private readonly DomainId appId = DomainId.NewGuid(); private readonly AppsCacheGrain sut; public AppsCacheGrainTests() { - sut = new AppsCacheGrain(appRepository); - sut.ActivateAsync(appId.ToString()).Wait(); + A.CallTo(() => identity.PrimaryKeyString) + .Returns(appId.ToString()); + + sut = new AppsCacheGrain(identity, appRepository); } [Fact] diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs index c8cc562df..d2fa84491 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs @@ -358,7 +358,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes var appGrain = A.Fake(); A.CallTo(() => appGrain.GetStateAsync()) - .Returns(J.Of(app)); + .Returns(app); A.CallTo(() => grainFactory.GetGrain(appId.Id.ToString(), null)) .Returns(appGrain); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/UsageNotifierGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/UsageNotifierGrainTests.cs index 7871428ee..abe7a7828 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/UsageNotifierGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/UsageNotifierGrainTests.cs @@ -7,6 +7,7 @@ using FakeItEasy; using NodaTime; +using Orleans.Core; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Entities.Notifications; using Squidex.Infrastructure.Orleans; @@ -17,10 +18,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans { public class UsageNotifierGrainTests { + private readonly IClock clock = A.Fake(); + private readonly IGrainIdentity identity = A.Fake(); private readonly IGrainState state = A.Fake>(); private readonly INotificationSender notificationSender = A.Fake(); private readonly IUserResolver userResolver = A.Fake(); - private readonly IClock clock = A.Fake(); private readonly UsageNotifierGrain sut; private Instant time = SystemClock.Instance.GetCurrentInstant(); @@ -32,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans A.CallTo(() => notificationSender.IsActive) .Returns(true); - sut = new UsageNotifierGrain(state, notificationSender, userResolver, clock); + sut = new UsageNotifierGrain(identity, state, notificationSender, userResolver, clock); } [Fact] diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsFluidExtensionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsFluidExtensionTests.cs index 01789b514..b71d953d9 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsFluidExtensionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsFluidExtensionTests.cs @@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Assets public AssetsFluidExtensionTests() { - var services = + var serviceProvider = new ServiceCollection() .AddSingleton(appProvider) .AddSingleton(assetFileStore) @@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.Assets var extensions = new IFluidExtension[] { new ContentFluidExtension(), - new AssetsFluidExtension(services) + new AssetsFluidExtension(serviceProvider) }; A.CallTo(() => appProvider.GetAppAsync(appId.Id, false, default)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsJintExtensionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsJintExtensionTests.cs index e8136491d..af425b6cd 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsJintExtensionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsJintExtensionTests.cs @@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Assets public AssetsJintExtensionTests() { - var services = + var serviceProvider = new ServiceCollection() .AddSingleton(appProvider) .AddSingleton(assetFileStore) @@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Assets var extensions = new IJintExtension[] { - new AssetsJintExtension(services) + new AssetsJintExtension(serviceProvider) }; A.CallTo(() => appProvider.GetAppAsync(appId.Id, false, A._)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs index a95ba7305..9a920429b 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs @@ -12,7 +12,6 @@ using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; using Xunit; namespace Squidex.Domain.Apps.Entities.Assets.DomainObject @@ -248,7 +247,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject var grain = A.Fake(); - A.CallTo(() => grain.ExecuteAsync(A>._)) + A.CallTo(() => grain.ExecuteAsync(A._)) .Returns(new CommandResult(command.AggregateId, 1, 0, result)); A.CallTo(() => grainFactory.GetGrain(command.AggregateId.ToString(), null)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectGrainTests.cs index 69954b24f..ccbce1bd9 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectGrainTests.cs @@ -6,6 +6,9 @@ // ========================================================================== using FakeItEasy; +using Orleans.Core; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; using Xunit; @@ -20,12 +23,19 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject [Fact] public void Should_set_limit() { - var serviceProvider = A.Fake(); + var id = DomainId.NewGuid(); - A.CallTo(() => serviceProvider.GetService(typeof(AssetDomainObject))) + var identity = A.Fake(); + + A.CallTo(() => identity.PrimaryKeyString) + .Returns(id.ToString()); + + var factory = A.Fake(); + + A.CallTo(() => factory.Create(id)) .Returns(A.Dummy()); - new AssetDomainObjectGrain(serviceProvider, limit); + new AssetDomainObjectGrain(identity, factory, limit); A.CallTo(() => limit.SetLimit(5000, TimeSpan.FromMinutes(5))) .MustHaveHappened(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectTests.cs index 0f4f345d7..35e7e29b8 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectTests.cs @@ -78,9 +78,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject .AddSingleton(tagService) .BuildServiceProvider(); - sut = new AssetDomainObject(PersistenceFactory, log, serviceProvider); #pragma warning disable MA0056 // Do not call overridable members in constructor - sut.Setup(Id); + sut = new AssetDomainObject(Id, PersistenceFactory, log, serviceProvider); #pragma warning restore MA0056 // Do not call overridable members in constructor } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetFolderDomainObjectGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetFolderDomainObjectGrainTests.cs index 2095d0dbd..e0ac9e6af 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetFolderDomainObjectGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetFolderDomainObjectGrainTests.cs @@ -6,6 +6,9 @@ // ========================================================================== using FakeItEasy; +using Orleans.Core; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; using Xunit; @@ -20,12 +23,19 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject [Fact] public void Should_set_limit() { - var serviceProvider = A.Fake(); + var id = DomainId.NewGuid(); - A.CallTo(() => serviceProvider.GetService(typeof(AssetFolderDomainObject))) + var identity = A.Fake(); + + A.CallTo(() => identity.PrimaryKeyString) + .Returns(id.ToString()); + + var factory = A.Fake(); + + A.CallTo(() => factory.Create(id)) .Returns(A.Dummy()); - new AssetFolderDomainObjectGrain(serviceProvider, limit); + new AssetFolderDomainObjectGrain(identity, factory, limit); A.CallTo(() => limit.SetLimit(5000, TimeSpan.FromMinutes(5))) .MustHaveHappened(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetFolderDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetFolderDomainObjectTests.cs index e2067192c..a1f851aaa 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetFolderDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetFolderDomainObjectTests.cs @@ -53,9 +53,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.DomainObject .AddSingleton(log) .BuildServiceProvider(); - sut = new AssetFolderDomainObject(PersistenceFactory, log, serviceProvider); #pragma warning disable MA0056 // Do not call overridable members in constructor - sut.Setup(Id); + sut = new AssetFolderDomainObject(Id, PersistenceFactory, log, serviceProvider); #pragma warning restore MA0056 // Do not call overridable members in constructor } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetLoaderTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetLoaderTests.cs index f07cf3aae..2f0892824 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetLoaderTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetLoaderTests.cs @@ -9,7 +9,6 @@ using FakeItEasy; using Orleans; using Squidex.Domain.Apps.Entities.Assets.DomainObject; using Squidex.Infrastructure; -using Squidex.Infrastructure.Orleans; using Xunit; namespace Squidex.Domain.Apps.Entities.Assets.Queries @@ -36,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries public async Task Should_return_null_if_no_state_returned() { A.CallTo(() => grain.GetStateAsync(10)) - .Returns(J.Of(null!)); + .Returns(Task.FromResult(null!)); Assert.Null(await sut.GetAsync(appId, id, 10)); } @@ -44,10 +43,10 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries [Fact] public async Task Should_return_null_if_state_empty() { - var content = new AssetEntity { Version = EtagVersion.Empty }; + var asset = new AssetEntity { Version = EtagVersion.Empty }; A.CallTo(() => grain.GetStateAsync(10)) - .Returns(J.Of(content)); + .Returns(asset); Assert.Null(await sut.GetAsync(appId, id, 10)); } @@ -55,10 +54,10 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries [Fact] public async Task Should_return_null_if_state_has_other_version() { - var content = new AssetEntity { Version = 5 }; + var asset = new AssetEntity { Version = 5 }; A.CallTo(() => grain.GetStateAsync(10)) - .Returns(J.Of(content)); + .Returns(asset); Assert.Null(await sut.GetAsync(appId, id, 10)); } @@ -66,14 +65,14 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries [Fact] public async Task Should_return_content_from_state() { - var content = new AssetEntity { Version = 10 }; + var asset = new AssetEntity { Version = 10 }; A.CallTo(() => grain.GetStateAsync(10)) - .Returns(J.Of(content)); + .Returns(asset); var result = await sut.GetAsync(appId, id, 10); - Assert.Same(content, result); + Assert.Same(asset, result); } } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RepairFilesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RepairFilesTests.cs index db4e7bc1b..047cdcd85 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RepairFilesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RepairFilesTests.cs @@ -17,14 +17,14 @@ namespace Squidex.Domain.Apps.Entities.Assets public class RepairFilesTests { private readonly IEventStore eventStore = A.Fake(); - private readonly IEventDataFormatter eventDataFormatter = A.Fake(); + private readonly IEventFormatter eventFormatter = A.Fake(); private readonly IAssetFileStore assetFileStore = A.Fake(); private readonly NamedId appId = NamedId.Of(DomainId.NewGuid(), "my-app"); private readonly RebuildFiles sut; public RepairFilesTests() { - sut = new RebuildFiles(assetFileStore, eventStore, eventDataFormatter); + sut = new RebuildFiles(assetFileStore, eventFormatter, eventStore); } [Fact] @@ -115,12 +115,12 @@ namespace Squidex.Domain.Apps.Entities.Assets if (@event != null) { - A.CallTo(() => eventDataFormatter.ParseIfKnown(storedEvent)) + A.CallTo(() => eventFormatter.ParseIfKnown(storedEvent)) .Returns(Envelope.Create(@event)); } else { - A.CallTo(() => eventDataFormatter.ParseIfKnown(storedEvent)) + A.CallTo(() => eventFormatter.ParseIfKnown(storedEvent)) .Returns(null); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs index ab7798e33..99c4f9b50 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs @@ -20,9 +20,9 @@ namespace Squidex.Domain.Apps.Entities.Backup { public class BackupReaderWriterTests { - private readonly IStreamNameResolver streamNameResolver = A.Fake(); + private readonly IEventFormatter eventFormatter; + private readonly IEventStreamNames eventStreamNames = A.Fake(); private readonly IJsonSerializer serializer = TestUtils.DefaultSerializer; - private readonly IEventDataFormatter formatter; private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); [TypeName(nameof(MyEvent))] @@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { typeNameRegistry.Map(typeof(MyEvent)); - formatter = new DefaultEventDataFormatter(typeNameRegistry, serializer); + eventFormatter = new DefaultEventFormatter(typeNameRegistry, serializer); } [Fact] @@ -194,7 +194,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { foreach (var (stream, envelope) in sourceEvents) { - var eventData = formatter.ToEventData(envelope, Guid.NewGuid(), true); + var eventData = eventFormatter.ToEventData(envelope, Guid.NewGuid(), true); var eventStored = new StoredEvent(stream, "1", 2, eventData); var index = int.Parse(envelope.Headers["Index"].ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture); @@ -214,7 +214,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { var targetEvents = new List<(string Stream, Envelope Event)>(); - await foreach (var @event in reader.ReadEventsAsync(streamNameResolver, formatter)) + await foreach (var @event in reader.ReadEventsAsync(eventStreamNames, eventFormatter)) { var index = int.Parse(@event.Event.Headers["Index"].ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupServiceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupServiceTests.cs index 7fbadbeaf..757ce0e8c 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupServiceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupServiceTests.cs @@ -58,7 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Backup .Returns(grain); A.CallTo(() => grain.GetStateAsync()) - .Returns(state.AsJ()); + .Returns(state); var result = await sut.GetRestoreAsync(); @@ -93,7 +93,7 @@ namespace Squidex.Domain.Apps.Entities.Backup .Returns(grain); A.CallTo(() => grain.GetStateAsync()) - .Returns(state.AsJ()); + .Returns(state); var result = await sut.GetBackupsAsync(appId); @@ -114,7 +114,7 @@ namespace Squidex.Domain.Apps.Entities.Backup .Returns(grain); A.CallTo(() => grain.GetStateAsync()) - .Returns(state.AsJ()); + .Returns(state); var result1 = await sut.GetBackupAsync(appId, backupId); var result2 = await sut.GetBackupAsync(appId, DomainId.NewGuid()); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsCommandMiddlewareTests.cs index b5f4a53f7..95caf94a1 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsCommandMiddlewareTests.cs @@ -11,7 +11,6 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Entities.Comments.Commands; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; using Squidex.Shared.Users; using Xunit; @@ -47,8 +46,8 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject A.CallTo(() => grainFactory.GetGrain(commentsId.ToString(), null)) .Returns(grain); - A.CallTo(() => grain.ExecuteAsync(A>.That.Matches(x => x.Value == command))) - .Returns(CommandResult.Empty(commentsId, 0, 0).AsJ()); + A.CallTo(() => grain.ExecuteAsync(command)) + .Returns(CommandResult.Empty(commentsId, 0, 0)); var isNextCalled = false; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsGrainTests.cs index 0bcaf78d3..f14f31897 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/DomainObject/CommentsGrainTests.cs @@ -8,6 +8,7 @@ using FakeItEasy; using FluentAssertions; using NodaTime; +using Orleans.Core; using Squidex.Domain.Apps.Core.Comments; using Squidex.Domain.Apps.Entities.Comments.Commands; using Squidex.Domain.Apps.Entities.TestHelpers; @@ -21,8 +22,9 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject { public class CommentsGrainTests { + private readonly IGrainIdentity identity = A.Fake(); + private readonly IEventFormatter eventFormatter = A.Fake(); private readonly IEventStore eventStore = A.Fake(); - private readonly IEventDataFormatter eventDataFormatter = A.Fake(); private readonly DomainId commentsId = DomainId.NewGuid(); private readonly DomainId commentId = DomainId.NewGuid(); private readonly RefToken actor = RefToken.User("me"); @@ -37,11 +39,13 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject public CommentsGrainTests() { + A.CallTo(() => identity.PrimaryKeyString) + .Returns(Id); + A.CallTo(() => eventStore.AppendAsync(A._, A._, A._, A>._, default)) .Invokes(x => LastEvents = sut!.GetUncommittedEvents().Select(x => x.To()).ToList()); - sut = new CommentsGrain(eventStore, eventDataFormatter); - sut.ActivateAsync(Id).Wait(); + sut = new CommentsGrain(identity, eventFormatter, eventStore); } [Fact] @@ -51,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject var result = await sut.ExecuteAsync(CreateCommentsCommand(command)); - result.Value.ShouldBeEquivalent(CommandResult.Empty(commentsId, 0, EtagVersion.Empty)); + result.ShouldBeEquivalent(CommandResult.Empty(commentsId, 0, EtagVersion.Empty)); (await sut.GetCommentsAsync(0)).Should().BeEquivalentTo(new CommentsResult { @@ -82,7 +86,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject var result = await sut.ExecuteAsync(CreateCommentsCommand(updateCommand)); - result.Value.ShouldBeEquivalent(CommandResult.Empty(commentsId, 1, 0)); + result.ShouldBeEquivalent(CommandResult.Empty(commentsId, 1, 0)); (await sut.GetCommentsAsync(-1)).Should().BeEquivalentTo(new CommentsResult { @@ -118,7 +122,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.DomainObject var result = await sut.ExecuteAsync(CreateCommentsCommand(deleteCommand)); - result.Value.ShouldBeEquivalent(CommandResult.Empty(commentsId, 2, 1)); + result.ShouldBeEquivalent(CommandResult.Empty(commentsId, 2, 1)); (await sut.GetCommentsAsync(-1)).Should().BeEquivalentTo(new CommentsResult { diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/WatchingGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/WatchingGrainTests.cs index bad9e860a..e83f907e0 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/WatchingGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Comments/WatchingGrainTests.cs @@ -7,12 +7,14 @@ using FakeItEasy; using NodaTime; +using Orleans.Core; using Xunit; namespace Squidex.Domain.Apps.Entities.Comments { public class WatchingGrainTests { + private readonly IGrainIdentity identity = A.Fake(); private readonly IClock clock = A.Fake(); private readonly WatchingGrain sut; private Instant now = SystemClock.Instance.GetCurrentInstant(); @@ -22,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Comments A.CallTo(() => clock.GetCurrentInstant()) .ReturnsLazily(() => now); - sut = new WatchingGrain(clock); + sut = new WatchingGrain(identity, clock); } [Fact] diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Counter/CounterGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Counter/CounterGrainTests.cs index 923fcdf36..7db4d3d09 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Counter/CounterGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Counter/CounterGrainTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using FakeItEasy; +using Orleans.Core; using Squidex.Infrastructure.Orleans; using Xunit; @@ -13,12 +14,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.Counter { public class CounterGrainTests { - private readonly IGrainState grainState = A.Fake>(); + private readonly IGrainIdentity identity = A.Fake(); + private readonly IGrainState state = A.Fake>(); private readonly CounterGrain sut; public CounterGrainTests() { - sut = new CounterGrain(grainState); + sut = new CounterGrain(identity, state); } [Fact] @@ -30,7 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Counter Assert.Equal(1, await sut.IncrementAsync("Counter2")); Assert.Equal(2, await sut.IncrementAsync("Counter2")); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappened(4, Times.Exactly); } @@ -44,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Counter Assert.Equal(2, await sut.IncrementAsync("Counter1")); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappened(4, Times.Exactly); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentCommandMiddlewareTests.cs index 72447662f..731712d92 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentCommandMiddlewareTests.cs @@ -12,7 +12,6 @@ using Squidex.Domain.Apps.Entities.Contents.Queries; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; using Xunit; namespace Squidex.Domain.Apps.Entities.Contents.DomainObject @@ -94,7 +93,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject var grain = A.Fake(); - A.CallTo(() => grain.ExecuteAsync(A>._)) + A.CallTo(() => grain.ExecuteAsync(A._)) .Returns(new CommandResult(command.AggregateId, 1, 0, result)); A.CallTo(() => grainFactory.GetGrain(command.AggregateId.ToString(), null)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectGrainTests.cs index 1746d0808..fea0b7fed 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectGrainTests.cs @@ -6,6 +6,9 @@ // ========================================================================== using FakeItEasy; +using Orleans.Core; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; using Xunit; @@ -20,12 +23,19 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject [Fact] public void Should_set_limit() { - var serviceProvider = A.Fake(); + var id = DomainId.NewGuid(); - A.CallTo(() => serviceProvider.GetService(typeof(ContentDomainObject))) + var identity = A.Fake(); + + A.CallTo(() => identity.PrimaryKeyString) + .Returns(id.ToString()); + + var factory = A.Fake(); + + A.CallTo(() => factory.Create(id)) .Returns(A.Dummy()); - new ContentDomainObjectGrain(serviceProvider, limit); + new ContentDomainObjectGrain(identity, factory, limit); A.CallTo(() => limit.SetLimit(5000, TimeSpan.FromMinutes(5))) .MustHaveHappened(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs index 3e5fb8a86..597d30d01 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs @@ -119,9 +119,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject .AddSingleton(new DefaultValidatorsFactory()) .BuildServiceProvider(); - sut = new ContentDomainObject(PersistenceFactory, log, serviceProvider); #pragma warning disable MA0056 // Do not call overridable members in constructor - sut.Setup(Id); + sut = new ContentDomainObject(Id, PersistenceFactory, log, serviceProvider); #pragma warning restore MA0056 // Do not call overridable members in constructor } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs index 3cb347087..6b25a3cd4 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs @@ -112,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL A.CallTo(() => appProvider.GetSchemasAsync(TestApp.Default.Id, default)) .Returns(schemas.ToList()); - var services = + var serviceProvider = new ServiceCollection() .AddMemoryCache() .AddTransient() @@ -148,7 +148,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var schemasHash = A.Fake(); - return new CachingGraphQLResolver(cache, schemasHash, services, Options.Create(new GraphQLOptions())); + return new CachingGraphQLResolver(cache, schemasHash, serviceProvider, Options.Create(new GraphQLOptions())); } } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentLoaderTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentLoaderTests.cs index d7f90f151..cffe3e07d 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentLoaderTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentLoaderTests.cs @@ -9,7 +9,6 @@ using FakeItEasy; using Orleans; using Squidex.Domain.Apps.Entities.Contents.DomainObject; using Squidex.Infrastructure; -using Squidex.Infrastructure.Orleans; using Xunit; namespace Squidex.Domain.Apps.Entities.Contents.Queries @@ -36,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries public async Task Should_return_null_if_no_state_returned() { A.CallTo(() => grain.GetStateAsync(10)) - .Returns(J.Of(null!)); + .Returns(Task.FromResult(null!)); Assert.Null(await sut.GetAsync(appId, id, 10)); } @@ -47,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries var content = new ContentEntity { Version = EtagVersion.Empty }; A.CallTo(() => grain.GetStateAsync(10)) - .Returns(J.Of(content)); + .Returns(content); Assert.Null(await sut.GetAsync(appId, id, 10)); } @@ -58,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries var content = new ContentEntity { Version = 5 }; A.CallTo(() => grain.GetStateAsync(10)) - .Returns(J.Of(content)); + .Returns(content); Assert.Null(await sut.GetAsync(appId, id, 10)); } @@ -69,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries var content = new ContentEntity { Version = 5 }; A.CallTo(() => grain.GetStateAsync(EtagVersion.Any)) - .Returns(J.Of(content)); + .Returns(content); await sut.GetAsync(appId, id, EtagVersion.Any); } @@ -80,7 +79,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries var content = new ContentEntity { Version = 10 }; A.CallTo(() => grain.GetStateAsync(10)) - .Returns(J.Of(content)); + .Returns(content); var result = await sut.GetAsync(appId, id, 10); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesFluidExtensionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesFluidExtensionTests.cs index 29df914c0..7b0614650 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesFluidExtensionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesFluidExtensionTests.cs @@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Contents public ReferencesFluidExtensionTests() { - var services = + var serviceProvider = new ServiceCollection() .AddSingleton(appProvider) .AddSingleton(contentQuery) @@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Entities.Contents var extensions = new IFluidExtension[] { new ContentFluidExtension(), - new ReferencesFluidExtension(services) + new ReferencesFluidExtension(serviceProvider) }; A.CallTo(() => appProvider.GetAppAsync(appId.Id, false, default)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesJintExtensionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesJintExtensionTests.cs index b056e806b..79ff535fc 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesJintExtensionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesJintExtensionTests.cs @@ -29,7 +29,7 @@ namespace Squidex.Domain.Apps.Entities.Contents public ReferencesJintExtensionTests() { - var services = + var serviceProvider = new ServiceCollection() .AddSingleton(appProvider) .AddSingleton(contentQuery) @@ -37,7 +37,7 @@ namespace Squidex.Domain.Apps.Entities.Contents var extensions = new IJintExtension[] { - new ReferencesJintExtension(services) + new ReferencesJintExtension(serviceProvider) }; A.CallTo(() => appProvider.GetAppAsync(appId.Id, false, default)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs index 78e9dbf02..3d67510af 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs @@ -11,7 +11,6 @@ using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; using Xunit; namespace Squidex.Domain.Apps.Entities.Rules.DomainObject @@ -93,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject var grain = A.Fake(); - A.CallTo(() => grain.ExecuteAsync(A>._)) + A.CallTo(() => grain.ExecuteAsync(A._)) .Returns(new CommandResult(command.AggregateId, 1, 0, result)); A.CallTo(() => grainFactory.GetGrain(command.AggregateId.ToString(), null)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleDomainObjectTests.cs index 74a9233c4..7f8803a7e 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleDomainObjectTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using FakeItEasy; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.Triggers; @@ -39,9 +40,14 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject { var log = A.Fake>(); - sut = new RuleDomainObject(PersistenceFactory, log, appProvider, ruleEnqueuer); + var serviceProvider = + new ServiceCollection() + .AddSingleton(appProvider) + .AddSingleton(ruleEnqueuer) + .BuildServiceProvider(); + #pragma warning disable MA0056 // Do not call overridable members in constructor - sut.Setup(Id); + sut = new RuleDomainObject(Id, PersistenceFactory, log, serviceProvider); #pragma warning restore MA0056 // Do not call overridable members in constructor } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesCacheGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesCacheGrainTests.cs index fe45886bf..1f654d0e1 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesCacheGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesCacheGrainTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using FakeItEasy; +using Orleans.Core; using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Infrastructure; using Xunit; @@ -14,14 +15,17 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes { public class RulesCacheGrainTests { + private readonly IGrainIdentity identity = A.Fake(); private readonly IRuleRepository ruleRepository = A.Fake(); private readonly DomainId appId = DomainId.NewGuid(); private readonly RulesCacheGrain sut; public RulesCacheGrainTests() { - sut = new RulesCacheGrain(ruleRepository); - sut.ActivateAsync(appId.ToString()).Wait(); + A.CallTo(() => identity.PrimaryKeyString) + .Returns(appId.ToString()); + + sut = new RulesCacheGrain(identity, ruleRepository); } [Fact] diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesIndexTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesIndexTests.cs index 45c23dc02..026c3d008 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesIndexTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesIndexTests.cs @@ -11,7 +11,6 @@ using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.DomainObject; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; using Xunit; namespace Squidex.Domain.Apps.Entities.Rules.Indexes @@ -113,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes var ruleGrain = A.Fake(); A.CallTo(() => ruleGrain.GetStateAsync()) - .Returns(J.Of(ruleEntity)); + .Returns(ruleEntity); var key = DomainId.Combine(appId, ruleId).ToString(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs index ea7d2ef2e..e7fca338c 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs @@ -37,9 +37,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject { var log = A.Fake>(); - sut = new SchemaDomainObject(PersistenceFactory, log); #pragma warning disable MA0056 // Do not call overridable members in constructor - sut.Setup(Id); + sut = new SchemaDomainObject(Id, PersistenceFactory, log); #pragma warning restore MA0056 // Do not call overridable members in constructor } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasCacheGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasCacheGrainTests.cs index 2b9792853..443c22a69 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasCacheGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasCacheGrainTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using FakeItEasy; +using Orleans.Core; using Squidex.Domain.Apps.Entities.Schemas.Repositories; using Squidex.Infrastructure; using Xunit; @@ -14,14 +15,17 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes { public class SchemasCacheGrainTests { + private readonly IGrainIdentity identity = A.Fake(); private readonly ISchemaRepository schemaRepository = A.Fake(); private readonly DomainId appId = DomainId.NewGuid(); private readonly SchemasCacheGrain sut; public SchemasCacheGrainTests() { - sut = new SchemasCacheGrain(schemaRepository); - sut.ActivateAsync(appId.ToString()).Wait(); + A.CallTo(() => identity.PrimaryKeyString) + .Returns(appId.ToString()); + + sut = new SchemasCacheGrain(identity, schemaRepository); } [Fact] diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs index 46a0b4731..f9eff1c84 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs @@ -16,7 +16,6 @@ using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.DomainObject; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Validation; using Xunit; @@ -313,7 +312,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes var schemaGrain = A.Fake(); A.CallTo(() => schemaGrain.GetStateAsync()) - .Returns(J.Of(schemaEntity)); + .Returns(schemaEntity); var key = DomainId.Combine(appId, schemaId.Id).ToString(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs index c7c838f77..c10d5d897 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs @@ -7,6 +7,7 @@ using FakeItEasy; using FluentAssertions; +using Orleans.Core; using Squidex.Domain.Apps.Core.Tags; using Squidex.Infrastructure; using Squidex.Infrastructure.Orleans; @@ -18,17 +19,20 @@ namespace Squidex.Domain.Apps.Entities.Tags { public class TagGrainTests { - private readonly IGrainState grainState = A.Fake>(); + private readonly IGrainIdentity identity = A.Fake(); + private readonly IGrainState state = A.Fake>(); private readonly string id = DomainId.NewGuid().ToString(); private readonly TagGrain sut; public TagGrainTests() { - A.CallTo(() => grainState.ClearAsync()) - .Invokes(() => grainState.Value = new TagGrain.State()); + A.CallTo(() => identity.PrimaryKeyString) + .Returns(id); - sut = new TagGrain(grainState); - sut.ActivateAsync(id).Wait(); + A.CallTo(() => state.ClearAsync()) + .Invokes(() => state.Value = new TagGrain.State()); + + sut = new TagGrain(identity, state); } [Fact] @@ -42,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Tags Assert.Empty(allTags); - A.CallTo(() => grainState.ClearAsync()) + A.CallTo(() => state.ClearAsync()) .MustHaveHappened(); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AssertHelper.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AssertHelper.cs index d4592e885..83dcf20ad 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AssertHelper.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AssertHelper.cs @@ -7,7 +7,6 @@ using FluentAssertions; using Squidex.Infrastructure.EventSourcing; -using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.TestHelpers { @@ -39,10 +38,5 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers { lhs.Should().BeEquivalentTo(rhs, o => o.IncludingProperties()); } - - public static void ShouldBeEquivalent(this J lhs, T rhs) - { - lhs.Value.Should().BeEquivalentTo(rhs, o => o.IncludingProperties()); - } } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs index 13a2dfda0..3a7385158 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs @@ -11,7 +11,6 @@ using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.EventSourcing; -using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.States; using Xunit; @@ -124,11 +123,6 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers return command; } - protected static J J(IAggregateCommand command) - { - return command.AsJ(); - } - protected TEvent CreateEvent(TEvent @event, bool fromClient = false) where TEvent : SquidexEvent { @event.Actor = fromClient ? ActorClient : Actor; diff --git a/backend/tests/Squidex.Infrastructure.Tests/Commands/CommandRequestTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Commands/CommandRequestTests.cs index 0b604a904..382dc4c30 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Commands/CommandRequestTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Commands/CommandRequestTests.cs @@ -96,7 +96,7 @@ namespace Squidex.Infrastructure.Commands } finally { - await Task.WhenAny(Task.Delay(2000), cluster.StopAllSilosAsync()); + await Task.WhenAny(Task.Delay(2000), cluster.DisposeAsync().AsTask()); } } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectTests.cs index 4918e613b..b2c404cd0 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectTests.cs @@ -17,12 +17,28 @@ namespace Squidex.Infrastructure.Commands { private readonly IPersistenceFactory persistenceFactory = A.Fake>(); private readonly IPersistence persistence = A.Fake>(); + private readonly IPersistence persistenceEvents = A.Fake>(); private readonly DomainId id = DomainId.NewGuid(); private readonly MyDomainObject sut; + private HandleEvent? handleEvent; public DomainObjectTests() { - sut = new MyDomainObject(persistenceFactory); + A.CallTo(() => persistenceFactory.WithEventSourcing(typeof(MyDomainObject), id, A._)) + .Invokes(args => + { + handleEvent = args.GetArgument(2)!; + }) + .Returns(persistenceEvents); + + A.CallTo(() => persistenceFactory.WithSnapshotsAndEventSourcing(typeof(MyDomainObject), id, A>._, A._)) + .Invokes(args => + { + handleEvent = args.GetArgument(3)!; + }) + .Returns(persistence); + + sut = new MyDomainObject(id, persistenceFactory); } [Fact] @@ -83,7 +99,7 @@ namespace Squidex.Infrastructure.Commands } [Fact] - public async Task Should_create_old_event() + public async Task Should_migrate_old_event_with_state() { SetupCreated(new ValueChanged { Value = 10 }, new MultipleByTwiceEvent()); @@ -514,8 +530,6 @@ namespace Squidex.Infrastructure.Commands private void SetupCreated(params IEvent[] @events) { - var handleEvent = new HandleEvent(_ => true); - var version = -1; A.CallTo(() => persistence.ReadAsync(-2, default)) @@ -525,27 +539,16 @@ namespace Squidex.Infrastructure.Commands foreach (var @event in events) { - handleEvent(Envelope.Create(@event)); + handleEvent?.Invoke(Envelope.Create(@event)); } }); - A.CallTo(() => persistenceFactory.WithSnapshotsAndEventSourcing(typeof(MyDomainObject), id, A>._, A._)) - .Invokes(args => - { - handleEvent = args.GetArgument(3)!; - }) - .Returns(persistence); - A.CallTo(() => persistence.Version) .ReturnsLazily(() => version); - - sut.Setup(id); } private void SetupLoaded() { - var handleEvent = new HandleEvent(_ => true); - var @events = new List>(); A.CallTo(() => persistence.WriteEventsAsync(A>>._, default)) @@ -554,21 +557,12 @@ namespace Squidex.Infrastructure.Commands @events.AddRange(args.GetArgument>>(0)!); }); - var eventsPersistence = A.Fake>(); - - A.CallTo(() => persistenceFactory.WithEventSourcing(typeof(MyDomainObject), id, A._)) - .Invokes(args => - { - handleEvent = args.GetArgument(2)!; - }) - .Returns(eventsPersistence); - - A.CallTo(() => eventsPersistence.ReadAsync(EtagVersion.Any, default)) + A.CallTo(() => persistenceEvents.ReadAsync(EtagVersion.Any, default)) .Invokes(_ => { foreach (var @event in events) { - handleEvent(@event); + handleEvent?.Invoke(@event); } }); } @@ -580,8 +574,6 @@ namespace Squidex.Infrastructure.Commands A.CallTo(() => persistence.Version) .Returns(-1); - - sut.Setup(id); } } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventConsumerFactoryTests.cs b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventConsumerFactoryTests.cs new file mode 100644 index 000000000..a06042ec6 --- /dev/null +++ b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventConsumerFactoryTests.cs @@ -0,0 +1,46 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using FakeItEasy; +using Xunit; + +namespace Squidex.Infrastructure.EventSourcing +{ + public class DefaultEventConsumerFactoryTests + { + private readonly IEventConsumer consumer1 = A.Fake(); + private readonly IEventConsumer consumer2 = A.Fake(); + private readonly DefaultEventConsumerFactory sut; + + public DefaultEventConsumerFactoryTests() + { + A.CallTo(() => consumer1.Name) + .Returns("consumer1"); + + A.CallTo(() => consumer2.Name) + .Returns("consumer2"); + + sut = new DefaultEventConsumerFactory(new[] { consumer1, consumer2 }); + } + + [Fact] + public void Should_return_consumer_by_name() + { + var returned1 = sut.Create(consumer1.Name); + var returned2 = sut.Create(consumer2.Name); + + Assert.Same(consumer1, returned1); + Assert.Same(returned2, returned2); + } + + [Fact] + public void Should_throw_exception_for_invalid_name() + { + Assert.Throws(() => sut.Create("invalid")); + } + } +} diff --git a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventFormatterTests.cs similarity index 92% rename from backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs rename to backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventFormatterTests.cs index 266aff604..2bcbd05c2 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventFormatterTests.cs @@ -13,7 +13,7 @@ using Xunit; namespace Squidex.Infrastructure.EventSourcing { - public class DefaultEventDataFormatterTests + public class DefaultEventFormatterTests { public sealed class MyOldEvent : IEvent, IMigrated { @@ -25,16 +25,16 @@ namespace Squidex.Infrastructure.EventSourcing } } - private readonly DefaultEventDataFormatter sut; + private readonly DefaultEventFormatter sut; - public DefaultEventDataFormatterTests() + public DefaultEventFormatterTests() { var typeNameRegistry = new TypeNameRegistry() .Map(typeof(MyEvent), "Event") .Map(typeof(MyOldEvent), "OldEvent"); - sut = new DefaultEventDataFormatter(typeNameRegistry, TestUtils.CreateSerializer(typeNameRegistry)); + sut = new DefaultEventFormatter(typeNameRegistry, TestUtils.CreateSerializer(typeNameRegistry)); } [Fact] diff --git a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs index 9ce0669a9..5575c259c 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs @@ -8,6 +8,7 @@ using FakeItEasy; using FluentAssertions; using Microsoft.Extensions.Logging; +using Orleans.Core; using Orleans.Storage; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.TestHelpers; @@ -22,12 +23,13 @@ namespace Squidex.Infrastructure.EventSourcing.Grains private IEventSubscriber? currentSubscriber; public MyEventConsumerGrain( - EventConsumerFactory eventConsumerFactory, + IGrainIdentity identity, IGrainState state, + IEventConsumerFactory eventConsumerFactory, + IEventFormatter eventFormatter, IEventStore eventStore, - IEventDataFormatter eventDataFormatter, ILogger log) - : base(eventConsumerFactory, state, eventStore, eventDataFormatter, log) + : base(identity, state, eventConsumerFactory, eventFormatter, eventStore, log) { } @@ -54,9 +56,11 @@ namespace Squidex.Infrastructure.EventSourcing.Grains } } - private readonly IGrainState grainState = A.Fake>(); + private readonly IGrainIdentity identity = A.Fake(); + private readonly IGrainState state = A.Fake>(); private readonly IEventConsumer eventConsumer = A.Fake(); - private readonly IEventDataFormatter formatter = A.Fake(); + private readonly IEventConsumerFactory eventConsumerFactory = A.Fake(); + private readonly IEventFormatter eventFormatter = A.Fake(); private readonly IEventStore eventStore = A.Fake(); private readonly IEventSubscription eventSubscription = A.Fake(); private readonly StoredEvent storedEvent; @@ -68,19 +72,28 @@ namespace Squidex.Infrastructure.EventSourcing.Grains public EventConsumerGrainTests() { - grainState.Value = new EventConsumerState + state.Value = new EventConsumerState { Position = initialPosition }; consumerName = eventConsumer.GetType().Name; + A.CallTo(() => identity.PrimaryKeyString) + .Returns(consumerName); + A.CallTo(() => eventStore.CreateSubscription(A._, A._, A._)) .Returns(eventSubscription); A.CallTo(() => eventConsumer.Name) .Returns(consumerName); + A.CallTo(() => eventSubscription.Sender) + .Returns(eventSubscription); + + A.CallTo(() => eventConsumerFactory.Create(eventConsumer.Name)) + .Returns(eventConsumer); + A.CallTo(() => eventConsumer.Handles(A._)) .Returns(true); @@ -93,30 +106,27 @@ namespace Squidex.Infrastructure.EventSourcing.Grains } }); - A.CallTo(() => eventSubscription.Sender) - .Returns(eventSubscription); - storedEvent = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData); - A.CallTo(() => formatter.ParseIfKnown(storedEvent)) + A.CallTo(() => eventFormatter.ParseIfKnown(storedEvent)) .Returns(envelope); var log = A.Fake>(); sut = new MyEventConsumerGrain( - x => eventConsumer, - grainState, + identity, + state, + eventConsumerFactory, + eventFormatter, eventStore, - formatter, log); } [Fact] public async Task Should_not_subscribe_to_event_store_if_stopped_in_db() { - grainState.Value = grainState.Value.Stopped(); + state.Value = state.Value.Stopped(); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await sut.CompleteAsync(); @@ -130,7 +140,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_subscribe_to_event_store_if_not_found_in_db() { - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await sut.CompleteAsync(); @@ -144,9 +153,8 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_subscribe_to_event_store_if_failed() { - grainState.Value = grainState.Value.Stopped(new InvalidOperationException()); + state.Value = state.Value.Stopped(new InvalidOperationException()); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await sut.CompleteAsync(); @@ -160,7 +168,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_subscribe_to_event_store_if_not_stopped_in_db() { - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await sut.CompleteAsync(); @@ -174,7 +181,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_stop_subscription_if_stopped() { - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await sut.StopAsync(); await sut.StopAsync(); @@ -183,7 +189,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains AssertGrainState(isStopped: true, position: initialPosition); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventSubscription.Unsubscribe()) @@ -193,7 +199,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_reset_consumer_if_resetting() { - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await sut.StopAsync(); await sut.ResetAsync(); @@ -202,7 +207,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains AssertGrainState(isStopped: false, position: null); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappened(2, Times.Exactly); A.CallTo(() => eventConsumer.ClearAsync()) @@ -211,7 +216,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventSubscription.Unsubscribe()) .MustHaveHappenedOnceExactly(); - A.CallTo(() => eventStore.CreateSubscription(A._, A._, grainState.Value.Position)) + A.CallTo(() => eventStore.CreateSubscription(A._, A._, state.Value.Position)) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventStore.CreateSubscription(A._, A._, null)) @@ -221,7 +226,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_invoke_and_update_position_if_event_received() { - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(eventSubscription, storedEvent); @@ -230,7 +234,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains AssertGrainState(isStopped: false, position: storedEvent.EventPosition, count: 1); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventConsumer.On(envelope)) @@ -243,7 +247,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventConsumer.BatchSize) .Returns(1); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(eventSubscription, storedEvent); @@ -256,7 +259,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains AssertGrainState(isStopped: false, position: storedEvent.EventPosition, count: 5); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappened(5, Times.Exactly); A.CallTo(() => eventConsumer.On(A>>._)) @@ -269,7 +272,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventConsumer.BatchSize) .Returns(100); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(eventSubscription, storedEvent); @@ -282,7 +284,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains AssertGrainState(isStopped: false, position: storedEvent.EventPosition, count: 5); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventConsumer.On(A>>._)) @@ -295,7 +297,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventConsumer.Handles(storedEvent)) .Returns(false); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(eventSubscription, storedEvent); @@ -304,7 +305,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains AssertGrainState(isStopped: false, position: storedEvent.EventPosition); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventConsumer.On(envelope)) @@ -314,10 +315,9 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_ignore_old_events() { - A.CallTo(() => formatter.ParseIfKnown(A.That.Matches(x => x.Data == eventData))) + A.CallTo(() => eventFormatter.ParseIfKnown(A.That.Matches(x => x.Data == eventData))) .Returns(null); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(eventSubscription, storedEvent); @@ -326,7 +326,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains AssertGrainState(isStopped: false, position: storedEvent.EventPosition); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventConsumer.On(envelope)) @@ -336,7 +336,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_not_invoke_and_update_position_if_event_is_from_another_subscription() { - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(A.Fake(), storedEvent); @@ -352,7 +351,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_stop_if_consumer_failed() { - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); var ex = new InvalidOperationException(); @@ -363,7 +361,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains AssertGrainState(isStopped: true, position: initialPosition, error: ex.ToString()); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventSubscription.Unsubscribe()) @@ -375,7 +373,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains { var ex = new InvalidOperationException(); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnErrorAsync(A.Fake(), ex); @@ -384,17 +381,15 @@ namespace Squidex.Infrastructure.EventSourcing.Grains AssertGrainState(isStopped: false, position: initialPosition); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustNotHaveHappened(); } [Fact] public async Task Should_wakeup_if_already_subscribed() { - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await sut.ActivateAsync(); - await sut.CompleteAsync(); A.CallTo(() => eventSubscription.WakeUp()) @@ -409,15 +404,13 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventConsumer.ClearAsync()) .Throws(ex); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await sut.ResetAsync(); - await sut.CompleteAsync(); AssertGrainState(isStopped: true, position: initialPosition, error: ex.ToString()); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventSubscription.Unsubscribe()) @@ -432,7 +425,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventConsumer.On(envelope)) .Throws(ex); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(eventSubscription, storedEvent); @@ -444,7 +436,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventConsumer.On(envelope)) .MustHaveHappened(); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventSubscription.Unsubscribe()) @@ -456,10 +448,9 @@ namespace Squidex.Infrastructure.EventSourcing.Grains { var ex = new InvalidOperationException(); - A.CallTo(() => formatter.ParseIfKnown(A.That.Matches(x => x.Data == eventData))) + A.CallTo(() => eventFormatter.ParseIfKnown(A.That.Matches(x => x.Data == eventData))) .Throws(ex); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(eventSubscription, storedEvent); @@ -471,7 +462,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventConsumer.On(envelope)) .MustNotHaveHappened(); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappenedOnceExactly(); A.CallTo(() => eventSubscription.Unsubscribe()) @@ -486,7 +477,6 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventConsumer.On(envelope)) .Throws(ex); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(eventSubscription, storedEvent); @@ -502,7 +492,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => eventConsumer.On(envelope)) .MustHaveHappened(); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .MustHaveHappened(2, Times.Exactly); A.CallTo(() => eventSubscription.Unsubscribe()) @@ -517,10 +507,9 @@ namespace Squidex.Infrastructure.EventSourcing.Grains { var ex = new InconsistentStateException(); - A.CallTo(() => grainState.WriteAsync()) + A.CallTo(() => state.WriteAsync()) .Throws(ex); - await sut.ActivateAsync(consumerName); await sut.ActivateAsync(); await OnEventAsync(eventSubscription, storedEvent); @@ -544,7 +533,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains { var expected = new EventConsumerState { IsStopped = isStopped, Position = position, Error = error, Count = count }; - grainState.Value.Should().BeEquivalentTo(expected); + state.Value.Should().BeEquivalentTo(expected); } } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/MongoParallelInsertTests.cs b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/MongoParallelInsertTests.cs index 3b95bf989..80d6f2dea 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/MongoParallelInsertTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/MongoParallelInsertTests.cs @@ -7,6 +7,7 @@ using FakeItEasy; using Microsoft.Extensions.Logging; +using Orleans.Core; using Orleans.Internal; using Squidex.Infrastructure.EventSourcing.Grains; using Squidex.Infrastructure.Orleans; @@ -22,9 +23,9 @@ namespace Squidex.Infrastructure.EventSourcing [Trait("Category", "Dependencies")] public sealed class MongoParallelInsertTests : IClassFixture { - private readonly IGrainState grainState = A.Fake>(); + private readonly IGrainState state = A.Fake>(); private readonly ILogger log = A.Fake>(); - private readonly IEventDataFormatter eventDataFormatter; + private readonly IEventFormatter eventFormatter; public MongoEventStoreFixture _ { get; } @@ -152,12 +153,13 @@ namespace Squidex.Infrastructure.EventSourcing public TaskScheduler Scheduler => scheduler; public MyEventConsumerGrain( - EventConsumerFactory eventConsumerFactory, + IGrainIdentity identity, IGrainState state, + IEventConsumerFactory eventConsumerFactory, + IEventFormatter eventFormatter, IEventStore eventStore, - IEventDataFormatter eventDataFormatter, ILogger log) - : base(eventConsumerFactory, state, eventStore, eventDataFormatter, log) + : base(identity, state, eventConsumerFactory, eventFormatter, eventStore, log) { } } @@ -212,7 +214,7 @@ namespace Squidex.Infrastructure.EventSourcing var typeNameRegistry = new TypeNameRegistry().Map(typeof(MyEvent), "My"); - eventDataFormatter = new DefaultEventDataFormatter(typeNameRegistry, TestUtils.DefaultSerializer); + eventFormatter = new DefaultEventFormatter(typeNameRegistry, TestUtils.DefaultSerializer); } [Fact] @@ -220,25 +222,24 @@ namespace Squidex.Infrastructure.EventSourcing { var expectedEvents = 10 * 1000; - var consumer = new MyEventConsumer(expectedEvents); - var consumerGrain = new MyEventConsumerGrain(_ => consumer, grainState, _.EventStore, eventDataFormatter, log); + var eventConsumer = new MyEventConsumer(expectedEvents); + var eventConsumerGrain = BuildGrain(eventConsumer); - await consumerGrain.ActivateAsync(consumer.Name); - await consumerGrain.ActivateAsync(); + await eventConsumerGrain.ActivateAsync(); - Parallel.For(0, 20, x => + await Parallel.ForEachAsync(Enumerable.Range(0, 20), async (_, _) => { for (var i = 0; i < 500; i++) { var commitId = Guid.NewGuid(); - var data = eventDataFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); + var data = eventFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); - _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }).Wait(); + await _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }); } }); - await AssertConsumerAsync(expectedEvents, consumer); + await AssertConsumerAsync(expectedEvents, eventConsumer); } [Fact] @@ -246,26 +247,25 @@ namespace Squidex.Infrastructure.EventSourcing { var expectedEvents = 10 * 1000; - var consumer = new MyEventConsumer(expectedEvents); - var consumerGrain = new MyEventConsumerGrain(_ => consumer, grainState, _.EventStore, eventDataFormatter, log); + var eventConsumer = new MyEventConsumer(expectedEvents); + var eventConsumerGrain = BuildGrain(eventConsumer); - await consumerGrain.ActivateAsync(consumer.Name); - await consumerGrain.ActivateAsync(); + await eventConsumerGrain.ActivateAsync(); - Parallel.For(0, 10, x => + await Parallel.ForEachAsync(Enumerable.Range(0, 10), async (_, _) => { for (var i = 0; i < 500; i++) { var commitId = Guid.NewGuid(); - var data1 = eventDataFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); - var data2 = eventDataFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); + var data1 = eventFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); + var data2 = eventFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); - _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data1, data2 }).Wait(); + await _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data1, data2 }); } }); - await AssertConsumerAsync(expectedEvents, consumer); + await AssertConsumerAsync(expectedEvents, eventConsumer); } [Fact] @@ -273,25 +273,24 @@ namespace Squidex.Infrastructure.EventSourcing { var expectedEvents = 10 * 1000; - var consumer = new MyEventConsumer(expectedEvents); - var consumerGrain = new MyEventConsumerGrain(_ => consumer, grainState, _.EventStore, eventDataFormatter, log); + var eventConsumer = new MyEventConsumer(expectedEvents); + var eventConsumerGrain = BuildGrain(eventConsumer); - Parallel.For(0, 10, x => + await Parallel.ForEachAsync(Enumerable.Range(0, 10), async (_, _) => { for (var i = 0; i < 1000; i++) { var commitId = Guid.NewGuid(); - var data = eventDataFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); + var data = eventFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); - _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }).Wait(); + await _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }); } }); - await consumerGrain.ActivateAsync(consumer.Name); - await consumerGrain.ActivateAsync(); + await eventConsumerGrain.ActivateAsync(); - await AssertConsumerAsync(expectedEvents, consumer); + await AssertConsumerAsync(expectedEvents, eventConsumer); } [Fact] @@ -299,37 +298,36 @@ namespace Squidex.Infrastructure.EventSourcing { var expectedEvents = 10 * 1000; - var consumer = new MyEventConsumer(expectedEvents); - var consumerGrain = new MyEventConsumerGrain(_ => consumer, grainState, _.EventStore, eventDataFormatter, log); + var eventConsumer = new MyEventConsumer(expectedEvents); + var eventConsumerGrain = BuildGrain(eventConsumer); - Parallel.For(0, 10, x => + await Parallel.ForEachAsync(Enumerable.Range(0, 10), async (_, _) => { for (var i = 0; i < 500; i++) { var commitId = Guid.NewGuid(); - var data = eventDataFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); + var data = eventFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); - _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }).Wait(); + await _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }); } }); - await consumerGrain.ActivateAsync(consumer.Name); - await consumerGrain.ActivateAsync(); + await eventConsumerGrain.ActivateAsync(); - Parallel.For(0, 10, x => + await Parallel.ForEachAsync(Enumerable.Range(0, 10), async (_, _) => { for (var i = 0; i < 500; i++) { var commitId = Guid.NewGuid(); - var data = eventDataFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); + var data = eventFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); - _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }).Wait(); + await _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }); } }); - await AssertConsumerAsync(expectedEvents, consumer); + await AssertConsumerAsync(expectedEvents, eventConsumer); } [Fact] @@ -337,13 +335,12 @@ namespace Squidex.Infrastructure.EventSourcing { var expectedEvents = 10 * 1000; - var consumer = new MyEventConsumer(expectedEvents); - var consumerGrain = new MyEventConsumerGrain(_ => consumer, grainState, _.EventStore, eventDataFormatter, log); + var eventConsumer = new MyEventConsumer(expectedEvents); + var eventConsumerGrain = BuildGrain(eventConsumer); - await consumerGrain.ActivateAsync(consumer.Name); - await consumerGrain.ActivateAsync(); + await eventConsumerGrain.ActivateAsync(); - Parallel.For(0, 10, x => + await Parallel.ForEachAsync(Enumerable.Range(0, 10), async (_, _) => { for (var j = 0; j < 10; j++) { @@ -351,16 +348,16 @@ namespace Squidex.Infrastructure.EventSourcing { var commitId = Guid.NewGuid(); - var data = eventDataFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); + var data = eventFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); - _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }).Wait(); + await _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }); } - Thread.Sleep(1000); + await Task.Delay(1000); } }); - await AssertConsumerAsync(expectedEvents, consumer); + await AssertConsumerAsync(expectedEvents, eventConsumer); } [Fact] @@ -368,50 +365,64 @@ namespace Squidex.Infrastructure.EventSourcing { var expectedEvents = 10 * 1000; - var consumer = new MyEventConsumer(expectedEvents); - var consumerGrain = new MyEventConsumerGrain(_ => consumer, grainState, _.EventStore, eventDataFormatter, log); + var eventConsumer = new MyEventConsumer(expectedEvents); + var eventConsumerGrain = BuildGrain(eventConsumer); - var scheduler = consumerGrain.Scheduler; + var scheduler = eventConsumerGrain.Scheduler; - consumer.EventReceived = count => + eventConsumer.EventReceived = count => { if (count % 1000 == 0) { Task.Factory.StartNew(async () => { - await consumerGrain.StopAsync(); - await consumerGrain.StartAsync(); + await eventConsumerGrain.StopAsync(); + await eventConsumerGrain.StartAsync(); }, default, default, scheduler).Forget(); } return Task.CompletedTask; }; - await consumerGrain.ActivateAsync(consumer.Name); - await consumerGrain.ActivateAsync(); + await eventConsumerGrain.ActivateAsync(); - Parallel.For(0, 10, x => + await Parallel.ForEachAsync(Enumerable.Range(0, 10), async (_, _) => { for (var i = 0; i < 1000; i++) { var commitId = Guid.NewGuid(); - var data = eventDataFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); + var data = eventFormatter.ToEventData(Envelope.Create(new MyEvent()), commitId); - _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }).Wait(); + await _.EventStore.AppendAsync(commitId, commitId.ToString(), new[] { data }); } }); - await AssertConsumerAsync(expectedEvents, consumer); + await AssertConsumerAsync(expectedEvents, eventConsumer); } - private static async Task AssertConsumerAsync(int expectedEvents, MyEventConsumer consumer) + private MyEventConsumerGrain BuildGrain(IEventConsumer eventConsumer) { - await consumer.Completed.WithTimeout(TimeSpan.FromSeconds(100)); + var identity = A.Fake(); + + A.CallTo(() => identity.PrimaryKeyString) + .Returns(eventConsumer.Name); + + var eventConsumerFactory = A.Fake(); + + A.CallTo(() => eventConsumerFactory.Create(eventConsumer.Name)) + .Returns(eventConsumer); + + return new MyEventConsumerGrain(identity, state, eventConsumerFactory, eventFormatter, _.EventStore, log); + } + + private static async Task AssertConsumerAsync(int expectedEvents, MyEventConsumer eventConsumer) + { + await eventConsumer.Completed.WithTimeout(TimeSpan.FromSeconds(100)); await Task.Delay(2000); - Assert.Equal(expectedEvents, consumer.Received); + Assert.Equal(expectedEvents, eventConsumer.Received); } } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/ActivationLimiterFilterTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/ActivationLimiterFilterTests.cs index 4afafd87f..4748cc1b6 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Orleans/ActivationLimiterFilterTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/ActivationLimiterFilterTests.cs @@ -7,6 +7,7 @@ using FakeItEasy; using Orleans; +using Orleans.Core; using Orleans.Runtime; using Xunit; @@ -24,8 +25,8 @@ namespace Squidex.Infrastructure.Orleans public sealed class MyGrain : GrainBase { - public MyGrain(IActivationLimit limit) - : base(null, CreateRuntime(limit)) + public MyGrain(IGrainIdentity identity, IActivationLimit limit) + : base(identity, CreateRuntime(limit)) { } @@ -50,10 +51,8 @@ namespace Squidex.Infrastructure.Orleans { var limit = A.Fake(); - var grain = new MyGrain(limit); - A.CallTo(() => context.Grain) - .Returns(grain); + .Returns(new MyGrain(A.Fake(), limit)); await sut.Invoke(context); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/ActivityPropagationTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/ActivityPropagationTests.cs new file mode 100644 index 000000000..aa3acfb86 --- /dev/null +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/ActivityPropagationTests.cs @@ -0,0 +1,140 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Diagnostics; +using Microsoft.Extensions.Configuration; +using Orleans; +using Orleans.Hosting; +using Orleans.TestingHost; +using Xunit; + +namespace Squidex.Infrastructure.Orleans +{ + [Trait("Category", "Dependencies")] + public class ActivityPropagationTests + { + public interface IActivityGrain : IGrainWithStringKey + { + public Task GetActivityId(); + } + + public class ActivityGrain : Grain, IActivityGrain + { + public Task GetActivityId() + { + return Task.FromResult(Activity.Current?.TraceId.ToHexString() ?? string.Empty); + } + } + + public sealed class Configurator : ISiloConfigurator, IClientBuilderConfigurator + { + public void Configure(ISiloBuilder siloBuilder) + { + siloBuilder.AddIncomingGrainCallFilter(); + siloBuilder.AddOutgoingGrainCallFilter(); + } + + public void Configure(IConfiguration configuration, IClientBuilder clientBuilder) + { + clientBuilder.AddOutgoingGrainCallFilter(); + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Should_forward_activity(bool listen) + { + var cluster = + new TestClusterBuilder(1) + .AddSiloBuilderConfigurator() + .AddClientBuilderConfigurator() + .Build(); + + await cluster.DeployAsync(); + try + { + using var listener = new ActivityListener + { + ShouldListenTo = s => true, + Sample = (ref ActivityCreationOptions activityOptions) => + { + return ActivitySamplingResult.AllData; + }, + SampleUsingParentId = (ref ActivityCreationOptions activityOptions) => + { + return ActivitySamplingResult.AllData; + } + }; + + if (listen) + { + ActivitySource.AddActivityListener(listener); + } + + using (var activity = Telemetry.Activities.StartActivity("Test", ActivityKind.Server)) + { + var grain = cluster.GrainFactory.GetGrain(SingleGrain.Id); + + var activityId = await grain.GetActivityId(); + + if (listen) + { + Assert.Equal(activity?.TraceId.ToHexString(), activityId); + } + else + { + Assert.Empty(activityId); + } + } + } + finally + { + await Task.WhenAny(Task.Delay(2000), cluster.DisposeAsync().AsTask()); + } + } + + [Fact] + public async Task Should_create_new_activity() + { + var cluster = + new TestClusterBuilder(1) + .AddSiloBuilderConfigurator() + .AddClientBuilderConfigurator() + .Build(); + + await cluster.DeployAsync(); + try + { + using var listener = new ActivityListener + { + ShouldListenTo = s => true, + Sample = (ref ActivityCreationOptions activityOptions) => + { + return ActivitySamplingResult.AllData; + }, + SampleUsingParentId = (ref ActivityCreationOptions activityOptions) => + { + return ActivitySamplingResult.AllData; + } + }; + + ActivitySource.AddActivityListener(listener); + + var grain = cluster.GrainFactory.GetGrain(SingleGrain.Id); + + var activityId = await grain.GetActivityId(); + + Assert.NotEmpty(activityId); + } + finally + { + await Task.WhenAny(Task.Delay(2000), cluster.DisposeAsync().AsTask()); + } + } + } +} diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/AsyncLocalTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/AsyncLocalTests.cs index 9c315c666..6abe8dd8b 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Orleans/AsyncLocalTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/AsyncLocalTests.cs @@ -58,7 +58,7 @@ namespace Squidex.Infrastructure.Orleans } finally { - await Task.WhenAny(Task.Delay(2000), cluster.StopAllSilosAsync()); + await Task.WhenAny(Task.Delay(2000), cluster.DisposeAsync().AsTask()); } } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/CultureFilterTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/CultureFilterTests.cs new file mode 100644 index 000000000..eeff370e5 --- /dev/null +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/CultureFilterTests.cs @@ -0,0 +1,102 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Globalization; +using Microsoft.Extensions.Configuration; +using Orleans; +using Orleans.Hosting; +using Orleans.TestingHost; +using Xunit; + +namespace Squidex.Infrastructure.Orleans +{ + [Trait("Category", "Dependencies")] + public class CultureFilterTests + { + public interface ICultureGrain : IGrainWithStringKey + { + public Task GetCultureAsync(bool chaining); + + public Task GetUICultureAsync(bool chaining); + } + + public class CultureGrain : Grain, ICultureGrain + { + public Task GetCultureAsync(bool chaining) + { + if (chaining) + { + return GrainFactory.GetGrain("1").GetCultureAsync(false); + } + + return Task.FromResult(CultureInfo.CurrentCulture.ToString()); + } + + public Task GetUICultureAsync(bool chaining) + { + if (chaining) + { + return GrainFactory.GetGrain("1").GetUICultureAsync(false); + } + + return Task.FromResult(CultureInfo.CurrentUICulture.ToString()); + } + } + + public sealed class Configurator : ISiloConfigurator, IClientBuilderConfigurator + { + public void Configure(ISiloBuilder siloBuilder) + { + siloBuilder.AddIncomingGrainCallFilter(); + siloBuilder.AddOutgoingGrainCallFilter(); + } + + public void Configure(IConfiguration configuration, IClientBuilder clientBuilder) + { + clientBuilder.AddOutgoingGrainCallFilter(); + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task Should_forward_culture(bool chaining) + { + await using var cluster = + new TestClusterBuilder(1) + .AddSiloBuilderConfigurator() + .AddClientBuilderConfigurator() + .Build(); + + await cluster.DeployAsync(); + + var previousCulture = CultureInfo.CurrentCulture; + var previousUICulture = CultureInfo.CurrentUICulture; + try + { + var culture = CultureInfo.GetCultureInfo("de"); + var cultureUI = CultureInfo.GetCultureInfo("it"); + + CultureInfo.CurrentCulture = culture; + CultureInfo.CurrentUICulture = cultureUI; + + var grain = cluster.GrainFactory.GetGrain(SingleGrain.Id); + + var cultureFromGrain = await grain.GetCultureAsync(chaining); + var cultureUIFromGrain = await grain.GetUICultureAsync(chaining); + + Assert.Equal(culture.Name, cultureFromGrain); + Assert.Equal(cultureUI.Name, cultureUIFromGrain); + } + finally + { + CultureInfo.CurrentCulture = previousCulture; + CultureInfo.CurrentUICulture = previousUICulture; + } + } + } +} diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/ExceptionWrapperFilterTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/ExceptionWrapperFilterTests.cs index a6ab90317..0a9aac59e 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Orleans/ExceptionWrapperFilterTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/ExceptionWrapperFilterTests.cs @@ -121,7 +121,7 @@ namespace Squidex.Infrastructure.Orleans } finally { - await Task.WhenAny(Task.Delay(2000), cluster.StopAllSilosAsync()); + await Task.WhenAny(Task.Delay(2000), cluster.DisposeAsync().AsTask()); } } @@ -138,7 +138,7 @@ namespace Squidex.Infrastructure.Orleans } finally { - await Task.WhenAny(Task.Delay(2000), cluster.StopAllSilosAsync()); + await Task.WhenAny(Task.Delay(2000), cluster.DisposeAsync().AsTask()); } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/Indexes/UniqueNameGrainTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/Indexes/UniqueNameGrainTests.cs index 7001d6c69..c751f4b7c 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Orleans/Indexes/UniqueNameGrainTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/Indexes/UniqueNameGrainTests.cs @@ -5,6 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using FakeItEasy; +using Orleans.Core; using Xunit; namespace Squidex.Infrastructure.Orleans.Indexes @@ -15,7 +17,7 @@ namespace Squidex.Infrastructure.Orleans.Indexes public UniqueNameGrainTests() { - sut = new UniqueNameGrain(); + sut = new UniqueNameGrain(A.Fake()); } [Fact] diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializationTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializationTests.cs index f8f105479..b0d969609 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializationTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializationTests.cs @@ -5,7 +5,11 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Orleans; +using Orleans.Configuration; +using Orleans.Hosting; using Orleans.TestingHost; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.TestHelpers; @@ -18,20 +22,16 @@ namespace Squidex.Infrastructure.Orleans { public interface ICommandGrain : IGrainWithStringKey { - public Task> ExecuteAsync(J request); + public Task ExecuteAsync(IAggregateCommand command); } public class CommandGrain : Grain, ICommandGrain { - public Task> ExecuteAsync(J request) + public Task ExecuteAsync(IAggregateCommand command) { - request.Value.ApplyContext(); + var result = new CommandResult(command.AggregateId, 0, 0, ((TestCommand)command).Value); - var command = (TestCommand)request.Value.Command; - - var result = new CommandResult(command.AggregateId, 0, 0, command.Value); - - return Task.FromResult(result.AsJ()); + return Task.FromResult(result); } } @@ -44,9 +44,33 @@ namespace Squidex.Infrastructure.Orleans public string Value { get; set; } } - public JsonExternalSerializationTests() + public sealed class Configurator : ISiloConfigurator, IClientBuilderConfigurator { - J.DefaultSerializer = TestUtils.DefaultSerializer; + public void Configure(ISiloBuilder siloBuilder) + { + siloBuilder.ConfigureServices(services => + { + services.AddSingleton(TestUtils.DefaultSerializer); + }); + + siloBuilder.Configure(options => + { + options.SerializationProviders.Add(typeof(JsonSerializer)); + }); + } + + public void Configure(IConfiguration configuration, IClientBuilder clientBuilder) + { + clientBuilder.ConfigureServices(services => + { + services.AddSingleton(TestUtils.DefaultSerializer); + }); + + clientBuilder.Configure(options => + { + options.SerializationProviders.Add(typeof(JsonSerializer)); + }); + } } [Fact] @@ -54,6 +78,8 @@ namespace Squidex.Infrastructure.Orleans { var cluster = new TestClusterBuilder(1) + .AddSiloBuilderConfigurator() + .AddClientBuilderConfigurator() .Build(); await cluster.DeployAsync(); @@ -64,14 +90,12 @@ namespace Squidex.Infrastructure.Orleans { var id = DomainId.NewGuid().ToString(); - var grain = cluster.GrainFactory.GetGrain(id); + var commandGrain = cluster.GrainFactory.GetGrain(id); + var commandTest = new TestCommand { Value = id }; - var result = await grain.ExecuteAsync(CommandRequest.Create(new TestCommand - { - Value = id - })); + var result = await commandGrain.ExecuteAsync(commandTest); - Assert.Equal(id, result.Value.Payload); + Assert.Equal(id, result.Payload); } } finally diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs index e42ddc697..2bf9130f1 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs @@ -16,39 +16,26 @@ namespace Squidex.Infrastructure.Orleans { public class JsonExternalSerializerTests { - public JsonExternalSerializerTests() - { - J.DefaultSerializer = TestUtils.DefaultSerializer; - } + private readonly IExternalSerializer serializer = new JsonSerializer(TestUtils.DefaultSerializer); [Fact] public void Should_not_copy_null() { var source = (string?)null; - var clone = J.Copy(source, null); + var clone = serializer.DeepCopy(source, null); Assert.Null(clone); } [Fact] - public void Should_copy_null_json() + public void Should_not_copy_values() { - var source = new J?>(null); + var source = new List { 1, 2, 3 }; - var clone = (J>)J.Copy(source, null)!; + var copy = serializer.DeepCopy(source, null)!; - Assert.Null(clone.Value); - } - - [Fact] - public void Should_not_copy_immutable_values() - { - var source = new List { 1, 2, 3 }.AsJ(); - - var copy = (J>)J.Copy(source, null)!; - - Assert.Same(source.Value, copy.Value); + Assert.Same(source, copy); } [Fact] @@ -63,19 +50,21 @@ namespace Squidex.Infrastructure.Orleans SerializeAndDeserialize(ArrayOfLength(8000), Assert.Equal); } - private static void SerializeAndDeserialize(T value, Action equals) where T : class + private void SerializeAndDeserialize(T value, Action assertEquals) where T : class { using (var buffer = new MemoryStream()) { - J.Serialize(J.Of(value), CreateWriter(buffer), typeof(T)); + var jsonWriter = CreateWriter(buffer); + var jsonReader = CreateReader(buffer); - buffer.Position = 0; + serializer.Serialize(value, jsonWriter, typeof(T)); - var copy = (J)J.Deserialize(typeof(J), CreateReader(buffer))!; + buffer.Position = 0; - equals(copy.Value, value); + var deserialized = (T)serializer.Deserialize(typeof(T), jsonReader)!; - Assert.NotSame(value, copy.Value); + assertEquals(deserialized, value); + Assert.NotSame(value, deserialized); } } @@ -85,8 +74,10 @@ namespace Squidex.Infrastructure.Orleans A.CallTo(() => reader.ReadByteArray(A._, A._, A._)) .Invokes(new Action((array, offset, length) => buffer.Read(array, offset, length))); + A.CallTo(() => reader.CurrentPosition) .ReturnsLazily(x => (int)buffer.Position); + A.CallTo(() => reader.Length) .ReturnsLazily(x => (int)buffer.Length); @@ -104,6 +95,7 @@ namespace Squidex.Infrastructure.Orleans A.CallTo(() => writer.Write(A._, A._, A._)) .Invokes(new Action(buffer.Write)); + A.CallTo(() => writer.CurrentOffset) .ReturnsLazily(x => (int)buffer.Position); diff --git a/backend/tests/Squidex.Infrastructure.Tests/States/DefaultStreamNameResolverTests.cs b/backend/tests/Squidex.Infrastructure.Tests/States/DefaultEventStreamNamesTests.cs similarity index 88% rename from backend/tests/Squidex.Infrastructure.Tests/States/DefaultStreamNameResolverTests.cs rename to backend/tests/Squidex.Infrastructure.Tests/States/DefaultEventStreamNamesTests.cs index 37e6e9c14..6c759ac96 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/States/DefaultStreamNameResolverTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/States/DefaultEventStreamNamesTests.cs @@ -9,9 +9,9 @@ using Xunit; namespace Squidex.Infrastructure.States { - public class DefaultStreamNameResolverTests + public class DefaultEventStreamNamesTests { - private readonly DefaultStreamNameResolver sut = new DefaultStreamNameResolver(); + private readonly DefaultEventStreamNames sut = new DefaultEventStreamNames(); private sealed class MyUser { diff --git a/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceBatchTests.cs b/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceBatchTests.cs index cb3d005e3..f59a4cf77 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceBatchTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceBatchTests.cs @@ -15,18 +15,18 @@ namespace Squidex.Infrastructure.States { public class PersistenceBatchTests { - private readonly ISnapshotStore snapshotStore = A.Fake>(); - private readonly IEventDataFormatter eventDataFormatter = A.Fake(); + private readonly IEventFormatter eventFormatter = A.Fake(); private readonly IEventStore eventStore = A.Fake(); - private readonly IStreamNameResolver streamNameResolver = A.Fake(); + private readonly IEventStreamNames eventStreamNames = A.Fake(); + private readonly ISnapshotStore snapshotStore = A.Fake>(); private readonly IStore sut; public PersistenceBatchTests() { - A.CallTo(() => streamNameResolver.GetStreamName(None.Type, A._)) + A.CallTo(() => eventStreamNames.GetStreamName(None.Type, A._)) .ReturnsLazily(x => x.GetArgument(1)!); - sut = new Store(snapshotStore, eventStore, eventDataFormatter, streamNameResolver); + sut = new Store(eventFormatter, eventStore, eventStreamNames, snapshotStore); } [Fact] @@ -202,10 +202,10 @@ namespace Squidex.Infrastructure.States storedStream.Add(eventStored); - A.CallTo(() => eventDataFormatter.Parse(eventStored)) + A.CallTo(() => eventFormatter.Parse(eventStored)) .Returns(new Envelope(@event)); - A.CallTo(() => eventDataFormatter.ParseIfKnown(eventStored)) + A.CallTo(() => eventFormatter.ParseIfKnown(eventStored)) .Returns(new Envelope(@event)); i++; diff --git a/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs b/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs index 56d09c982..4ba1c0d5f 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs @@ -16,18 +16,18 @@ namespace Squidex.Infrastructure.States public class PersistenceEventSourcingTests { private readonly DomainId key = DomainId.NewGuid(); - private readonly ISnapshotStore snapshotStore = A.Fake>(); - private readonly IEventDataFormatter eventDataFormatter = A.Fake(); + private readonly IEventFormatter eventFormatter = A.Fake(); private readonly IEventStore eventStore = A.Fake(); - private readonly IStreamNameResolver streamNameResolver = A.Fake(); + private readonly IEventStreamNames eventStreamNames = A.Fake(); + private readonly ISnapshotStore snapshotStore = A.Fake>(); private readonly IStore sut; public PersistenceEventSourcingTests() { - A.CallTo(() => streamNameResolver.GetStreamName(None.Type, A._)) + A.CallTo(() => eventStreamNames.GetStreamName(None.Type, A._)) .ReturnsLazily(x => x.GetArgument(1)!); - sut = new Store(snapshotStore, eventStore, eventDataFormatter, streamNameResolver); + sut = new Store(eventFormatter, eventStore, eventStreamNames, snapshotStore); } [Fact] @@ -70,7 +70,7 @@ namespace Squidex.Infrastructure.States A.CallTo(() => eventStore.QueryAsync(key.ToString(), 0, A._)) .Returns(new List { storedEvent }); - A.CallTo(() => eventDataFormatter.ParseIfKnown(storedEvent)) + A.CallTo(() => eventFormatter.ParseIfKnown(storedEvent)) .Returns(null); var persistedEvents = Save.Events(); @@ -348,6 +348,8 @@ namespace Squidex.Infrastructure.States A.CallTo(() => snapshotStore.RemoveAsync(key, A._)) .MustHaveHappened(); + + Assert.Equal(EtagVersion.Empty, persistence.Version); } private void SetupEventStore(int count, int eventOffset = 0, int readPosition = 0) @@ -373,10 +375,10 @@ namespace Squidex.Infrastructure.States eventsStored.Add(eventStored); - A.CallTo(() => eventDataFormatter.Parse(eventStored)) + A.CallTo(() => eventFormatter.Parse(eventStored)) .Returns(new Envelope(@event)); - A.CallTo(() => eventDataFormatter.ParseIfKnown(eventStored)) + A.CallTo(() => eventFormatter.ParseIfKnown(eventStored)) .Returns(new Envelope(@event)); i++; diff --git a/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs b/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs index d5d2fb507..161e8edf2 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs @@ -15,14 +15,14 @@ namespace Squidex.Infrastructure.States { private readonly DomainId key = DomainId.NewGuid(); private readonly ISnapshotStore snapshotStore = A.Fake>(); - private readonly IEventDataFormatter eventDataFormatter = A.Fake(); + private readonly IEventStreamNames eventStreamNames = A.Fake(); + private readonly IEventFormatter eventFormatter = A.Fake(); private readonly IEventStore eventStore = A.Fake(); - private readonly IStreamNameResolver streamNameResolver = A.Fake(); private readonly IStore sut; public PersistenceSnapshotTests() { - sut = new Store(snapshotStore, eventStore, eventDataFormatter, streamNameResolver); + sut = new Store(eventFormatter, eventStore, eventStreamNames, snapshotStore); } [Fact] @@ -169,6 +169,8 @@ namespace Squidex.Infrastructure.States A.CallTo(() => snapshotStore.RemoveAsync(key, A._)) .MustHaveHappened(); + + Assert.Equal(EtagVersion.Empty, persistence.Version); } [Fact] diff --git a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainObject.cs b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainObject.cs index ca6d9e5cf..96333803e 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainObject.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainObject.cs @@ -27,8 +27,8 @@ namespace Squidex.Infrastructure.TestHelpers set => Capacity = value; } - public MyDomainObject(IPersistenceFactory factory) - : base(factory, A.Dummy()) + public MyDomainObject(DomainId id, IPersistenceFactory factory) + : base(id, factory, A.Dummy()) { } diff --git a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs index b269b384b..e313dd399 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs @@ -41,6 +41,15 @@ namespace Squidex.Infrastructure.TestHelpers } } + public sealed class ValueChanged : IEvent + { + public long Value { get; set; } + } + + public sealed class Deleted : IEvent + { + } + public sealed class MultipleByTwiceEvent : IEvent, IMigratedStateEvent { public IEvent Migrate(MyDomainState state) @@ -51,13 +60,4 @@ namespace Squidex.Infrastructure.TestHelpers }; } } - - public sealed class ValueChanged : IEvent - { - public long Value { get; set; } - } - - public sealed class Deleted : IEvent - { - } }