From edc15588a8f3baf72afe9ef1b3e968f50ffa57c2 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 2 Sep 2017 18:59:03 +0200 Subject: [PATCH] Updated domain object to configure scripts. --- .../FieldExtensions.cs | 2 - .../Scripting/JurassicScriptEngine.cs | 8 +- .../Scripting/ScriptUser.cs | 10 +- .../Schemas/ScriptsConfigured.cs | 28 +++++ .../Schemas/MongoSchemaEntity.cs | 22 ++++ .../MongoSchemaRepository_EventHandling.cs | 5 + .../Schemas/ISchemaEntity.cs | 12 ++ .../Implementations/CachingSchemaProvider.cs | 5 + .../Contents/Commands/ContentCommand.cs | 3 + .../Contents/ContentChangedResult.cs | 24 ++++ .../Contents/ContentCommandMiddleware.cs | 117 ++++++++++++++---- .../Contents/ContentDomainObject.cs | 11 +- .../Schemas/Commands/ConfigureScripts.cs | 25 ++++ .../Schemas/SchemaCommandMiddleware.cs | 5 + .../Schemas/SchemaDomainObject.cs | 11 ++ .../CQRS/Commands/AggregateHandler.cs | 4 +- .../Contents/ContentCommandHandlerTests.cs | 54 +++++++- .../Schemas/SchemaCommandHandlerTests.cs | 13 ++ .../Schemas/SchemaDomainObjectTests.cs | 50 ++++++++ 19 files changed, 368 insertions(+), 41 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Events/Schemas/ScriptsConfigured.cs create mode 100644 src/Squidex.Domain.Apps.Write/Contents/ContentChangedResult.cs create mode 100644 src/Squidex.Domain.Apps.Write/Schemas/Commands/ConfigureScripts.cs diff --git a/src/Squidex.Domain.Apps.Core/FieldExtensions.cs b/src/Squidex.Domain.Apps.Core/FieldExtensions.cs index 8b1ba1221..18fe7baeb 100644 --- a/src/Squidex.Domain.Apps.Core/FieldExtensions.cs +++ b/src/Squidex.Domain.Apps.Core/FieldExtensions.cs @@ -40,8 +40,6 @@ namespace Squidex.Domain.Apps.Core public static async Task ValidateAsync(this Field field, JToken value, ValidationContext context, Action addError) { - Guard.NotNull(value, nameof(value)); - try { var typedValue = value.IsNull() ? null : field.ConvertValue(value); diff --git a/src/Squidex.Domain.Apps.Core/Scripting/JurassicScriptEngine.cs b/src/Squidex.Domain.Apps.Core/Scripting/JurassicScriptEngine.cs index 6d1849930..1db52834e 100644 --- a/src/Squidex.Domain.Apps.Core/Scripting/JurassicScriptEngine.cs +++ b/src/Squidex.Domain.Apps.Core/Scripting/JurassicScriptEngine.cs @@ -92,12 +92,16 @@ namespace Squidex.Domain.Apps.Core.Scripting engine.SetGlobalFunction("disallow", new Action(message => { - throw new DomainForbiddenException(!string.IsNullOrWhiteSpace(message) ? message : "Not allowed"); + var exMessage = !string.IsNullOrWhiteSpace(message) ? message : "Not allowed"; + + throw new DomainForbiddenException(exMessage); })); engine.SetGlobalFunction("reject", new Action(message => { - throw new ValidationException($"Failed to {operationName}", !string.IsNullOrWhiteSpace(message) ? new[] { new ValidationError(message) } : null); + var errors = !string.IsNullOrWhiteSpace(message) ? new[] { new ValidationError(message) } : null; + + throw new ValidationException($"Script rejected to to {operationName}.", errors); })); var json = JsonConvert.SerializeObject(context, serializerSettings); diff --git a/src/Squidex.Domain.Apps.Core/Scripting/ScriptUser.cs b/src/Squidex.Domain.Apps.Core/Scripting/ScriptUser.cs index d0a25d3f3..d799f7ff9 100644 --- a/src/Squidex.Domain.Apps.Core/Scripting/ScriptUser.cs +++ b/src/Squidex.Domain.Apps.Core/Scripting/ScriptUser.cs @@ -1,4 +1,12 @@ -using System.Collections.Generic; +// ========================================================================== +// ScriptUser.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; using System.Security.Claims; namespace Squidex.Domain.Apps.Core.Scripting diff --git a/src/Squidex.Domain.Apps.Events/Schemas/ScriptsConfigured.cs b/src/Squidex.Domain.Apps.Events/Schemas/ScriptsConfigured.cs new file mode 100644 index 000000000..e0160a330 --- /dev/null +++ b/src/Squidex.Domain.Apps.Events/Schemas/ScriptsConfigured.cs @@ -0,0 +1,28 @@ +// ========================================================================== +// ScriptsConfigured.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Events.Schemas +{ + [TypeName("ScriptsConfiguredEvent")] + public sealed class ScriptsConfigured : SchemaEvent + { + public string ScriptQuery { get; set; } + + public string ScriptCreate { get; set; } + + public string ScriptUpdate { get; set; } + + public string ScriptDelete { get; set; } + + public string ScriptPublish { get; set; } + + public string ScriptUnpublish { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs index f2af85af0..e896b5dd7 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs @@ -25,6 +25,8 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas [BsonElement] public string Name { get; set; } + public string ScriptUnpublish { get; set; } + [BsonRequired] [BsonElement] public string Schema { get; set; } @@ -53,6 +55,26 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas [BsonElement] public bool IsDeleted { get; set; } + [BsonIgnoreIfNull] + [BsonElement] + public string ScriptQuery { get; set; } + + [BsonIgnoreIfNull] + [BsonElement] + public string ScriptCreate { get; set; } + + [BsonIgnoreIfNull] + [BsonElement] + public string ScriptUpdate { get; set; } + + [BsonIgnoreIfNull] + [BsonElement] + public string ScriptDelete { get; set; } + + [BsonIgnoreIfNull] + [BsonElement] + public string ScriptPublish { get; set; } + Schema ISchemaEntity.Schema { get { return schema.Value; } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs index ad68a206c..d5329163c 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs @@ -103,6 +103,11 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s, registry)); } + protected Task On(ScriptsConfigured @event, EnvelopeHeaders headers) + { + return Collection.UpdateAsync(@event, headers, e => SimpleMapper.Map(@event, e)); + } + protected Task On(SchemaDeleted @event, EnvelopeHeaders headers) { return Collection.UpdateAsync(@event, headers, e => e.IsDeleted = true); diff --git a/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs b/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs index 35844b1d9..55afa8ab2 100644 --- a/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs +++ b/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs @@ -18,6 +18,18 @@ namespace Squidex.Domain.Apps.Read.Schemas bool IsDeleted { get; } + string ScriptQuery { get; } + + string ScriptCreate { get; } + + string ScriptUpdate { get; } + + string ScriptDelete { get; } + + string ScriptPublish { get; } + + string ScriptUnpublish { get; } + Schema Schema { get; } } } diff --git a/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs b/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs index 8d6018204..d57aa4ad8 100644 --- a/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs +++ b/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs @@ -17,6 +17,7 @@ using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.Tasks; +// ReSharper disable ConvertIfStatementToSwitchStatement // ReSharper disable ConvertIfStatementToConditionalTernaryExpression // ReSharper disable InvertIf @@ -125,6 +126,10 @@ namespace Squidex.Domain.Apps.Read.Schemas.Services.Implementations { Remove(schemaUpdatedEvent.AppId, schemaUpdatedEvent.SchemaId); } + else if (@event.Payload is ScriptsConfigured scriptsConfiguredEvent) + { + Remove(scriptsConfiguredEvent.AppId, scriptsConfiguredEvent.SchemaId); + } return TaskHelper.Done; } diff --git a/src/Squidex.Domain.Apps.Write/Contents/Commands/ContentCommand.cs b/src/Squidex.Domain.Apps.Write/Contents/Commands/ContentCommand.cs index 94a02c572..67ec680ed 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/Commands/ContentCommand.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/Commands/ContentCommand.cs @@ -7,12 +7,15 @@ // ========================================================================== using System; +using System.Security.Claims; using Squidex.Infrastructure.CQRS.Commands; namespace Squidex.Domain.Apps.Write.Contents.Commands { public abstract class ContentCommand : SchemaCommand, IAggregateCommand { + public ClaimsPrincipal Principal { get; set; } + public Guid ContentId { get; set; } Guid IAggregateCommand.AggregateId diff --git a/src/Squidex.Domain.Apps.Write/Contents/ContentChangedResult.cs b/src/Squidex.Domain.Apps.Write/Contents/ContentChangedResult.cs new file mode 100644 index 000000000..4550eb7ae --- /dev/null +++ b/src/Squidex.Domain.Apps.Write/Contents/ContentChangedResult.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// ContentChangedResult.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure.CQRS.Commands; + +namespace Squidex.Domain.Apps.Write.Contents +{ + public sealed class ContentChangedResult : EntitySavedResult + { + public NamedContentData Content { get; } + + public ContentChangedResult(NamedContentData content, long version) + : base(version) + { + Content = content; + } + } +} diff --git a/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs b/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs index a2c372e7d..745cf4b81 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs @@ -11,10 +11,14 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Core.Scripting; +using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps.Services; using Squidex.Domain.Apps.Read.Assets.Repositories; using Squidex.Domain.Apps.Read.Contents.Repositories; +using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas.Services; using Squidex.Domain.Apps.Write.Contents.Commands; using Squidex.Infrastructure; @@ -32,66 +36,120 @@ namespace Squidex.Domain.Apps.Write.Contents private readonly IAssetRepository assetRepository; private readonly IContentRepository contentRepository; private readonly ISchemaProvider schemas; + private readonly IScriptEngine scriptEngine; public ContentCommandMiddleware( IAggregateHandler handler, IAppProvider appProvider, IAssetRepository assetRepository, ISchemaProvider schemas, + IScriptEngine scriptEngine, IContentRepository contentRepository) { Guard.NotNull(handler, nameof(handler)); Guard.NotNull(schemas, nameof(schemas)); Guard.NotNull(appProvider, nameof(appProvider)); + Guard.NotNull(scriptEngine, nameof(scriptEngine)); Guard.NotNull(assetRepository, nameof(assetRepository)); Guard.NotNull(contentRepository, nameof(contentRepository)); this.handler = handler; this.schemas = schemas; this.appProvider = appProvider; + this.scriptEngine = scriptEngine; this.assetRepository = assetRepository; this.contentRepository = contentRepository; } protected async Task On(CreateContent command, CommandContext context) { - await ValidateAsync(command, () => "Failed to create content", true); - - await handler.CreateAsync(context, c => + await handler.CreateAsync(context, async content => { - c.Create(command); + var schemaAndApp = await ResolveSchemaAndAppAsync(command); + var scriptContext = CreateScriptContext(content, command, command.Data); + + command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.Schema.ScriptCreate, "create content", true); + command.Data.Enrich(schemaAndApp.Schema.Schema, schemaAndApp.App.PartitionResolver); + + await ValidateAsync(schemaAndApp, command, () => "Failed to create content", false); - context.Complete(EntityCreatedResult.Create(command.Data, c.Version)); + content.Create(command); + + context.Complete(EntityCreatedResult.Create(command.Data, content.Version)); }); } protected async Task On(UpdateContent command, CommandContext context) { - await ValidateAsync(command, () => "Failed to update content"); + await handler.UpdateAsync(context, async content => + { + var schemaAndApp = await ResolveSchemaAndAppAsync(command); + var scriptContext = CreateScriptContext(content, command, command.Data); + + command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.Schema.ScriptUpdate, "update content", true); - await handler.UpdateAsync(context, c => c.Update(command)); + await ValidateAsync(schemaAndApp, command, () => "Failed to update content", false); + + content.Update(command); + + context.Complete(new ContentChangedResult(content.Data, content.Version)); + }); } protected async Task On(PatchContent command, CommandContext context) { - await ValidateAsync(command, () => "Failed to patch content"); + await handler.UpdateAsync(context, async content => + { + var schemaAndApp = await ResolveSchemaAndAppAsync(command); + var scriptContext = CreateScriptContext(content, command, command.Data); + + command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.Schema.ScriptUpdate, "patch content", true); + + await ValidateAsync(schemaAndApp, command, () => "Failed to patch content", true); - await handler.UpdateAsync(context, c => c.Patch(command)); + content.Patch(command); + + context.Complete(new ContentChangedResult(content.Data, content.Version)); + }); } protected Task On(PublishContent command, CommandContext context) { - return handler.UpdateAsync(context, c => c.Publish(command)); + return handler.UpdateAsync(context, async content => + { + var schemaAndApp = await ResolveSchemaAndAppAsync(command); + var scriptContext = CreateScriptContext(content, command); + + scriptEngine.Execute(scriptContext, schemaAndApp.Schema.ScriptPublish, "publish content"); + + content.Publish(command); + }); } protected Task On(UnpublishContent command, CommandContext context) { - return handler.UpdateAsync(context, c => c.Unpublish(command)); + return handler.UpdateAsync(context, async content => + { + var schemaAndApp = await ResolveSchemaAndAppAsync(command); + var scriptContext = CreateScriptContext(content, command); + + scriptEngine.Execute(scriptContext, schemaAndApp.Schema.ScriptUnpublish, "unpublish content"); + + content.Unpublish(command); + }); } protected Task On(DeleteContent command, CommandContext context) { - return handler.UpdateAsync(context, c => c.Delete(command)); + return handler.UpdateAsync(context, async content => + { + var schemaAndApp = await ResolveSchemaAndAppAsync(command); + var scriptContext = CreateScriptContext(content, command); + + scriptEngine.Execute(scriptContext, schemaAndApp.Schema.ScriptDelete, "delete content"); + + content.Delete(command); + }); } public async Task HandleAsync(CommandContext context, Func next) @@ -102,16 +160,10 @@ namespace Squidex.Domain.Apps.Write.Contents } } - private async Task ValidateAsync(ContentDataCommand command, Func message, bool enrich = false) + private async Task ValidateAsync((ISchemaEntity Schema, IAppEntity App) schemaAndApp, ContentDataCommand command, Func message, bool partial) { Guard.Valid(command, nameof(command), message); - var taskForApp = appProvider.FindAppByIdAsync(command.AppId.Id); - var taskForSchema = schemas.FindSchemaByIdAsync(command.SchemaId.Id); - - await Task.WhenAll(taskForApp, taskForSchema); - - var schemaObject = taskForSchema.Result.Schema; var schemaErrors = new List(); var appId = command.AppId.Id; @@ -127,17 +179,34 @@ namespace Squidex.Domain.Apps.Write.Contents return assetRepository.QueryNotFoundAsync(appId, assetIds.ToList()); }); - await command.Data.ValidateAsync(validationContext, schemaObject, taskForApp.Result.PartitionResolver, schemaErrors); + if (partial) + { + await command.Data.ValidatePartialAsync(validationContext, schemaAndApp.Schema.Schema, schemaAndApp.App.PartitionResolver, schemaErrors); + } + else + { + await command.Data.ValidateAsync(validationContext, schemaAndApp.Schema.Schema, schemaAndApp.App.PartitionResolver, schemaErrors); + } if (schemaErrors.Count > 0) { throw new ValidationException(message(), schemaErrors); } + } - if (enrich) - { - command.Data.Enrich(schemaObject, taskForApp.Result.PartitionResolver); - } + private static ScriptContext CreateScriptContext(ContentDomainObject content, ContentCommand command, NamedContentData data = null) + { + return new ScriptContext { ContentId = content.Id, Data = data, OldData = content.Data, User = ScriptUser.Create(command.Principal) }; + } + + private async Task<(ISchemaEntity Schema, IAppEntity App)> ResolveSchemaAndAppAsync(SchemaCommand command) + { + var taskForApp = appProvider.FindAppByIdAsync(command.AppId.Id); + var taskForSchema = schemas.FindSchemaByIdAsync(command.SchemaId.Id); + + await Task.WhenAll(taskForApp, taskForSchema); + + return (taskForSchema.Result, taskForApp.Result); } } } diff --git a/src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs b/src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs index ec6d885b0..df6ae3a99 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs @@ -35,6 +35,11 @@ namespace Squidex.Domain.Apps.Write.Contents get { return isPublished; } } + public NamedContentData Data + { + get { return data; } + } + public ContentDomainObject(Guid id, int version) : base(id, version) { @@ -122,7 +127,7 @@ namespace Squidex.Domain.Apps.Write.Contents VerifyCreatedAndNotDeleted(); - if (!command.Data.Equals(data)) + if (!command.Data.Equals(Data)) { RaiseEvent(SimpleMapper.Map(command, new ContentUpdated())); } @@ -136,9 +141,9 @@ namespace Squidex.Domain.Apps.Write.Contents VerifyCreatedAndNotDeleted(); - var newData = data.MergeInto(command.Data); + var newData = Data.MergeInto(command.Data); - if (!newData.Equals(data)) + if (!newData.Equals(Data)) { RaiseEvent(SimpleMapper.Map(command, new ContentUpdated { Data = newData })); } diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Commands/ConfigureScripts.cs b/src/Squidex.Domain.Apps.Write/Schemas/Commands/ConfigureScripts.cs new file mode 100644 index 000000000..9d605487b --- /dev/null +++ b/src/Squidex.Domain.Apps.Write/Schemas/Commands/ConfigureScripts.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// ConfigureScripts.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +namespace Squidex.Domain.Apps.Write.Schemas.Commands +{ + public sealed class ConfigureScripts : SchemaAggregateCommand + { + public string ScriptQuery { get; set; } + + public string ScriptCreate { get; set; } + + public string ScriptUpdate { get; set; } + + public string ScriptDelete { get; set; } + + public string ScriptPublish { get; set; } + + public string ScriptUnpublish { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs b/src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs index 391bed097..39f9c925e 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs @@ -120,6 +120,11 @@ namespace Squidex.Domain.Apps.Write.Schemas return handler.UpdateAsync(context, s => s.Unpublish(command)); } + protected Task On(ConfigureScripts command, CommandContext context) + { + return handler.UpdateAsync(context, s => s.ConfigureScripts(command)); + } + public async Task HandleAsync(CommandContext context, Func next) { if (!await this.DispatchActionAsync(context.Command, context)) diff --git a/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs b/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs index 377d5c76b..884394840 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs @@ -276,6 +276,17 @@ namespace Squidex.Domain.Apps.Write.Schemas return this; } + public SchemaDomainObject ConfigureScripts(ConfigureScripts command) + { + Guard.NotNull(command, nameof(command)); + + VerifyCreatedAndNotDeleted(); + + RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); + + return this; + } + public SchemaDomainObject Delete(DeleteSchema command) { VerifyCreatedAndNotDeleted(); diff --git a/src/Squidex.Infrastructure/CQRS/Commands/AggregateHandler.cs b/src/Squidex.Infrastructure/CQRS/Commands/AggregateHandler.cs index 1f8a991e8..7f02a1b58 100644 --- a/src/Squidex.Infrastructure/CQRS/Commands/AggregateHandler.cs +++ b/src/Squidex.Infrastructure/CQRS/Commands/AggregateHandler.cs @@ -87,9 +87,7 @@ namespace Squidex.Infrastructure.CQRS.Commands private static IAggregateCommand GetCommand(CommandContext context) { - var command = context.Command as IAggregateCommand; - - if (command == null) + if (!(context.Command is IAggregateCommand command)) { throw new ArgumentException("Context must have an aggregate command.", nameof(context)); } diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandHandlerTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandHandlerTests.cs index 0f0ad7964..221ab88fb 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandHandlerTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandHandlerTests.cs @@ -12,6 +12,7 @@ using FakeItEasy; using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps.Services; using Squidex.Domain.Apps.Read.Assets.Repositories; @@ -34,8 +35,10 @@ namespace Squidex.Domain.Apps.Write.Contents private readonly ContentDomainObject content; private readonly ISchemaProvider schemaProvider = A.Fake(); private readonly ISchemaEntity schemaEntity = A.Fake(); + private readonly IScriptEngine scriptEngine = A.Fake(); private readonly IAppProvider appProvider = A.Fake(); private readonly IAppEntity appEntity = A.Fake(); + private readonly NamedContentData invalidData = new NamedContentData().AddField("my-field", new ContentFieldData().SetValue(null)); private readonly NamedContentData data = new NamedContentData().AddField("my-field", new ContentFieldData().SetValue(1)); private readonly LanguagesConfig languagesConfig = LanguagesConfig.Create(Language.DE); private readonly Guid contentId = Guid.NewGuid(); @@ -49,7 +52,7 @@ namespace Squidex.Domain.Apps.Write.Contents content = new ContentDomainObject(contentId, -1); - sut = new ContentCommandMiddleware(Handler, appProvider, A.Dummy(), schemaProvider, A.Dummy()); + sut = new ContentCommandMiddleware(Handler, appProvider, A.Dummy(), schemaProvider, scriptEngine, A.Dummy()); A.CallTo(() => appEntity.LanguagesConfig).Returns(languagesConfig); A.CallTo(() => appEntity.PartitionResolver).Returns(languagesConfig.ToResolver()); @@ -62,7 +65,9 @@ namespace Squidex.Domain.Apps.Write.Contents [Fact] public async Task Create_should_throw_exception_if_data_is_not_valid() { - var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = new NamedContentData() }); + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored, A.Ignored, true)).Returns(invalidData); + + var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = invalidData }); await TestCreate(content, async _ => { @@ -73,6 +78,9 @@ namespace Squidex.Domain.Apps.Write.Contents [Fact] public async Task Create_should_create_content() { + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored, A.Ignored, true)).Returns(data); + A.CallTo(() => schemaEntity.ScriptCreate).Returns(""); + var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = data }); await TestCreate(content, async _ => @@ -81,14 +89,18 @@ namespace Squidex.Domain.Apps.Write.Contents }); Assert.Equal(data, context.Result>().IdOrValue); + + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "", "create content", true)).MustHaveHappened(); } [Fact] public async Task Update_should_throw_exception_if_data_is_not_valid() { + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored, A.Ignored, true)).Returns(invalidData); + CreateContent(); - var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = new NamedContentData() }); + var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = invalidData }); await TestUpdate(content, async _ => { @@ -99,6 +111,9 @@ namespace Squidex.Domain.Apps.Write.Contents [Fact] public async Task Update_should_update_domain_object() { + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored, A.Ignored, true)).Returns(data); + A.CallTo(() => schemaEntity.ScriptUpdate).Returns(""); + CreateContent(); var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = data }); @@ -107,14 +122,20 @@ namespace Squidex.Domain.Apps.Write.Contents { await sut.HandleAsync(context); }); + + Assert.Equal(data, context.Result().Content); + + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "", "update content", true)).MustHaveHappened(); } [Fact] public async Task Patch_should_throw_exception_if_data_is_not_valid() { + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored, A.Ignored, true)).Returns(invalidData); + CreateContent(); - var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = new NamedContentData() }); + var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = invalidData }); await TestUpdate(content, async _ => { @@ -125,21 +146,32 @@ namespace Squidex.Domain.Apps.Write.Contents [Fact] public async Task Patch_should_update_domain_object() { - var otherContent = new NamedContentData().AddField("my-field", new ContentFieldData().SetValue(3)); + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored, A.Ignored, true)).Returns(data); + A.CallTo(() => schemaEntity.ScriptUpdate).Returns(""); + + var path = new NamedContentData().AddField("my-field", new ContentFieldData().SetValue(3)); + + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored, A.Ignored, true)).Returns(path); CreateContent(); - var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = otherContent }); + var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = path }); await TestUpdate(content, async _ => { await sut.HandleAsync(context); }); + + Assert.NotNull(context.Result().Content); + + A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "", "patch content", true)).MustHaveHappened(); } [Fact] public async Task Publish_should_publish_domain_object() { + A.CallTo(() => schemaEntity.ScriptPublish).Returns(""); + CreateContent(); var context = CreateContextForCommand(new PublishContent { ContentId = contentId }); @@ -148,11 +180,15 @@ namespace Squidex.Domain.Apps.Write.Contents { await sut.HandleAsync(context); }); + + A.CallTo(() => scriptEngine.Execute(A.Ignored, "", "publish content")).MustHaveHappened(); } [Fact] public async Task Unpublish_should_unpublish_domain_object() { + A.CallTo(() => schemaEntity.ScriptUnpublish).Returns(""); + CreateContent(); var context = CreateContextForCommand(new UnpublishContent { ContentId = contentId }); @@ -161,11 +197,15 @@ namespace Squidex.Domain.Apps.Write.Contents { await sut.HandleAsync(context); }); + + A.CallTo(() => scriptEngine.Execute(A.Ignored, "", "unpublish content")).MustHaveHappened(); } [Fact] public async Task Delete_should_update_domain_object() { + A.CallTo(() => schemaEntity.ScriptDelete).Returns(""); + CreateContent(); var command = CreateContextForCommand(new DeleteContent { ContentId = contentId }); @@ -174,6 +214,8 @@ namespace Squidex.Domain.Apps.Write.Contents { await sut.HandleAsync(command); }); + + A.CallTo(() => scriptEngine.Execute(A.Ignored, "", "delete content")).MustHaveHappened(); } private void CreateContent() diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandHandlerTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandHandlerTests.cs index 87ed80fb5..c4452047f 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandHandlerTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandHandlerTests.cs @@ -125,6 +125,19 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } + [Fact] + public async Task ConfigureScripts_should_update_domain_object() + { + CreateSchema(); + + var context = CreateContextForCommand(new ConfigureScripts()); + + await TestUpdate(schema, async _ => + { + await sut.HandleAsync(context); + }); + } + [Fact] public async Task DeleteSchema_should_update_domain_object() { diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs index 98301182b..5ef7f53eb 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs @@ -159,6 +159,56 @@ namespace Squidex.Domain.Apps.Write.Schemas ); } + [Fact] + public void ConfigureScripts_should_throw_exception_if_not_created() + { + Assert.Throws(() => + { + sut.ConfigureScripts(CreateCommand(new ConfigureScripts())); + }); + } + + [Fact] + public void ConfigureScripts_should_throw_exception_if_schema_is_deleted() + { + CreateSchema(); + DeleteSchema(); + + Assert.Throws(() => + { + sut.ConfigureScripts(CreateCommand(new ConfigureScripts())); + }); + } + + [Fact] + public void ConfigureScripts_should_create_events() + { + CreateSchema(); + + sut.ConfigureScripts(CreateCommand(new ConfigureScripts + { + ScriptCreate = "", + ScriptUpdate = "", + ScriptDelete = "", + ScriptPublish = "", + ScriptUnpublish = "", + ScriptQuery = "", + })); + + sut.GetUncomittedEvents() + .ShouldHaveSameEvents( + CreateEvent(new ScriptsConfigured + { + ScriptCreate = "", + ScriptUpdate = "", + ScriptDelete = "", + ScriptPublish = "", + ScriptUnpublish = "", + ScriptQuery = "", + }) + ); + } + [Fact] public void Reorder_should_throw_exception_if_not_created() {