Skip to content

Commit

Permalink
Scripting fixes (#1000)
Browse files Browse the repository at this point in the history
* Fix scripting.
* Fix content type.
* Fix schema names.
* Update client library.
  • Loading branch information
SebastianStehle committed Jun 23, 2023
1 parent b7bc76b commit 2a54737
Show file tree
Hide file tree
Showing 101 changed files with 316 additions and 232 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ jobs:
run: |
echo "BUILD_NUMBER=$(($BUILD_NUMBER + 6000))" >> $GITHUB_ENV
- name: Publish - Login to Docker Hub
uses: docker/[email protected]
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Publish - Rename Tags
if: github.event_name != 'pull_request'
run: |
Expand Down
2 changes: 1 addition & 1 deletion backend/i18n/source/backend_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"common.httpOnlyAsUser": "Not allowed for clients.",
"common.httpValidationError": "Validation error",
"common.initialStep": "Initial step",
"common.jsError": "Failed to execute script with Javascript error: {message}",
"common.jsError": "Failed to execute script with Javascript runtime error: {message}",
"common.jsNotAllowed": "Script has forbidden the operation.",
"common.jsParseError": "Failed to execute script with Javascript syntax error: {message}",
"common.jsRejected": "Script rejected the operation.",
Expand Down
2 changes: 1 addition & 1 deletion backend/i18n/translator/Squidex.Translator/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ private static (DirectoryInfo, TranslationService) Setup(TranslateArguments argu
throw new ArgumentException("Folder does not exist.", nameof(arguments));
}

var supportedLocales = new string[] { "en", "nl", "it", "zh", "pt","fr" };
var supportedLocales = new string[] { "en", "nl", "it", "zh", "pt", "fr" };

var locales = supportedLocales;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public AddSchemaNames(ResolvedComponents components)
this.components = components;
}

