From dcf44d303ddbf398e1b9865565fef92518162937 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Mon, 18 Dec 2017 23:35:52 +0100 Subject: [PATCH] Simplified schema resolving. --- .../MongoContentRepository_SnapshotStore.cs | 27 +++++----- .../Schemas/MongoSchemaEntity.cs | 4 ++ .../Schemas/MongoSchemaRepository.cs | 18 +++---- .../MongoSchemaRepository_SnapshotStore.cs | 3 +- .../AppProvider.cs | 54 +++++++++++-------- .../IAppProvider.cs | 4 +- .../Schemas/Repositories/ISchemaRepository.cs | 4 +- .../Contents/ContentQueryServiceTests.cs | 15 +++--- .../Rules/Guards/GuardRuleTests.cs | 2 +- .../Triggers/ContentChangedTriggerTests.cs | 4 +- .../Rules/RuleCommandMiddlewareTests.cs | 2 +- .../Schemas/Guards/GuardSchemaTests.cs | 4 +- .../Schemas/SchemaCommandMiddlewareTests.cs | 4 +- tools/Migrate_01/Migration01_FromCqrs.cs | 25 +++++---- 14 files changed, 98 insertions(+), 72 deletions(-) diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs index 8eadcba64..9746b4055 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using MongoDB.Driver; using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Entities.Contents.State; +using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Reflection; @@ -28,12 +29,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents if (contentEntity != null) { - var schema = await appProvider.GetSchemaAsync(contentEntity.AppId, contentEntity.SchemaId, true); - - if (schema == null) - { - throw new InvalidOperationException($"Cannot find schema {contentEntity.SchemaId}"); - } + var schema = await GetSchemaAsync(contentEntity.AppId, contentEntity.SchemaId); contentEntity?.ParseData(schema.SchemaDef); @@ -52,12 +48,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents return; } - var schema = await appProvider.GetSchemaAsync(value.AppId, value.SchemaId, true); - - if (schema == null) - { - throw new InvalidOperationException($"Cannot find schema {value.SchemaId}"); - } + var schema = await GetSchemaAsync(value.AppId, value.SchemaId); var idData = value.Data?.ToIdModel(schema.SchemaDef, true); @@ -94,5 +85,17 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents } } } + + private async Task GetSchemaAsync(Guid appId, Guid schemaId) + { + var schema = await appProvider.GetSchemaAsync(appId, schemaId); + + if (schema == null) + { + throw new DomainObjectNotFoundException(schemaId.ToString(), typeof(ISchemaEntity)); + } + + return schema; + } } } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaEntity.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaEntity.cs index f01d871f1..97aa996ad 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaEntity.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaEntity.cs @@ -38,5 +38,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas [BsonElement] [BsonRequired] public int Version { get; set; } + + [BsonElement] + [BsonRequired] + public bool IsDeleted { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs index 57ebd9d05..efe80a7a8 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs @@ -30,23 +30,23 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas protected override async Task SetupCollectionAsync(IMongoCollection collection) { - await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId)); - await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Name)); + await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Ascending(x => x.IsDeleted)); + await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Ascending(x => x.Name).Ascending(x => x.IsDeleted)); } - public async Task> QueryAllSchemaIdsAsync(Guid appId, string name) + public async Task FindSchemaIdAsync(Guid appId, string name) { - var schemaEntities = - await Collection.Find(x => x.AppId == appId && x.Name == name).Only(x => x.Id).SortByDescending(x => x.Version) - .ToListAsync(); + var schemaEntity = + await Collection.Find(x => x.AppId == appId && x.Name == name && !x.IsDeleted).Only(x => x.Id).SortByDescending(x => x.Version) + .FirstOrDefaultAsync(); - return schemaEntities.Select(x => Guid.Parse(x["_id"].AsString)).ToList(); + return schemaEntity != null ? Guid.Parse(schemaEntity["_id"].AsString) : Guid.Empty; } - public async Task> QueryAllSchemaIdsAsync(Guid appId) + public async Task> QuerySchemaIdsAsync(Guid appId) { var schemaEntities = - await Collection.Find(x => x.AppId == appId).Only(x => x.Id) + await Collection.Find(x => x.AppId == appId && !x.IsDeleted).Only(x => x.Id) .ToListAsync(); return schemaEntities.Select(x => Guid.Parse(x["_id"].AsString)).ToList(); diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs index a2ad87135..ba8e6a93e 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs @@ -41,7 +41,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas .Set(x => x.State, value) .Set(x => x.AppId, value.AppId) .Set(x => x.Name, value.Name) - .Set(x => x.Version, newVersion), + .Set(x => x.Version, newVersion) + .Set(x => x.IsDeleted, value.IsDeleted), Upsert); } catch (MongoWriteException ex) diff --git a/src/Squidex.Domain.Apps.Entities/AppProvider.cs b/src/Squidex.Domain.Apps.Entities/AppProvider.cs index fd727c7db..5ed2eb7d8 100644 --- a/src/Squidex.Domain.Apps.Entities/AppProvider.cs +++ b/src/Squidex.Domain.Apps.Entities/AppProvider.cs @@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Repositories; using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities @@ -49,14 +50,19 @@ namespace Squidex.Domain.Apps.Entities { var app = await stateFactory.GetSingleAsync(appId); - if (IsFound(app)) + if (!IsFound(app)) { return (null, null); } var schema = await stateFactory.GetSingleAsync(id); - return IsFound(schema, false) ? (null, null) : (app.State, schema.State); + if (!IsFound(schema) || schema.State.IsDeleted) + { + return (null, null); + } + + return (app.State, schema.State); } public async Task GetAppAsync(string appName) @@ -68,38 +74,42 @@ namespace Squidex.Domain.Apps.Entities return null; } - var app = await stateFactory.GetSingleAsync(appId); - - return IsFound(app) ? app.State : null; + return (await stateFactory.GetSingleAsync(appId)).State; } - public async Task GetSchemaAsync(Guid appId, Guid id, bool provideDeleted = false) + public async Task GetSchemaAsync(Guid appId, string name) { - var schema = await stateFactory.GetSingleAsync(id); + var schemaId = await GetSchemaIdAsync(appId, name); + + if (schemaId == Guid.Empty) + { + return null; + } - return IsFound(schema, provideDeleted) ? schema.State : null; + return (await stateFactory.GetSingleAsync(schemaId)).State; } - public async Task GetSchemaAsync(Guid appId, string name, bool provideDeleted = false) + public async Task GetSchemaAsync(Guid appId, Guid id) { - var ids = await schemaRepository.QueryAllSchemaIdsAsync(appId, name); + var schema = await stateFactory.GetSingleAsync(id); - var schemas = - await Task.WhenAll( - ids.Select(id => stateFactory.GetSingleAsync(id))); + if (!IsFound(schema)) + { + return null; + } - return schemas.OrderByDescending(x => x.State.LastModified).FirstOrDefault(s => IsFound(s, provideDeleted))?.State; + return schema.State; } public async Task> GetSchemasAsync(Guid appId) { - var ids = await schemaRepository.QueryAllSchemaIdsAsync(appId); + var ids = await schemaRepository.QuerySchemaIdsAsync(appId); var schemas = await Task.WhenAll( ids.Select(id => stateFactory.GetSingleAsync(id))); - return schemas.Where(s => IsFound(s)).Select(s => (ISchemaEntity)s.State).ToList(); + return schemas.Where(IsFound).Select(s => (ISchemaEntity)s.State).ToList(); } public async Task> GetRulesAsync(Guid appId) @@ -110,7 +120,7 @@ namespace Squidex.Domain.Apps.Entities await Task.WhenAll( ids.Select(id => stateFactory.GetSingleAsync(id))); - return rules.Select(r => (IRuleEntity)r.State).ToList(); + return rules.Where(IsFound).Select(r => (IRuleEntity)r.State).ToList(); } public async Task> GetUserApps(string userId) @@ -121,7 +131,7 @@ namespace Squidex.Domain.Apps.Entities await Task.WhenAll( ids.Select(id => stateFactory.GetSingleAsync(id))); - return apps.Select(a => (IAppEntity)a.State).ToList(); + return apps.Where(IsFound).Select(a => (IAppEntity)a.State).ToList(); } private Task GetAppIdAsync(string name) @@ -129,14 +139,14 @@ namespace Squidex.Domain.Apps.Entities return appRepository.FindAppIdByNameAsync(name); } - private static bool IsFound(AppDomainObject app) + private async Task GetSchemaIdAsync(Guid appId, string name) { - return app.Version >= 0; + return await schemaRepository.FindSchemaIdAsync(appId, name); } - private static bool IsFound(SchemaDomainObject schema, bool provideDeleted = false) + private static bool IsFound(IDomainObject app) { - return schema.Version >= 0 && (!schema.State.IsDeleted || provideDeleted); + return app.Version > EtagVersion.Empty; } } } diff --git a/src/Squidex.Domain.Apps.Entities/IAppProvider.cs b/src/Squidex.Domain.Apps.Entities/IAppProvider.cs index a8bef2d45..7dd382589 100644 --- a/src/Squidex.Domain.Apps.Entities/IAppProvider.cs +++ b/src/Squidex.Domain.Apps.Entities/IAppProvider.cs @@ -21,9 +21,9 @@ namespace Squidex.Domain.Apps.Entities Task GetAppAsync(string appName); - Task GetSchemaAsync(Guid appId, Guid id, bool provideDeleted = false); + Task GetSchemaAsync(Guid appId, Guid id); - Task GetSchemaAsync(Guid appId, string name, bool provideDeleted = false); + Task GetSchemaAsync(Guid appId, string name); Task> GetSchemasAsync(Guid appId); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Repositories/ISchemaRepository.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Repositories/ISchemaRepository.cs index 103b19ac3..642072660 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Repositories/ISchemaRepository.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Repositories/ISchemaRepository.cs @@ -14,8 +14,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Repositories { public interface ISchemaRepository { - Task> QueryAllSchemaIdsAsync(Guid appId, string name); + Task FindSchemaIdAsync(Guid appId, string name); - Task> QueryAllSchemaIdsAsync(Guid appId); + Task> QuerySchemaIdsAsync(Guid appId); } } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs index adb46576b..eb276760a 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs @@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Contents [Fact] public async Task Should_return_schema_from_id_if_string_is_guid() { - A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId)) .Returns(schema); var result = await sut.FindSchemaAsync(app, schemaId.ToString()); @@ -72,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Contents [Fact] public async Task Should_return_schema_from_name_if_string_not_guid() { - A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema", false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema")) .Returns(schema); var result = await sut.FindSchemaAsync(app, "my-schema"); @@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Entities.Contents [Fact] public async Task Should_throw_if_schema_not_found() { - A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema", false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId, "my-schema")) .Returns((ISchemaEntity)null); await Assert.ThrowsAsync(() => sut.FindSchemaAsync(app, "my-schema")); @@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Contents [Fact] public async Task Should_return_content_from_repository_and_transform() { - A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId)) .Returns(schema); A.CallTo(() => contentRepository.FindContentAsync(app, schema, contentId)) .Returns(content); @@ -114,8 +114,9 @@ namespace Squidex.Domain.Apps.Entities.Contents [Fact] public async Task Should_throw_if_content_to_find_does_not_exist() { - A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId)) .Returns(schema); + A.CallTo(() => contentRepository.FindContentAsync(app, schema, contentId)) .Returns((IContentEntity)null); @@ -196,7 +197,7 @@ namespace Squidex.Domain.Apps.Entities.Contents private void SetupFakeWithIdQuery(Status[] status, HashSet ids) { - A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId)) .Returns(schema); A.CallTo(() => contentRepository.QueryAsync(app, schema, A.That.IsSameSequenceAs(status), ids)) @@ -205,7 +206,7 @@ namespace Squidex.Domain.Apps.Entities.Contents private void SetupFakeWithOdataQuery(Status[] status) { - A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId)) .Returns(schema); A.CallTo(() => contentRepository.QueryAsync(app, schema, A.That.IsSameSequenceAs(status), A.Ignored)) diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs index 81d5f20fe..8498f15f1 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs @@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards public GuardRuleTests() { - A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A.Ignored, false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, A.Ignored)) .Returns(A.Fake()); } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs index f24ecd9cd..127b6e53a 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs @@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers [Fact] public async Task Should_add_error_if_schemas_ids_are_not_valid() { - A.CallTo(() => appProvider.GetSchemaAsync(appId, A.Ignored, false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId, A.Ignored)) .Returns(Task.FromResult(null)); var trigger = new ContentChangedTrigger @@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers [Fact] public async Task Should_not_add_error_if_schemas_ids_are_valid() { - A.CallTo(() => appProvider.GetSchemaAsync(appId, A.Ignored, false)) + A.CallTo(() => appProvider.GetSchemaAsync(appId, A.Ignored)) .Returns(A.Fake()); var trigger = new ContentChangedTrigger diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleCommandMiddlewareTests.cs index 61ab2de50..4f629fc7f 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleCommandMiddlewareTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleCommandMiddlewareTests.cs @@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Entities.Rules public RuleCommandMiddlewareTests() { - A.CallTo(() => appProvider.GetSchemaAsync(A.Ignored, A.Ignored, false)) + A.CallTo(() => appProvider.GetSchemaAsync(A.Ignored, A.Ignored)) .Returns(A.Fake()); sut = new RuleCommandMiddleware(Handler, appProvider); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs index be2bf2775..7eb912705 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs @@ -33,7 +33,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards .AddField(new StringField(1, "field1", Partitioning.Invariant)) .AddField(new StringField(2, "field2", Partitioning.Invariant)); - A.CallTo(() => appProvider.GetSchemaAsync(A.Ignored, "new-schema", false)) + A.CallTo(() => appProvider.GetSchemaAsync(A.Ignored, "new-schema")) .Returns(Task.FromResult(null)); } @@ -48,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards [Fact] public Task CanCreate_should_throw_exception_if_name_already_in_use() { - A.CallTo(() => appProvider.GetSchemaAsync(A.Ignored, "new-schema", false)) + A.CallTo(() => appProvider.GetSchemaAsync(A.Ignored, "new-schema")) .Returns(Task.FromResult(A.Fake())); var command = new CreateSchema { AppId = appId, Name = "new-schema" }; diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandMiddlewareTests.cs index 1cd2a9a1d..939ca3802 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandMiddlewareTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandMiddlewareTests.cs @@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas sut = new SchemaCommandMiddleware(Handler, appProvider); - A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName, false)) + A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName)) .Returns((ISchemaEntity)null); } @@ -54,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas Assert.Equal(SchemaId, context.Result>().IdOrValue); - A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName, false)).MustHaveHappened(); + A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName)).MustHaveHappened(); } [Fact] diff --git a/tools/Migrate_01/Migration01_FromCqrs.cs b/tools/Migrate_01/Migration01_FromCqrs.cs index 2dcc7c765..84dd96462 100644 --- a/tools/Migrate_01/Migration01_FromCqrs.cs +++ b/tools/Migrate_01/Migration01_FromCqrs.cs @@ -56,7 +56,22 @@ namespace Migrate_01 { var version = storedEvent.EventStreamNumber; - if (@event.Payload is AssetEvent assetEvent) + if (@event.Payload is ContentEvent contentEvent) + { + try + { + var content = await stateFactory.CreateAsync(contentEvent.ContentId); + + content.UpdateState(content.State.Apply(@event)); + + await content.WriteStateAsync(version); + } + catch (DomainObjectNotFoundException) + { + // Schema has been deleted. + } + } + else if (@event.Payload is AssetEvent assetEvent) { var asset = await stateFactory.CreateAsync(assetEvent.AssetId); @@ -64,14 +79,6 @@ namespace Migrate_01 await asset.WriteStateAsync(version); } - else if (@event.Payload is ContentEvent contentEvent) - { - var content = await stateFactory.CreateAsync(contentEvent.ContentId); - - content.UpdateState(content.State.Apply(@event)); - - await content.WriteStateAsync(version); - } else if (@event.Payload is SchemaEvent schemaEvent) { var schema = await stateFactory.GetSingleAsync(schemaEvent.SchemaId.Id);