diff --git a/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs b/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs index 8512ce30b..eef025594 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs @@ -138,9 +138,9 @@ namespace Squidex.Domain.Apps.Core.Scripting contextInstance.FastAddProperty("data", new ContentDataObject(engine, context.Data), true, true, true); } - if (context.OldData != null) + if (context.DataOld != null) { - contextInstance.FastAddProperty("oldData", new ContentDataObject(engine, context.OldData), true, true, true); + contextInstance.FastAddProperty("oldData", new ContentDataObject(engine, context.DataOld), true, true, true); } if (context.User != null) @@ -150,7 +150,14 @@ namespace Squidex.Domain.Apps.Core.Scripting if (!string.IsNullOrWhiteSpace(context.Operation)) { - contextInstance.FastAddProperty("operation", context.Operation, false, true, false); + contextInstance.FastAddProperty("operation", context.Operation, false, false, false); + } + + contextInstance.FastAddProperty("status", context.Status.ToString(), false, false, false); + + if (context.StatusOld != default) + { + contextInstance.FastAddProperty("oldStatus", context.StatusOld.ToString(), false, false, false); } engine.SetValue("ctx", contextInstance); diff --git a/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptContext.cs b/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptContext.cs index d3d631a40..40d4212ae 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptContext.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptContext.cs @@ -19,7 +19,11 @@ namespace Squidex.Domain.Apps.Core.Scripting public NamedContentData Data { get; set; } - public NamedContentData OldData { get; set; } + public NamedContentData DataOld { get; set; } + + public Status Status { get; set; } + + public Status StatusOld { get; set; } public string Operation { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs index 63f7022e6..70c910dd2 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs @@ -68,9 +68,11 @@ namespace Squidex.Domain.Apps.Entities.Contents { var ctx = await CreateContext(c.AppId.Id, c.SchemaId.Id, Guid.Empty, () => "Failed to create content."); + var statusInfo = await contentWorkflow.GetInitialStatusAsync(ctx.Schema); + await GuardContent.CanCreate(ctx.Schema, contentWorkflow, c); - await ctx.ExecuteScriptAndTransformAsync(s => s.Create, "Create", c, c.Data); + await ctx.ExecuteScriptAndTransformAsync(s => s.Create, "Create", c, c.Data, statusInfo.Status); await ctx.EnrichAsync(c.Data); if (!c.DoNotValidate) @@ -80,11 +82,9 @@ namespace Squidex.Domain.Apps.Entities.Contents if (c.Publish) { - await ctx.ExecuteScriptAsync(s => s.Change, "Published", c, c.Data); + await ctx.ExecuteScriptAsync(s => s.Change, "Published", c, c.Data, Status.Published); } - var statusInfo = await contentWorkflow.GetInitialStatusAsync(ctx.Schema); - Create(c, statusInfo.Status); return Snapshot; @@ -148,7 +148,7 @@ namespace Squidex.Domain.Apps.Entities.Contents reason = StatusChange.Change; } - await ctx.ExecuteScriptAsync(s => s.Change, reason, c, Snapshot.Data); + await ctx.ExecuteScriptAsync(s => s.Change, reason, c, null, c.Status); ChangeStatus(c, reason); } @@ -186,7 +186,7 @@ namespace Squidex.Domain.Apps.Entities.Contents GuardContent.CanDelete(ctx.Schema, c); - await ctx.ExecuteScriptAsync(s => s.Delete, "Delete", c, Snapshot.Data); + await ctx.ExecuteScriptAsync(s => s.Delete, "Delete", c, null); Delete(c); }); @@ -218,7 +218,7 @@ namespace Squidex.Domain.Apps.Entities.Contents await ctx.ValidateAsync(command.Data); } - newData = await ctx.ExecuteScriptAndTransformAsync(s => s.Update, "Update", command, newData, Snapshot.Data); + newData = await ctx.ExecuteScriptAndTransformAsync(s => s.Update, "Update", command, newData); if (isProposal) { @@ -309,7 +309,7 @@ namespace Squidex.Domain.Apps.Entities.Contents private async Task CreateContext(Guid appId, Guid schemaId, Guid contentId, Func message) { var operationContext = - await ContentOperationContext.CreateAsync(appId, schemaId, contentId, + await ContentOperationContext.CreateAsync(appId, schemaId, contentId, Snapshot, appProvider, assetRepository, contentRepository, scriptEngine, message); return operationContext; diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs index 9d851ece7..ee7bf9bdb 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs @@ -30,6 +30,7 @@ namespace Squidex.Domain.Apps.Entities.Contents private IScriptEngine scriptEngine; private ISchemaEntity schemaEntity; private IAppEntity appEntity; + private IContentEntity contentEntity; private Guid contentId; private Guid schemaId; private Func message; @@ -43,6 +44,7 @@ namespace Squidex.Domain.Apps.Entities.Contents Guid appId, Guid schemaId, Guid contentId, + IContentEntity contentEntity, IAppProvider appProvider, IAssetRepository assetRepository, IContentRepository contentRepository, @@ -56,6 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Contents appEntity = appEntity, assetRepository = assetRepository, contentId = contentId, + contentEntity = contentEntity, contentRepository = contentRepository, message = message, schemaId = schemaId, @@ -87,27 +90,51 @@ namespace Squidex.Domain.Apps.Entities.Contents return data.ValidatePartialAsync(ctx, schemaEntity.SchemaDef, appEntity.PartitionResolver(), message); } - public Task ExecuteScriptAndTransformAsync(Func script, object operation, ContentCommand command, NamedContentData data, NamedContentData oldData = null) + public Task ExecuteScriptAndTransformAsync(Func script, object operation, ContentCommand command, + NamedContentData data, Status? status = null) { - var ctx = CreateScriptContext(operation, command, data, oldData); + var ctx = CreateScriptContext(operation, command, data, status); var result = scriptEngine.ExecuteAndTransform(ctx, GetScript(script)); return Task.FromResult(result); } - public Task ExecuteScriptAsync(Func script, object operation, ContentCommand command, NamedContentData data, NamedContentData oldData = null) + public Task ExecuteScriptAsync(Func script, object operation, ContentCommand command, + NamedContentData data, Status? status = null) { - var ctx = CreateScriptContext(operation, command, data, oldData); + var ctx = CreateScriptContext(operation, command, data, status); scriptEngine.Execute(ctx, GetScript(script)); return TaskHelper.Done; } - private static ScriptContext CreateScriptContext(object operation, ContentCommand command, NamedContentData data, NamedContentData oldData) + private ScriptContext CreateScriptContext(object operation, ContentCommand command, NamedContentData data, Status? status) { - return new ScriptContext { ContentId = command.ContentId, OldData = oldData, Data = data, User = command.User, Operation = operation.ToString() }; + var result = new ScriptContext { ContentId = command.ContentId, Data = data, User = command.User, Operation = operation.ToString() }; + + if (data != null) + { + result.Data = data; + result.DataOld = contentEntity?.Data; + } + else + { + result.Data = contentEntity?.Data; + } + + if (status.HasValue) + { + result.Status = status.Value; + result.StatusOld = contentEntity?.Status ?? default; + } + else + { + result.Status = contentEntity?.Status ?? default; + } + + return result; } private ValidationContext CreateValidationContext() diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs index 0d25ccfde..a93f59289 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs @@ -261,7 +261,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting userIdentity.AddClaim(new Claim(OpenIdClaims.ClientId, "2")); - var context = new ScriptContext { Data = content, OldData = oldContent, User = userPrincipal }; + var context = new ScriptContext { Data = content, DataOld = oldContent, User = userPrincipal }; var result = sut.ExecuteAndTransform(context, @" ctx.data.number0.iv = ctx.data.number0.iv + ctx.oldData.number0.iv * parseInt(ctx.user.id, 10); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs index 948971ffb..9b9fadc65 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs @@ -134,7 +134,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentCreated { Data = data, Status = Status.Draft }) ); - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")) + A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(data, null, Status.Draft, default), "")) .MustHaveHappened(); A.CallTo(() => scriptEngine.Execute(A.Ignored, "")) .MustNotHaveHappened(); @@ -157,9 +157,9 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentStatusChanged { Status = Status.Published, Change = StatusChange.Published }) ); - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")) + A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(data, null, Status.Draft, default), "")) .MustHaveHappened(); - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")) + A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Published, Status.Draft), "")) .MustHaveHappened(); } @@ -187,7 +187,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentUpdated { Data = otherData }) ); - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")) + A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(otherData, data, Status.Draft, default), "")) .MustHaveHappened(); } @@ -210,7 +210,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentUpdateProposed { Data = otherData }) ); - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")) + A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(otherData, data, Status.Published, default), "")) .MustHaveHappened(); } @@ -257,7 +257,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentUpdated { Data = patched }) ); - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")) + A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(patched, data, Status.Draft, default), "")) .MustHaveHappened(); } @@ -280,7 +280,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentUpdateProposed { Data = patched }) ); - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")) + A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(patched, data, Status.Published, default), "")) .MustHaveHappened(); } @@ -319,7 +319,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentStatusChanged { Change = StatusChange.Published, Status = Status.Published }) ); - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")) + A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Published, Status.Draft), "")) .MustHaveHappened(); } @@ -341,7 +341,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentStatusChanged { Status = Status.Archived }) ); - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")) + A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Archived, Status.Draft), "")) .MustHaveHappened(); } @@ -364,7 +364,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentStatusChanged { Change = StatusChange.Unpublished, Status = Status.Draft }) ); - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")) + A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Draft, Status.Published), "")) .MustHaveHappened(); } @@ -387,7 +387,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentStatusChanged { Status = Status.Draft }) ); - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")) + A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Draft, Status.Archived), "")) .MustHaveHappened(); } @@ -462,6 +462,9 @@ namespace Squidex.Domain.Apps.Entities.Contents .ShouldHaveSameEvents( CreateContentEvent(new ContentSchedulingCancelled()) ); + + A.CallTo(() => scriptEngine.Execute(A.Ignored, "")) + .MustNotHaveHappened(); } [Fact] @@ -482,7 +485,7 @@ namespace Squidex.Domain.Apps.Entities.Contents CreateContentEvent(new ContentDeleted()) ); - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")) + A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Draft, default), "")) .MustHaveHappened(); } @@ -542,6 +545,21 @@ namespace Squidex.Domain.Apps.Entities.Contents return sut.ExecuteAsync(CreateContentCommand(new ChangeContentStatus { Status = Status.Published })); } + private ScriptContext ScriptContext(NamedContentData newData, NamedContentData oldData, Status newStatus, Status oldStatus) + { + return A.That.Matches(x => M(x, newData, oldData, newStatus, oldStatus)); + } + + private bool M(ScriptContext x, NamedContentData newData, NamedContentData oldData, Status newStatus, Status oldStatus) + { + return + Equals(x.Data, newData) && + Equals(x.DataOld, oldData) && + Equals(x.Status, newStatus) && + Equals(x.StatusOld, oldStatus) && + x.ContentId == contentId && x.User == User; + } + protected T CreateContentEvent(T @event) where T : ContentEvent { @event.ContentId = contentId;