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();
Result result;
try
{
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?)",
Resources.ScriptingPatchJson);
describe(JsonType.Function, "deleteJSON(url, body, callback, headers?)",
describe(JsonType.Function, "deleteJSON(url, callback, headers?)",
Resources.ScriptingDeleteJson);
}
@ -81,11 +81,6 @@ public sealed class HttpJintExtension : IJintExtension, IScriptDescriptor
{
context.Schedule(async (scheduler, ct) =>
{
if (callback == null)
{
throw new JavaScriptException("Callback cannot be null.");
}
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
{
throw new JavaScriptException("URL is not valid.");
@ -100,7 +95,10 @@ public sealed class HttpJintExtension : IJintExtension, IScriptDescriptor
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)
{
if (IsCompleted || action == null)
lock (Engine)
{
return;
}
if (IsCompleted || action == null)
{
return;
}
Engine.ResetConstraints();
action();
Engine.ResetConstraints();
action();
}
}
void IScheduler.Run<TArg>(Action<TArg>? action, TArg argument)
{
if (IsCompleted || action == null)
lock (Engine)
{
return;
}
if (IsCompleted || action == null)
{
return;
}
Engine.ResetConstraints();
action(argument);
Engine.ResetConstraints();
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.Json;
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.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Json;
@ -47,6 +48,7 @@ public static class SerializationServices
options.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
options.Converters.Add(new GeoJsonConverterFactory());
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<FieldPropertiesDto>(typeRegistry));
options.Converters.Add(new PolymorphicConverter<RuleAction>(typeRegistry));
@ -95,6 +97,9 @@ public static class SerializationServices
services.AddSingleton<ITypeProvider>(
new AssemblyTypeProvider<RuleTriggerDto>("triggerType"));
services.AddSingleton<ITypeProvider>(
new AssemblyTypeProvider<EnrichedEvent>());
services.AddSingletonAs<FieldTypeProvider>()
.As<ITypeProvider>();

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

@ -7,6 +7,7 @@
using System.Net;
using System.Security.Claims;
using Jint.Native;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Squidex.Domain.Apps.Core.Contents;
@ -39,7 +40,8 @@ public class JintScriptEngineTests : IClassFixture<TranslationsFixture>
new DateTimeJintExtension(),
new HttpJintExtension(httpClientFactory),
new StringJintExtension(),
new StringWordsJintExtension()
new StringWordsJintExtension(),
new AsyncExtension(),
};
var httpResponse = new HttpResponseMessage(HttpStatusCode.OK)
@ -61,6 +63,23 @@ public class JintScriptEngineTests : IClassFixture<TranslationsFixture>
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]
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"]);
}
[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);
}
[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]
public async Task Should_run_rules_on_asset_change()
{

Loading…
Cancel
Save