Browse Source

Fix scripting and jint issues.

pull/971/head
Sebastian 3 years ago
parent
commit
e84a428348
  1. 1
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs
  2. 12
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/HttpJintExtension.cs
  3. 26
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptExecutionContext.cs
  4. 5
      backend/src/Squidex/Config/Domain/SerializationServices.cs
  5. 44
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs
  6. 47
      tools/TestSuite/TestSuite.ApiTests/RuleRunnerTests.cs

1
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs

@ -357,7 +357,6 @@ public sealed class RuleService : IRuleService
var actionWatch = ValueStopwatch.StartNew(); var actionWatch = ValueStopwatch.StartNew();
Result result; Result result;
try try
{ {
var actionType = typeRegistry.GetType<RuleAction>(actionName); var actionType = typeRegistry.GetType<RuleAction>(actionName);

12
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/HttpJintExtension.cs

@ -53,7 +53,7 @@ public sealed class HttpJintExtension : IJintExtension, IScriptDescriptor
describe(JsonType.Function, "patchJSON(url, body, callback, headers?)", describe(JsonType.Function, "patchJSON(url, body, callback, headers?)",
Resources.ScriptingPatchJson); Resources.ScriptingPatchJson);
describe(JsonType.Function, "deleteJSON(url, body, callback, headers?)", describe(JsonType.Function, "deleteJSON(url, callback, headers?)",
Resources.ScriptingDeleteJson); Resources.ScriptingDeleteJson);
} }
@ -81,11 +81,6 @@ public sealed class HttpJintExtension : IJintExtension, IScriptDescriptor
{ {
context.Schedule(async (scheduler, ct) => context.Schedule(async (scheduler, ct) =>
{ {
if (callback == null)
{
throw new JavaScriptException("Callback cannot be null.");
}
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
{ {
throw new JavaScriptException("URL is not valid."); throw new JavaScriptException("URL is not valid.");
@ -100,7 +95,10 @@ public sealed class HttpJintExtension : IJintExtension, IScriptDescriptor
var responseObject = await ParseResponseasync(context, response, ct); var responseObject = await ParseResponseasync(context, response, ct);
scheduler.Run(callback, responseObject); if (callback != null)
{
scheduler.Run(callback, responseObject);
}
}); });
} }

26
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptExecutionContext.cs

@ -140,23 +140,29 @@ public sealed class ScriptExecutionContext<T> : ScriptExecutionContext, ISchedul
void IScheduler.Run(Action? action) void IScheduler.Run(Action? action)
{ {
if (IsCompleted || action == null) lock (Engine)
{ {
return; if (IsCompleted || action == null)
} {
return;
}
Engine.ResetConstraints(); Engine.ResetConstraints();
action(); action();
}
} }
void IScheduler.Run<TArg>(Action<TArg>? action, TArg argument) void IScheduler.Run<TArg>(Action<TArg>? action, TArg argument)
{ {
if (IsCompleted || action == null) lock (Engine)
{ {
return; if (IsCompleted || action == null)
} {
return;
}
Engine.ResetConstraints(); Engine.ResetConstraints();
action(argument); action(argument);
}
} }
} }

5
backend/src/Squidex/Config/Domain/SerializationServices.cs

@ -20,6 +20,7 @@ using Squidex.Domain.Apps.Core.Apps.Json;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Contents.Json; using Squidex.Domain.Apps.Core.Contents.Json;
using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Json; using Squidex.Domain.Apps.Core.Rules.Json;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Domain.Apps.Core.Schemas.Json;
@ -47,6 +48,7 @@ public static class SerializationServices
options.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); options.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
options.Converters.Add(new GeoJsonConverterFactory()); options.Converters.Add(new GeoJsonConverterFactory());
options.Converters.Add(new PolymorphicConverter<IEvent>(typeRegistry)); options.Converters.Add(new PolymorphicConverter<IEvent>(typeRegistry));
options.Converters.Add(new PolymorphicConverter<EnrichedEvent>(typeRegistry));
options.Converters.Add(new PolymorphicConverter<FieldProperties>(typeRegistry)); options.Converters.Add(new PolymorphicConverter<FieldProperties>(typeRegistry));
options.Converters.Add(new PolymorphicConverter<FieldPropertiesDto>(typeRegistry)); options.Converters.Add(new PolymorphicConverter<FieldPropertiesDto>(typeRegistry));
options.Converters.Add(new PolymorphicConverter<RuleAction>(typeRegistry)); options.Converters.Add(new PolymorphicConverter<RuleAction>(typeRegistry));
@ -95,6 +97,9 @@ public static class SerializationServices
services.AddSingleton<ITypeProvider>( services.AddSingleton<ITypeProvider>(
new AssemblyTypeProvider<RuleTriggerDto>("triggerType")); new AssemblyTypeProvider<RuleTriggerDto>("triggerType"));
services.AddSingleton<ITypeProvider>(
new AssemblyTypeProvider<EnrichedEvent>());
services.AddSingletonAs<FieldTypeProvider>() services.AddSingletonAs<FieldTypeProvider>()
.As<ITypeProvider>(); .As<ITypeProvider>();