public JsonObject ConvertItemBefore(IField parentField, JsonObject source, IEnumerable<IField> schema)
public JsonObject ConvertItemAfter(IField parentField, JsonObject source, IEnumerable<IField> schema)
{
if (parentField is IArrayField)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
using Jint.Native.Json;
using Jint.Runtime;
using Squidex.Domain.Apps.Core.Properties;
using Squidex.Infrastructure;

namespace Squidex.Domain.Apps.Core.Scripting.Extensions;

public sealed class HttpJintExtension : IJintExtension, IScriptDescriptor
{
private delegate void HttpJson(string url, Action<JsValue> callback, JsValue? headers = null);
private delegate void HttpJsonWithBody(string url, JsValue post, Action<JsValue> callback, JsValue? headers = null);
private delegate void HttpJson(string url, Action<JsValue> callback, JsValue? headers = null, bool ignoreError = false);
private delegate void HttpJsonWithBody(string url, JsValue post, Action<JsValue> callback, JsValue? headers = null, bool ignoreError = false);
private readonly IHttpClientFactory httpClientFactory;

public HttpJintExtension(IHttpClientFactory httpClientFactory)
Expand All @@ -41,43 +42,43 @@ public void Describe(AddDescription describe, ScriptScope scope)
return;
}

describe(JsonType.Function, "getJSON(url, callback, headers?)",
describe(JsonType.Function, "getJSON(url, callback, headers?, ignoreError?)",
Resources.ScriptingGetJSON);

describe(JsonType.Function, "postJSON(url, body, callback, headers?)",
describe(JsonType.Function, "postJSON(url, body, callback, headers?, ignoreError?)",
Resources.ScriptingPostJSON);

describe(JsonType.Function, "putJSON(url, body, callback, headers?)",
describe(JsonType.Function, "putJSON(url, body, callback, headers?, ignoreError?)",
Resources.ScriptingPutJson);

describe(JsonType.Function, "patchJSON(url, body, callback, headers?)",
describe(JsonType.Function, "patchJSON(url, body, callback, headers?, ignoreError?)",
Resources.ScriptingPatchJson);

describe(JsonType.Function, "deleteJSON(url, callback, headers?)",
describe(JsonType.Function, "deleteJSON(url, callback, headers?, ignoreError?)",
Resources.ScriptingDeleteJson);
}

private void AddMethod(ScriptExecutionContext context, HttpMethod method, string name)
{
var action = new HttpJson((url, callback, headers) =>
var action = new HttpJson((url, callback, headers, ignoreError) =>
{
Request(context, method, url, null, callback, headers);
Request(context, method, url, null, callback, headers, ignoreError);
});

context.Engine.SetValue(name, action);
}

private void AddBodyMethod(ScriptExecutionContext context, HttpMethod method, string name)
{
var action = new HttpJsonWithBody((url, body, callback, headers) =>
var action = new HttpJsonWithBody((url, body, callback, headers, ignoreError) =>
{
Request(context, method, url, body, callback, headers);
Request(context, method, url, body, callback, headers, ignoreError);
});

context.Engine.SetValue(name, action);
}

private void Request(ScriptExecutionContext context, HttpMethod method, string url, JsValue? body, Action<JsValue> callback, JsValue? headers)
private void Request(ScriptExecutionContext context, HttpMethod method, string url, JsValue? body, Action<JsValue> callback, JsValue? headers, bool ignoreError)
{
context.Schedule(async (scheduler, ct) =>
{
Expand All @@ -86,19 +87,53 @@ private void Request(ScriptExecutionContext context, HttpMethod method, string u
throw new JavaScriptException("URL is not valid.");
}
var httpClient = httpClientFactory.CreateClient("Jint");
if (callback == null)
{
throw new JavaScriptException("Callback is not defined.");
}
var request = CreateRequest(context, method, uri, body, headers);
var response = await httpClient.SendAsync(request, ct);
try
{
response.EnsureSuccessStatusCode();
var httpClient = httpClientFactory.CreateClient("Jint");
var responseObject = await ParseResponseasync(context, response, ct);
var request = CreateRequest(context, method, uri, body, headers);
var response = await httpClient.SendAsync(request, ct);
if (!ignoreError)
{
response.EnsureSuccessStatusCode();
}
JsValue responseObject;
if (ignoreError && !response.IsSuccessStatusCode)
{
var responseString = await response.Content.ReadAsStringAsync(ct);
responseObject = JsValue.FromObject(context.Engine, new Dictionary<string, object?>
{
["statusCode"] = (int)response.StatusCode,
["headers"] =
response.Content.Headers
.Concat(response.Headers)
.Concat(response.TrailingHeaders)
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Last().Value.First()),
["body"] = responseString,
});
}
else
{
responseObject = await ParseResponseAsync(context, response, ct);
}
if (callback != null)
{
scheduler.Run(callback, responseObject);
}
catch (Exception ex)
{
throw new JavaScriptException(ex.Message);
}
});
}

Expand All @@ -108,13 +143,12 @@ private static HttpRequestMessage CreateRequest(ScriptExecutionContext context,

if (body != null)
{
var serializer = new JsonSerializer(context.Engine);

var json = serializer.Serialize(body, JsValue.Undefined, JsValue.Undefined)?.ToString();
var jsonWriter = new JsonSerializer(context.Engine);
var jsonContent = jsonWriter.Serialize(body, JsValue.Undefined, JsValue.Undefined)?.ToString();

if (json != null)
if (jsonContent != null)
{
request.Content = new StringContent(json, Encoding.UTF8, "text/json");
request.Content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
}
}

Expand All @@ -138,7 +172,7 @@ private static HttpRequestMessage CreateRequest(ScriptExecutionContext context,
return request;
}

private static async Task<JsValue> ParseResponseasync(ScriptExecutionContext context, HttpResponseMessage response,
private static async Task<JsValue> ParseResponseAsync(ScriptExecutionContext context, HttpResponseMessage response,
CancellationToken ct)
{
var responseString = await response.Content.ReadAsStringAsync(ct);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public async Task<JsonValue> ExecuteAsync(ScriptVars vars, string script, Script
Guard.NotNull(vars);
Guard.NotNullOrEmpty(script);

using (var combined = CancellationTokenSource.CreateLinkedTokenSource(ct))
using var combined = CancellationTokenSource.CreateLinkedTokenSource(ct);
try
{
// Enforce a timeout after a configured time span.
combined.CancelAfter(timeoutExecution);
Expand All @@ -66,6 +67,10 @@ public async Task<JsonValue> ExecuteAsync(ScriptVars vars, string script, Script

return await context.CompleteAsync() ?? JsonMapper.Map(result);
}
catch (Exception ex)
{
throw MapException(ex);
}
}

public async Task<ContentData> TransformAsync(DataScriptVars vars, string script, ScriptOptions options = default,
Expand All @@ -74,7 +79,8 @@ public async Task<ContentData> TransformAsync(DataScriptVars vars, string script
Guard.NotNull(vars);
Guard.NotNullOrEmpty(script);

using (var combined = CancellationTokenSource.CreateLinkedTokenSource(ct))
using var combined = CancellationTokenSource.CreateLinkedTokenSource(ct);
try
{
// Enforce a timeout after a configured time span.
combined.CancelAfter(timeoutExecution);
Expand Down Expand Up @@ -107,21 +113,32 @@ public async Task<ContentData> TransformAsync(DataScriptVars vars, string script

return await context.CompleteAsync() ?? vars.Data!;
}
catch (Exception ex)
{
throw MapException(ex);
}
}

public JsonValue Execute(ScriptVars vars, string script, ScriptOptions options = default)
{
Guard.NotNull(vars);
Guard.NotNullOrEmpty(script);

var context =
CreateEngine<object>(options, default)
.Extend(vars, options)
.Extend(extensions);
try
{
var context =
CreateEngine<object>(options, default)
.Extend(vars, options)
.Extend(extensions);

var result = Execute(context.Engine, script);
var result = Execute(context.Engine, script);

return JsonMapper.Map(result);
return JsonMapper.Map(result);
}
catch (Exception ex)
{
throw MapException(ex);
}
}

private ScriptExecutionContext<T> CreateEngine<T>(ScriptOptions options, CancellationToken ct)
Expand Down Expand Up @@ -164,34 +181,28 @@ private ScriptExecutionContext<T> CreateEngine<T>(ScriptOptions options, Cancell

private JsValue Execute(Engine engine, string script)
{
try
{
var program = parser.Parse(script);
var program = parser.Parse(script);

lock (engine)
{
return engine.Evaluate(program);
}
}
catch (ArgumentException ex)
{
throw new ValidationException(T.Get("common.jsParseError", new { error = ex.Message }));
}
catch (JavaScriptException ex)
lock (engine)
{
throw new ValidationException(T.Get("common.jsError", new { message = ex.Message }));
return engine.Evaluate(program);
}
catch (ParserException ex)
{
throw new ValidationException(T.Get("common.jsError", new { message = ex.Message }));
}
catch (DomainException)
{
throw;
}
catch (Exception ex)
}

private static Exception MapException(Exception inner)
{
switch (inner)
{
throw new ValidationException(T.Get("common.jsError", new { message = ex.GetType().Name }), ex);
case ArgumentException:
return new ValidationException(T.Get("common.jsParseError", new { error = inner.Message }));
case JavaScriptException:
return new ValidationException(T.Get("common.jsError", new { message = inner.Message }));
case ParserException:
return new ValidationException(T.Get("common.jsError", new { message = inner.Message }));
case DomainException:
return inner;
default:
return new ValidationException(T.Get("common.jsError", new { message = inner.GetType().Name }), inner);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="NJsonSchema" Version="10.8.0" />
<PackageReference Include="NJsonSchema" Version="10.9.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="5.4.0" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="5.5.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="7.0.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

using Microsoft.Extensions.Logging;
using NodaTime;
using Squidex.ClientLibrary;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Hosting;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Notifo.SDK" Version="1.7.4" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.CLI.Core" Version="10.0.0" />
<PackageReference Include="Squidex.CLI.Core" Version="10.3.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="7.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
Expand Down
12 changes: 6 additions & 6 deletions backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@
<PackageReference Include="NodaTime" Version="3.1.6" />
<PackageReference Include="OpenTelemetry.Api" Version="1.4.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets" Version="5.4.0" />
<PackageReference Include="Squidex.Caching" Version="5.4.0" />
<PackageReference Include="Squidex.Hosting.Abstractions" Version="5.4.0" />
<PackageReference Include="Squidex.Log" Version="5.4.0" />
<PackageReference Include="Squidex.Messaging" Version="5.4.0" />
<PackageReference Include="Squidex.Text" Version="5.4.0" />
<PackageReference Include="Squidex.Assets" Version="5.5.0" />
<PackageReference Include="Squidex.Caching" Version="5.5.0" />
<PackageReference Include="Squidex.Hosting.Abstractions" Version="5.5.0" />
<PackageReference Include="Squidex.Log" Version="5.5.0" />
<PackageReference Include="Squidex.Messaging" Version="5.5.0" />
<PackageReference Include="Squidex.Text" Version="5.5.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="7.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
Expand Down
2 changes: 1 addition & 1 deletion backend/src/Squidex.Shared/Texts.resx
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@
<value>Initial step</value>
</data>
<data name="common.jsError" xml:space="preserve">
<value>Failed to execute script with Javascript error: {message}</value>
<value>Failed to execute script with Javascript runtime error: {message}</value>
</data>
<data name="common.jsNotAllowed" xml:space="preserve">
<value>Script has forbidden the operation.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
// ==========================================================================

using Microsoft.Extensions.Options;
using Squidex.Areas.Api.Controllers.News.Models;
using Squidex.ClientLibrary;
using FeatureDto = Squidex.Areas.Api.Controllers.News.Models.FeatureDto;
using FeaturesDto = Squidex.Areas.Api.Controllers.News.Models.FeaturesDto;

#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body

Expand Down
2 changes: 1 addition & 1 deletion backend/src/Squidex/Config/Web/WebExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static IApplicationBuilder UseSquidexHealthCheck(this IApplicationBuilder
var json = serializer.Serialize(response);
httpContext.Response.Headers[HeaderNames.ContentType] = "text/json";
httpContext.Response.Headers[HeaderNames.ContentType] = "application/json";
return httpContext.Response.WriteAsync(json);
});
Expand Down
Loading

0 comments on commit 2a54737

Please sign in to comment.