Browse Source

Merge pull request #390 from Squidex/script/status

Script/status
pull/392/head
Sebastian Stehle 7 years ago
committed by GitHub
parent
commit
b2430abc6f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs
  2. 6
      src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptContext.cs
  3. 65
      src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs
  4. 26
      src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs
  5. 2
      tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs
  6. 42
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs

13
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); 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) if (context.User != null)
@ -150,7 +150,14 @@ namespace Squidex.Domain.Apps.Core.Scripting
if (!string.IsNullOrWhiteSpace(context.Operation)) 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); engine.SetValue("ctx", contextInstance);

6
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 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; } public string Operation { get; set; }
} }

65
src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs

@ -66,11 +66,21 @@ namespace Squidex.Domain.Apps.Entities.Contents
case CreateContent createContent: case CreateContent createContent:
return CreateReturnAsync(createContent, async c => return CreateReturnAsync(createContent, async c =>
{ {
var ctx = await CreateContext(c.AppId.Id, c.SchemaId.Id, Guid.Empty, () => "Failed to create content."); var ctx = await CreateContext(c.AppId.Id, c.SchemaId.Id, c, () => "Failed to create content.");
var status = (await contentWorkflow.GetInitialStatusAsync(ctx.Schema)).Status;
await GuardContent.CanCreate(ctx.Schema, contentWorkflow, c); await GuardContent.CanCreate(ctx.Schema, contentWorkflow, c);
await ctx.ExecuteScriptAndTransformAsync(s => s.Create, "Create", c, c.Data); c.Data = await ctx.ExecuteScriptAndTransformAsync(s => s.Create,
new ScriptContext
{
Operation = "Create",
Data = c.Data,
Status = status,
StatusOld = default
});
await ctx.EnrichAsync(c.Data); await ctx.EnrichAsync(c.Data);
if (!c.DoNotValidate) if (!c.DoNotValidate)
@ -80,12 +90,17 @@ namespace Squidex.Domain.Apps.Entities.Contents
if (c.Publish) if (c.Publish)
{ {
await ctx.ExecuteScriptAsync(s => s.Change, "Published", c, c.Data); await ctx.ExecuteScriptAsync(s => s.Change,
new ScriptContext
{
Operation = "Published",
Data = c.Data,
Status = Status.Published,
StatusOld = status
});
} }
var statusInfo = await contentWorkflow.GetInitialStatusAsync(ctx.Schema); Create(c, status);
Create(c, statusInfo.Status);
return Snapshot; return Snapshot;
}); });
@ -117,7 +132,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
var isChangeConfirm = Snapshot.IsPending && Snapshot.Status == Status.Published && c.Status == Status.Published; var isChangeConfirm = Snapshot.IsPending && Snapshot.Status == Status.Published && c.Status == Status.Published;
var ctx = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, Snapshot.Id, () => "Failed to change content."); var ctx = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, c, () => "Failed to change content.");
await GuardContent.CanChangeStatus(ctx.Schema, Snapshot, contentWorkflow, c, isChangeConfirm); await GuardContent.CanChangeStatus(ctx.Schema, Snapshot, contentWorkflow, c, isChangeConfirm);
@ -148,7 +163,14 @@ namespace Squidex.Domain.Apps.Entities.Contents
reason = StatusChange.Change; reason = StatusChange.Change;
} }
await ctx.ExecuteScriptAsync(s => s.Change, reason, c, Snapshot.Data); await ctx.ExecuteScriptAsync(s => s.Change,
new ScriptContext
{
Operation = reason.ToString(),
Data = Snapshot.Data,
Status = c.Status,
StatusOld = Snapshot.Status
});
ChangeStatus(c, reason); ChangeStatus(c, reason);
} }
@ -182,11 +204,18 @@ namespace Squidex.Domain.Apps.Entities.Contents
case DeleteContent deleteContent: case DeleteContent deleteContent:
return UpdateAsync(deleteContent, async c => return UpdateAsync(deleteContent, async c =>
{ {
var ctx = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, Snapshot.Id, () => "Failed to delete content."); var ctx = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, c, () => "Failed to delete content.");
GuardContent.CanDelete(ctx.Schema, c); GuardContent.CanDelete(ctx.Schema, c);
await ctx.ExecuteScriptAsync(s => s.Delete, "Delete", c, Snapshot.Data); await ctx.ExecuteScriptAsync(s => s.Delete,
new ScriptContext
{
Operation = "Delete",
Data = Snapshot.Data,
Status = Snapshot.Status,
StatusOld = default
});
Delete(c); Delete(c);
}); });
@ -207,7 +236,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
if (!currentData.Equals(newData)) if (!currentData.Equals(newData))
{ {
var ctx = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, Snapshot.Id, () => "Failed to update content."); var ctx = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, command, () => "Failed to update content.");
if (partial) if (partial)
{ {
@ -218,7 +247,15 @@ namespace Squidex.Domain.Apps.Entities.Contents
await ctx.ValidateAsync(command.Data); await ctx.ValidateAsync(command.Data);
} }
newData = await ctx.ExecuteScriptAndTransformAsync(s => s.Update, "Update", command, newData, Snapshot.Data); newData = await ctx.ExecuteScriptAndTransformAsync(s => s.Update,
new ScriptContext
{
Operation = "Create",
Data = newData,
DataOld = currentData,
Status = Snapshot.Status,
StatusOld = default
});
if (isProposal) if (isProposal)
{ {
@ -306,10 +343,10 @@ namespace Squidex.Domain.Apps.Entities.Contents
} }
} }
private async Task<ContentOperationContext> CreateContext(Guid appId, Guid schemaId, Guid contentId, Func<string> message) private async Task<ContentOperationContext> CreateContext(Guid appId, Guid schemaId, ContentCommand command, Func<string> message)
{ {
var operationContext = var operationContext =
await ContentOperationContext.CreateAsync(appId, schemaId, contentId, await ContentOperationContext.CreateAsync(appId, schemaId, command,
appProvider, assetRepository, contentRepository, scriptEngine, message); appProvider, assetRepository, contentRepository, scriptEngine, message);
return operationContext; return operationContext;

26
src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs

@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
private IScriptEngine scriptEngine; private IScriptEngine scriptEngine;
private ISchemaEntity schemaEntity; private ISchemaEntity schemaEntity;
private IAppEntity appEntity; private IAppEntity appEntity;
private Guid contentId; private ContentCommand command;
private Guid schemaId; private Guid schemaId;
private Func<string> message; private Func<string> message;
@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
public static async Task<ContentOperationContext> CreateAsync( public static async Task<ContentOperationContext> CreateAsync(
Guid appId, Guid appId,
Guid schemaId, Guid schemaId,
Guid contentId, ContentCommand command,
IAppProvider appProvider, IAppProvider appProvider,
IAssetRepository assetRepository, IAssetRepository assetRepository,
IContentRepository contentRepository, IContentRepository contentRepository,
@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
appEntity = appEntity, appEntity = appEntity,
assetRepository = assetRepository, assetRepository = assetRepository,
contentId = contentId, command = command,
contentRepository = contentRepository, contentRepository = contentRepository,
message = message, message = message,
schemaId = schemaId, schemaId = schemaId,
@ -87,32 +87,34 @@ namespace Squidex.Domain.Apps.Entities.Contents
return data.ValidatePartialAsync(ctx, schemaEntity.SchemaDef, appEntity.PartitionResolver(), message); return data.ValidatePartialAsync(ctx, schemaEntity.SchemaDef, appEntity.PartitionResolver(), message);
} }
public Task<NamedContentData> ExecuteScriptAndTransformAsync(Func<SchemaScripts, string> script, object operation, ContentCommand command, NamedContentData data, NamedContentData oldData = null) public Task<NamedContentData> ExecuteScriptAndTransformAsync(Func<SchemaScripts, string> script, ScriptContext context)
{ {
var ctx = CreateScriptContext(operation, command, data, oldData); Enrich(context);
var result = scriptEngine.ExecuteAndTransform(ctx, GetScript(script)); var result = scriptEngine.ExecuteAndTransform(context, GetScript(script));
return Task.FromResult(result); return Task.FromResult(result);
} }
public Task ExecuteScriptAsync(Func<SchemaScripts, string> script, object operation, ContentCommand command, NamedContentData data, NamedContentData oldData = null) public Task ExecuteScriptAsync(Func<SchemaScripts, string> script, ScriptContext context)
{ {
var ctx = CreateScriptContext(operation, command, data, oldData); Enrich(context);
scriptEngine.Execute(ctx, GetScript(script)); scriptEngine.Execute(context, GetScript(script));
return TaskHelper.Done; return TaskHelper.Done;
} }
private static ScriptContext CreateScriptContext(object operation, ContentCommand command, NamedContentData data, NamedContentData oldData) private void Enrich(ScriptContext context)
{ {
return new ScriptContext { ContentId = command.ContentId, OldData = oldData, Data = data, User = command.User, Operation = operation.ToString() }; context.ContentId = command.ContentId;
context.User = command.User;
} }
private ValidationContext CreateValidationContext() private ValidationContext CreateValidationContext()
{ {
return new ValidationContext(contentId, schemaId, QueryContentsAsync, QueryAssetsAsync); return new ValidationContext(command.ContentId, schemaId, QueryContentsAsync, QueryAssetsAsync);
} }
private async Task<IReadOnlyList<IAssetInfo>> QueryAssetsAsync(IEnumerable<Guid> assetIds) private async Task<IReadOnlyList<IAssetInfo>> QueryAssetsAsync(IEnumerable<Guid> assetIds)

2
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")); 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, @" var result = sut.ExecuteAndTransform(context, @"
ctx.data.number0.iv = ctx.data.number0.iv + ctx.oldData.number0.iv * parseInt(ctx.user.id, 10); ctx.data.number0.iv = ctx.data.number0.iv + ctx.oldData.number0.iv * parseInt(ctx.user.id, 10);

42
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 }) CreateContentEvent(new ContentCreated { Data = data, Status = Status.Draft })
); );
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")) A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(data, null, Status.Draft, default), "<create-script>"))
.MustHaveHappened(); .MustHaveHappened();
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>"))
.MustNotHaveHappened(); .MustNotHaveHappened();
@ -157,9 +157,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentStatusChanged { Status = Status.Published, Change = StatusChange.Published }) CreateContentEvent(new ContentStatusChanged { Status = Status.Published, Change = StatusChange.Published })
); );
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")) A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(data, null, Status.Draft, default), "<create-script>"))
.MustHaveHappened(); .MustHaveHappened();
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Published, Status.Draft), "<change-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -187,7 +187,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentUpdated { Data = otherData }) CreateContentEvent(new ContentUpdated { Data = otherData })
); );
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")) A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(otherData, data, Status.Draft, default), "<update-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -210,7 +210,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentUpdateProposed { Data = otherData }) CreateContentEvent(new ContentUpdateProposed { Data = otherData })
); );
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")) A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(otherData, data, Status.Published, default), "<update-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -257,7 +257,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentUpdated { Data = patched }) CreateContentEvent(new ContentUpdated { Data = patched })
); );
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")) A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(patched, data, Status.Draft, default), "<update-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -280,7 +280,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentUpdateProposed { Data = patched }) CreateContentEvent(new ContentUpdateProposed { Data = patched })
); );
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")) A.CallTo(() => scriptEngine.ExecuteAndTransform(ScriptContext(patched, data, Status.Published, default), "<update-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -319,7 +319,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentStatusChanged { Change = StatusChange.Published, Status = Status.Published }) CreateContentEvent(new ContentStatusChanged { Change = StatusChange.Published, Status = Status.Published })
); );
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Published, Status.Draft), "<change-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -341,7 +341,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentStatusChanged { Status = Status.Archived }) CreateContentEvent(new ContentStatusChanged { Status = Status.Archived })
); );
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Archived, Status.Draft), "<change-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -364,7 +364,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentStatusChanged { Change = StatusChange.Unpublished, Status = Status.Draft }) CreateContentEvent(new ContentStatusChanged { Change = StatusChange.Unpublished, Status = Status.Draft })
); );
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Draft, Status.Published), "<change-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -387,7 +387,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentStatusChanged { Status = Status.Draft }) CreateContentEvent(new ContentStatusChanged { Status = Status.Draft })
); );
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Draft, Status.Archived), "<change-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -462,6 +462,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
.ShouldHaveSameEvents( .ShouldHaveSameEvents(
CreateContentEvent(new ContentSchedulingCancelled()) CreateContentEvent(new ContentSchedulingCancelled())
); );
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>"))
.MustNotHaveHappened();
} }
[Fact] [Fact]
@ -482,7 +485,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
CreateContentEvent(new ContentDeleted()) CreateContentEvent(new ContentDeleted())
); );
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<delete-script>")) A.CallTo(() => scriptEngine.Execute(ScriptContext(data, null, Status.Draft, default), "<delete-script>"))
.MustHaveHappened(); .MustHaveHappened();
} }
@ -542,6 +545,21 @@ namespace Squidex.Domain.Apps.Entities.Contents
return sut.ExecuteAsync(CreateContentCommand(new ChangeContentStatus { Status = Status.Published })); return sut.ExecuteAsync(CreateContentCommand(new ChangeContentStatus { Status = Status.Published }));
} }
private ScriptContext ScriptContext(NamedContentData newData, NamedContentData oldData, Status newStatus, Status oldStatus)
{
return A<ScriptContext>.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>(T @event) where T : ContentEvent protected T CreateContentEvent<T>(T @event) where T : ContentEvent
{ {
@event.ContentId = contentId; @event.ContentId = contentId;

Loading…
Cancel
Save