44
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs

@ -7,6 +7,7 @@
using System.Net; using System.Net;
using System.Security.Claims; using System.Security.Claims;
using Jint.Native;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
@ -39,7 +40,8 @@ public class JintScriptEngineTests : IClassFixture<TranslationsFixture>
new DateTimeJintExtension(), new DateTimeJintExtension(),
new HttpJintExtension(httpClientFactory), new HttpJintExtension(httpClientFactory),
new StringJintExtension(), new StringJintExtension(),
new StringWordsJintExtension() new StringWordsJintExtension(),
new AsyncExtension(),
}; };
var httpResponse = new HttpResponseMessage(HttpStatusCode.OK) var httpResponse = new HttpResponseMessage(HttpStatusCode.OK)
@ -61,6 +63,23 @@ public class JintScriptEngineTests : IClassFixture<TranslationsFixture>
extensions); extensions);
} }
private sealed class AsyncExtension : IJintExtension
{
private delegate void Delay(Action callback);
public void ExtendAsync(ScriptExecutionContext context)
{
context.Engine.SetValue("setTimeout", new Delay(callback =>
{
context.Schedule(async (scheduler, ct) =>
{
await Task.Delay(5, ct);
scheduler.Run(callback);
});
}));
}
}
[Fact] [Fact]
public async Task ExecuteAsync_should_catch_script_syntax_errors() public async Task ExecuteAsync_should_catch_script_syntax_errors()
{ {
@ -598,4 +617,27 @@ public class JintScriptEngineTests : IClassFixture<TranslationsFixture>
Assert.Equal(JsonValue.Create(28), actual["test"]!["iv"]); Assert.Equal(JsonValue.Create(28), actual["test"]!["iv"]);
} }
[Fact]
public async Task Should_not_run_callbacks_in_parallel()
{
var vars = new DataScriptVars
{
["value"] = 13
};
const string script1 = @"
var x = ctx.value;
for (var i = 0; i < 100; i++) {
setTimeout(function () {
x++;
ctx.shared = x;
});
}
";
await sut.ExecuteAsync(vars, script1, new ScriptOptions { AsContext = true });
Assert.Equal(113.0, vars["shared"]);
}
} }

47
tools/TestSuite/TestSuite.ApiTests/RuleRunnerTests.cs

@ -83,6 +83,53 @@ public class RuleRunnerTests : IClassFixture<ClientFixture>, IClassFixture<Webho
Assert.Single(eventsRule.Items); Assert.Single(eventsRule.Items);
} }
[Fact]
public async Task Should_run_scripting_rule_on_content_change()
{
// STEP 0: Create app.
await CreateAppAsync();
// STEP 1: Start webhook session
var (url, sessionId) = await webhookCatcher.CreateSessionAsync();
// STEP 2: Create rule
var createRule = new CreateRuleDto
{
Action = new ScriptRuleActionDto
{
Script = $@"
postJSON('{url}', {{ schemaName: event.schemaId.Name }})
"
},
Trigger = new ContentChangedRuleTriggerDto
{
HandleAll = true
}
};
var rule = await _.Rules.PostRuleAsync(appName, createRule);
// STEP 3: Create test content
await CreateContentAsync();
// Get requests.
var requests = await webhookCatcher.WaitForRequestsAsync(sessionId, TimeSpan.FromMinutes(2));
var request = requests.FirstOrDefault(x => x.Method == "POST" && x.Content.Contains(schemaName, StringComparison.OrdinalIgnoreCase));
Assert.NotNull(request);
// STEP 4: Get events
var eventsAll = await _.Rules.GetEventsAsync(appName, rule.Id);
var eventsRule = await _.Rules.GetEventsAsync(appName);
Assert.Single(eventsAll.Items);
Assert.Single(eventsRule.Items);
}
[Fact] [Fact]
public async Task Should_run_rules_on_asset_change() public async Task Should_run_rules_on_asset_change()
{ {

Loading…
Cancel
Save