Browse Source

More autocompletion.

pull/973/head
Sebastian 3 years ago
parent
commit
c971f9c187
  1. 9
      backend/src/Squidex.Domain.Apps.Core.Model/FieldDescriptions.Designer.cs
  2. 3
      backend/src/Squidex.Domain.Apps.Core.Model/FieldDescriptions.resx
  3. 5
      backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterExtensions.cs
  4. 1
      backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterVisitor.cs
  5. 6
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/JintExtensions.cs
  6. 73
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs
  7. 3
      backend/src/Squidex.Infrastructure/Queries/FilterField.cs
  8. 2
      backend/src/Squidex.Infrastructure/Queries/FilterSchema.cs
  9. 19
      backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs
  10. 19
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ScriptingCompleterTests.cs
  11. 4
      frontend/src/app/features/content/shared/preview-button.component.ts
  12. 2
      frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.html
  13. 14
      frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.ts
  14. 4
      frontend/src/app/framework/angular/forms/editors/code-editor.component.scss
  15. 18
      frontend/src/app/shared/services/schemas.service.spec.ts
  16. 6
      frontend/src/app/shared/services/schemas.service.ts
  17. 11
      frontend/src/app/theme/_bootstrap.scss

9
backend/src/Squidex.Domain.Apps.Core.Model/FieldDescriptions.Designer.cs

@ -60,6 +60,15 @@ namespace Squidex.Domain.Apps.Core {
}
}
/// <summary>
/// Looks up a localized string similar to The access token of the current user..
/// </summary>
public static string AccessToken {
get {
return ResourceManager.GetString("AccessToken", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The user or client that triggered the event or command..
/// </summary>

3
backend/src/Squidex.Domain.Apps.Core.Model/FieldDescriptions.resx

@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AccessToken" xml:space="preserve">
<value>The access token of the current user.</value>
</data>
<data name="Actor" xml:space="preserve">
<value>The user or client that triggered the event or command.</value>
</data>

5
backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterExtensions.cs

@ -39,7 +39,8 @@ public static class FilterExtensions
foreach (var partitionKey in partitioning.AllKeys)
{
var partitionDescription = FieldPartitionDescription(field, partitioning.GetName(partitionKey) ?? partitionKey);
var partitionName = partitioning.GetName(partitionKey) ?? partitionKey;
var partitionDescription = BuildPartitionDescription(field, partitionName);
var partitionField = new FilterField(
fieldSchema,
@ -69,7 +70,7 @@ public static class FilterExtensions
return dataSchema;
}
private static string FieldPartitionDescription(RootField field, string partition)
private static string BuildPartitionDescription(RootField field, string partition)
{
var name = field.DisplayName();

1
backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterVisitor.cs

@ -98,6 +98,7 @@ internal sealed class FilterVisitor : IFieldVisitor<FilterSchema?, FilterVisitor
{
return new FilterSchema(FilterSchemaType.String)
{
AllowedValues = field.Properties.AllowedValues?.ToArray(),
Extra = new
{
options = field.Properties.AllowedValues

6
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/JintExtensions.cs

@ -35,7 +35,7 @@ public static class JintExtensions
return ids;
}
internal static ScriptExecutionContext<T> ExtendAsync<T>(this ScriptExecutionContext<T> context,
internal static ScriptExecutionContext<T> ExtendAsync<T>(this ScriptExecutionContext<T> context,
IEnumerable<IJintExtension> extensions)
{
foreach (var extension in extensions)
@ -46,7 +46,7 @@ public static class JintExtensions
return context;
}
internal static ScriptExecutionContext<T> Extend<T>(this ScriptExecutionContext<T> context,
internal static ScriptExecutionContext<T> Extend<T>(this ScriptExecutionContext<T> context,
IEnumerable<IJintExtension> extensions)
{
foreach (var extension in extensions)
@ -57,7 +57,7 @@ public static class JintExtensions
return context;
}
internal static ScriptExecutionContext<T> Extend<T>(this ScriptExecutionContext<T> context,
internal static ScriptExecutionContext<T> Extend<T>(this ScriptExecutionContext<T> context,
ScriptVars vars,
ScriptOptions options)
{

73
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs

@ -80,6 +80,13 @@ public sealed class ScriptingCompleter
return new Process(descriptors, dataSchema.Flatten()).FieldRule();
}
public IReadOnlyList<ScriptingValue> PreviewUrl(FilterSchema dataSchema)
{
Guard.NotNull(dataSchema);
return new Process(descriptors, dataSchema.Flatten()).PreviewUrl();
}
public IReadOnlyList<ScriptingValue> AssetScript()
{
return new Process(descriptors).AssetScript();
@ -157,9 +164,7 @@ public sealed class ScriptingCompleter
public IReadOnlyList<ScriptingValue> ContentScript()
{
var scope = ScriptScope.ContentScript | ScriptScope.Transform | ScriptScope.Async;
AddHelpers(scope);
AddHelpers(ScriptScope.ContentScript | ScriptScope.Transform | ScriptScope.Async);
AddObject("ctx", FieldDescriptions.Context, () =>
{
@ -171,9 +176,7 @@ public sealed class ScriptingCompleter
public IReadOnlyList<ScriptingValue> ContentTrigger()
{
var scope = ScriptScope.ContentTrigger | ScriptScope.Async;
AddHelpers(scope);
AddHelpers(ScriptScope.ContentTrigger | ScriptScope.Async);
AddObject("event", FieldDescriptions.Event, () =>
{
@ -207,6 +210,25 @@ public sealed class ScriptingCompleter
return Build();
}
public IReadOnlyList<ScriptingValue> PreviewUrl()
{
AddString("id",
FieldDescriptions.EntityId);
AddNumber("version",
FieldDescriptions.EntityVersion);
AddString("accessToken",
FieldDescriptions.AccessToken);
AddObject("data", FieldDescriptions.ContentData, () =>
{
AddData();
});
return Build();
}
public IReadOnlyList<ScriptingValue> FieldRule()
{
AddObject("user", FieldDescriptions.User, () =>
@ -381,39 +403,38 @@ public sealed class ScriptingCompleter
}
foreach (var field in dataSchema.Fields)
{
JsonType type = ConvertType(field);
Add(type, field.Path, field.Description, field.Schema.AllowedValues);
}
static JsonType ConvertType(FilterField field)
{
switch (field.Schema.Type)
{
case FilterSchemaType.Any:
AddAny(field.Path, field.Description);
break;
return JsonType.Any;
case FilterSchemaType.Boolean:
AddBoolean(field.Path, field.Description);
break;
return JsonType.Boolean;
case FilterSchemaType.DateTime:
AddString(field.Path, field.Description);
break;
return JsonType.String;
case FilterSchemaType.GeoObject:
AddObject(field.Path, field.Description);
break;
return JsonType.Object;
case FilterSchemaType.Guid:
AddString(field.Path, field.Description);
break;
return JsonType.String;
case FilterSchemaType.Number:
AddNumber(field.Path, field.Description);
break;
return JsonType.Number;
case FilterSchemaType.Object:
AddObject(field.Path, field.Description);
break;
return JsonType.Object;
case FilterSchemaType.ObjectArray:
AddArray(field.Path, field.Description);
break;
return JsonType.Array;
case FilterSchemaType.String:
AddString(field.Path, field.Description);
break;
return JsonType.String;
case FilterSchemaType.StringArray:
AddArray(field.Path, field.Description);
break;
return JsonType.Array;
default:
return JsonType.String;
}
}
}

3
backend/src/Squidex.Infrastructure/Queries/FilterField.cs

@ -9,5 +9,4 @@
namespace Squidex.Infrastructure.Queries;
public sealed record FilterField(FilterSchema Schema, string Path, string? Description = null,
bool IsNullable = false);
public sealed record FilterField(FilterSchema Schema, string Path, string? Description = null, bool IsNullable = false);

2
backend/src/Squidex.Infrastructure/Queries/FilterSchema.cs

@ -25,6 +25,8 @@ public sealed record FilterSchema(FilterSchemaType Type)
public ReadonlyList<FilterField>? Fields { get; init; }
public string[]? AllowedValues { get; init; }
public object? Extra { get; init; }
public FilterSchema Flatten(int maxDepth = 7, Predicate<FilterSchema>? predicate = null)

19
backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs

@ -313,7 +313,7 @@ public sealed class SchemasController : ApiController
[ApiPermissionOrAnonymous]
[ApiCosts(1)]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IActionResult> GetScriptCompletion(string app, string schema,
public async Task<IActionResult> GetContentScriptsCompletion(string app, string schema,
[FromServices] ScriptingCompleter completer)
{
var completion = completer.ContentScript(await BuildModel());
@ -326,7 +326,7 @@ public sealed class SchemasController : ApiController
[ApiPermissionOrAnonymous]
[ApiCosts(1)]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IActionResult> GetScriptTriggerCompletion(string app, string schema,
public async Task<IActionResult> GetContentTriggersCompletion(string app, string schema,
[FromServices] ScriptingCompleter completer)
{
var completion = completer.ContentTrigger(await BuildModel());
@ -339,7 +339,7 @@ public sealed class SchemasController : ApiController
[ApiPermissionOrAnonymous]
[ApiCosts(1)]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IActionResult> GetScriptFieldRulesCompletion(string app, string schema,
public async Task<IActionResult> GetFieldRulesCompletion(string app, string schema,
[FromServices] ScriptingCompleter completer)
{
var completion = completer.FieldRule(await BuildModel());
@ -347,6 +347,19 @@ public sealed class SchemasController : ApiController
return Ok(completion);
}
[HttpGet]
[Route("apps/{app}/schemas/{schema}/completion/prview-urls")]
[ApiPermissionOrAnonymous]
[ApiCosts(1)]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IActionResult> GetPreviewUrlsCompletion(string app, string schema,
[FromServices] ScriptingCompleter completer)
{
var completion = completer.PreviewUrl(await BuildModel());
return Ok(completion);
}
[HttpGet]
[Route("apps/{app}/schemas/{schema}/filters")]
[ApiPermissionOrAnonymous]

19
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ScriptingCompleterTests.cs

@ -190,7 +190,24 @@ public class ScriptingCompleterTests
"user.displayName",
"user.email",
"user.id",
"user.role",
"user.role"
});
}
[Fact]
public void Should_describe_prview_url()
{
var actual = sut.PreviewUrl(dataSchema);
AssertCompletion(actual,
new[]
{
"accessToken",
"data",
"data['my-field']",
"data['my-field'].iv",
"id",
"version"
});
}

4
frontend/src/app/features/content/shared/preview-button.component.ts

@ -75,9 +75,7 @@ export class PreviewButtonComponent extends StatefulComponent<State> implements
}
private navigateTo(name: string) {
const vars = { ...this.content };
vars['accessToken'] = this.authService.user?.accessToken;
const vars = { ...this.content, ...this.authService.user || {} };
const url = interpolate(this.schema.previewUrls[name], vars, 'iv');

2
frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.html

@ -22,7 +22,7 @@
<div class="col">
<sqx-control-errors for="url"></sqx-control-errors>
<input class="form-control" maxlength="1000" formControlName="url" placeholder="{{ 'schemas.previewUrls.urlPlaceholder' | sqxTranslate }}">
<sqx-code-editor [singleLine]="true" mode="ace/mode/liquid" formControlName="url" [completion]="fieldCompletions | async"></sqx-code-editor>
</div>
<div class="col-auto">

14
frontend/src/app/features/schemas/pages/schema/preview/schema-preview-urls-form.component.ts

@ -5,27 +5,35 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, Input } from '@angular/core';
import { ConfigurePreviewUrlsForm, SchemaDto, SchemasState } from '@app/shared';
import { Component, Input, OnInit } from '@angular/core';
import { EMPTY, Observable, shareReplay } from 'rxjs';
import { ConfigurePreviewUrlsForm, SchemaCompletions, SchemaDto, SchemasService, SchemasState } from '@app/shared';
@Component({
selector: 'sqx-schema-preview-urls-form',
styleUrls: ['./schema-preview-urls-form.component.scss'],
templateUrl: './schema-preview-urls-form.component.html',
})
export class SchemaPreviewUrlsFormComponent {
export class SchemaPreviewUrlsFormComponent implements OnInit {
@Input()
public schema!: SchemaDto;
public editForm = new ConfigurePreviewUrlsForm();
public fieldCompletions: Observable<SchemaCompletions> = EMPTY;
public isEditable = false;
constructor(
private readonly schemasState: SchemasState,
private readonly schemasService: SchemasService,
) {
}
public ngOnInit() {
this.fieldCompletions = this.schemasService.getFieldRulesCompletion(this.schemasState.appName, this.schema.name).pipe(shareReplay(1));
}
public ngOnChanges() {
this.isEditable = this.schema.canUpdateUrls;

4
frontend/src/app/framework/angular/forms/editors/code-editor.component.scss

@ -33,6 +33,10 @@
background: $color-border !important;
}
.ace_bracket {
display: none;
}
.ace_scroller {
box-shadow: none !important;
}

18
frontend/src/app/shared/services/schemas.service.spec.ts

@ -629,6 +629,24 @@ describe('SchemasService', () => {
expect(completions!).toEqual([]);
}));
it('should make get request to get preview urls completions',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
let completions: SchemaCompletions;
schemasService.getPreviewUrlsCompletion('my-app', 'my-schema').subscribe(result => {
completions = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/completion/preview-urls');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush([]);
expect(completions!).toEqual([]);
}));
function schemaPropertiesResponse(id: number, suffix = '') {
const key = `${id}${suffix}`;

6
frontend/src/app/shared/services/schemas.service.ts

@ -764,6 +764,12 @@ export class SchemasService {
return this.http.get<SchemaCompletions>(url);
}
public getPreviewUrlsCompletion(appName: string, schemaName: string): Observable<SchemaCompletions> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/completion/preview-urls`);
return this.http.get<SchemaCompletions>(url);
}
public getFilters(appName: string, schemaName: string): Observable<QueryModel> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/filters`);

11
frontend/src/app/theme/_bootstrap.scss

@ -394,11 +394,10 @@ a {
}
&.active {
& {
background: none;
border-color: $color-theme-brand;
color: $color-theme-brand;
}
background: none;
border-color: $color-theme-brand;
border-radius: $border-radius;
color: $color-theme-brand;
i {
color: $color-theme-brand;
@ -415,6 +414,8 @@ a {
&:hover {
color: $color-theme-brand;
border-color: $color-border;
border-radius: $border-radius;
i {
color: $color-theme-brand;

Loading…
Cancel
Save