mirror of https://github.com/Squidex/squidex.git
Browse Source
* First settings. * Migrations. * Angular update. * Type safe templates * Type safe templates * Backend Tests and minor fixes. * Some tests. * Cleanup and tests. * Fix tests for title component. * Remove patterns test. * Tests for settings.pull/687/head
committed by
GitHub
355 changed files with 6012 additions and 6328 deletions
@ -0,0 +1,112 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Migrations.OldEvents; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Collections; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Migrations; |
|||
|
|||
#pragma warning disable CS0618 // Type or member is obsolete
|
|||
|
|||
namespace Migrations.Migrations |
|||
{ |
|||
public sealed class CreateAppSettings : IMigration |
|||
{ |
|||
private readonly ICommandBus commandBus; |
|||
private readonly IEventDataFormatter eventDataFormatter; |
|||
private readonly IEventStore eventStore; |
|||
|
|||
public CreateAppSettings(ICommandBus commandBus, |
|||
IEventDataFormatter eventDataFormatter, |
|||
IEventStore eventStore) |
|||
{ |
|||
this.commandBus = commandBus; |
|||
this.eventDataFormatter = eventDataFormatter; |
|||
this.eventStore = eventStore; |
|||
} |
|||
|
|||
public async Task UpdateAsync() |
|||
{ |
|||
var apps = new Dictionary<NamedId<DomainId>, Dictionary<DomainId, (string Name, string Pattern, string? Message)>>(); |
|||
|
|||
await eventStore.QueryAsync(storedEvent => |
|||
{ |
|||
var @event = eventDataFormatter.ParseIfKnown(storedEvent); |
|||
|
|||
if (@event != null) |
|||
{ |
|||
switch (@event.Payload) |
|||
{ |
|||
case AppPatternAdded patternAdded: |
|||
{ |
|||
var patterns = apps.GetOrAddNew(patternAdded.AppId); |
|||
|
|||
patterns[patternAdded.PatternId] = (patternAdded.Name, patternAdded.Pattern, patternAdded.Message); |
|||
break; |
|||
} |
|||
|
|||
case AppPatternUpdated patternUpdated: |
|||
{ |
|||
var patterns = apps.GetOrAddNew(patternUpdated.AppId); |
|||
|
|||
patterns[patternUpdated.PatternId] = (patternUpdated.Name, patternUpdated.Pattern, patternUpdated.Message); |
|||
break; |
|||
} |
|||
|
|||
case AppPatternDeleted patternDeleted: |
|||
{ |
|||
var patterns = apps.GetOrAddNew(patternDeleted.AppId); |
|||
|
|||
patterns.Remove(patternDeleted.PatternId); |
|||
break; |
|||
} |
|||
|
|||
case AppArchived appArchived: |
|||
{ |
|||
apps.Remove(appArchived.AppId); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
}, "^app\\-"); |
|||
|
|||
var actor = RefToken.Client("Migrator"); |
|||
|
|||
foreach (var (appId, patterns) in apps) |
|||
{ |
|||
if (patterns.Count > 0) |
|||
{ |
|||
var settings = new AppSettings |
|||
{ |
|||
Patterns = patterns.Values.Select(x => new Pattern(x.Name, x.Pattern) |
|||
{ |
|||
Message = x.Message |
|||
}).ToReadOnlyCollection() |
|||
}; |
|||
|
|||
await commandBus.PublishAsync(new UpdateAppSettings |
|||
{ |
|||
AppId = appId, |
|||
Settings = settings, |
|||
FromRule = true, |
|||
Actor = actor |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,71 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Diagnostics.Contracts; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Collections; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Apps |
|||
{ |
|||
public sealed class AppPatterns : ImmutableDictionary<DomainId, AppPattern> |
|||
{ |
|||
public static readonly AppPatterns Empty = new AppPatterns(); |
|||
|
|||
private AppPatterns() |
|||
{ |
|||
} |
|||
|
|||
public AppPatterns(Dictionary<DomainId, AppPattern> inner) |
|||
: base(inner) |
|||
{ |
|||
} |
|||
|
|||
[Pure] |
|||
public AppPatterns Remove(DomainId id) |
|||
{ |
|||
return Without<AppPatterns>(id); |
|||
} |
|||
|
|||
[Pure] |
|||
public AppPatterns Add(DomainId id, string name, string pattern, string? message = null) |
|||
{ |
|||
Guard.NotNullOrEmpty(name, nameof(name)); |
|||
Guard.NotNullOrEmpty(pattern, nameof(pattern)); |
|||
|
|||
var newPattern = new AppPattern(name, pattern) { Message = message }; |
|||
|
|||
return With<AppPatterns>(id, newPattern); |
|||
} |
|||
|
|||
[Pure] |
|||
public AppPatterns Update(DomainId id, string? name = null, string? pattern = null, string? message = null) |
|||
{ |
|||
if (!TryGetValue(id, out var appPattern)) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
var newPattern = appPattern with |
|||
{ |
|||
Message = message |
|||
}; |
|||
|
|||
if (!string.IsNullOrWhiteSpace(name)) |
|||
{ |
|||
newPattern = newPattern with { Name = name }; |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(pattern)) |
|||
{ |
|||
newPattern = newPattern with { Pattern = pattern }; |
|||
} |
|||
|
|||
return With<AppPatterns>(id, newPattern); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.ObjectModel; |
|||
using Squidex.Infrastructure.Collections; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Apps |
|||
{ |
|||
[Equals(DoNotAddEqualityOperators = true)] |
|||
public sealed class AppSettings |
|||
{ |
|||
public static readonly AppSettings Empty = new AppSettings(); |
|||
|
|||
public ReadOnlyCollection<Pattern> Patterns { get; init; } = ReadOnlyCollection.Empty<Pattern>(); |
|||
|
|||
public ReadOnlyCollection<Editor> Editors { get; init; } = ReadOnlyCollection.Empty<Editor>(); |
|||
|
|||
public bool HideScheduler { get; init; } |
|||
|
|||
public bool HideDateTimeModeButton { get; init; } |
|||
|
|||
public bool HideDateTimeQuickButtons { get; init; } |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Apps |
|||
{ |
|||
public sealed record Editor(string Name, string Url) |
|||
{ |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Apps.Json |
|||
{ |
|||
public sealed class AppPatternsSurrogate : Dictionary<DomainId, AppPattern>, ISurrogate<AppPatterns> |
|||
{ |
|||
public void FromSource(AppPatterns source) |
|||
{ |
|||
foreach (var (key, pattern) in source) |
|||
{ |
|||
Add(key, pattern); |
|||
} |
|||
} |
|||
|
|||
public AppPatterns ToSource() |
|||
{ |
|||
if (Count == 0) |
|||
{ |
|||
return AppPatterns.Empty; |
|||
} |
|||
|
|||
return new AppPatterns(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps.Commands |
|||
{ |
|||
public sealed class AddPattern : AppUpdateCommand |
|||
{ |
|||
public DomainId PatternId { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public string Pattern { get; set; } |
|||
|
|||
public string? Message { get; set; } |
|||
|
|||
public AddPattern() |
|||
{ |
|||
PatternId = DomainId.NewGuid(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,22 +1,16 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps.Commands |
|||
{ |
|||
public sealed class UpdatePattern : AppUpdateCommand |
|||
public sealed class UpdateAppSettings : AppUpdateCommand |
|||
{ |
|||
public DomainId PatternId { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public string Pattern { get; set; } |
|||
|
|||
public string? Message { get; set; } |
|||
public AppSettings Settings { get; set; } |
|||
} |
|||
} |
|||
@ -1,109 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Translations; |
|||
using Squidex.Infrastructure.Validation; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards |
|||
{ |
|||
public static class GuardAppPatterns |
|||
{ |
|||
public static void CanAdd(AddPattern command, IAppEntity app) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
var patterns = app.Patterns; |
|||
|
|||
Validate.It(e => |
|||
{ |
|||
if (command.PatternId == DomainId.Empty) |
|||
{ |
|||
e(Not.Defined(nameof(command.PatternId)), nameof(command.PatternId)); |
|||
} |
|||
|
|||
if (string.IsNullOrWhiteSpace(command.Name)) |
|||
{ |
|||
e(Not.Defined(nameof(command.Name)), nameof(command.Name)); |
|||
} |
|||
|
|||
if (patterns.Values.Any(x => x.Name.Equals(command.Name, StringComparison.OrdinalIgnoreCase))) |
|||
{ |
|||
e(T.Get("apps.patterns.nameAlreadyExists")); |
|||
} |
|||
|
|||
if (string.IsNullOrWhiteSpace(command.Pattern)) |
|||
{ |
|||
e(Not.Defined(nameof(command.Pattern)), nameof(command.Pattern)); |
|||
} |
|||
else if (!command.Pattern.IsValidRegex()) |
|||
{ |
|||
e(Not.Valid(nameof(command.Pattern)), nameof(command.Pattern)); |
|||
} |
|||
|
|||
if (patterns.Values.Any(x => x.Pattern == command.Pattern)) |
|||
{ |
|||
e(T.Get("apps.patterns.patternAlreadyExists")); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public static void CanDelete(DeletePattern command, IAppEntity app) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
var patterns = app.Patterns; |
|||
|
|||
if (!patterns.ContainsKey(command.PatternId)) |
|||
{ |
|||
throw new DomainObjectNotFoundException(command.PatternId.ToString()); |
|||
} |
|||
} |
|||
|
|||
public static void CanUpdate(UpdatePattern command, IAppEntity app) |
|||
{ |
|||
Guard.NotNull(command, nameof(command)); |
|||
|
|||
var patterns = app.Patterns; |
|||
|
|||
if (!patterns.ContainsKey(command.PatternId)) |
|||
{ |
|||
throw new DomainObjectNotFoundException(command.PatternId.ToString()); |
|||
} |
|||
|
|||
Validate.It(e => |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(command.Name)) |
|||
{ |
|||
e(Not.Defined(nameof(command.Name)), nameof(command.Name)); |
|||
} |
|||
|
|||
if (patterns.Any(x => x.Key != command.PatternId && x.Value.Name.Equals(command.Name, StringComparison.OrdinalIgnoreCase))) |
|||
{ |
|||
e(T.Get("apps.patterns.nameAlreadyExists")); |
|||
} |
|||
|
|||
if (string.IsNullOrWhiteSpace(command.Pattern)) |
|||
{ |
|||
e(Not.Defined(nameof(command.Pattern)), nameof(command.Pattern)); |
|||
} |
|||
else if (!command.Pattern.IsValidRegex()) |
|||
{ |
|||
e(Not.Valid(nameof(command.Pattern)), nameof(command.Pattern)); |
|||
} |
|||
|
|||
if (patterns.Any(x => x.Key != command.PatternId && x.Value.Pattern == command.Pattern)) |
|||
{ |
|||
e(T.Get("apps.patterns.patternAlreadyExists")); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public sealed class InitialPatterns : Dictionary<DomainId, AppPattern> |
|||
{ |
|||
public InitialPatterns() |
|||
{ |
|||
} |
|||
|
|||
public InitialPatterns(Dictionary<DomainId, AppPattern> patterns) |
|||
: base(patterns) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
|
|||
namespace Squidex.Domain.Apps.Events.Apps |
|||
{ |
|||
[EventType(nameof(AppSettingsUpdated))] |
|||
public sealed class AppSettingsUpdated : AppEvent |
|||
{ |
|||
public AppSettings Settings { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Linq; |
|||
using Squidex.Infrastructure.Security; |
|||
|
|||
namespace Squidex.Shared |
|||
{ |
|||
public static class PermissionExtensions |
|||
{ |
|||
public static bool Allows(this PermissionSet permissions, string id, string app = Permission.Any, string schema = Permission.Any) |
|||
{ |
|||
var permission = Permissions.ForApp(id, app, schema); |
|||
|
|||
return permissions.Allows(permission); |
|||
} |
|||
|
|||
public static string[] ToAppNames(this PermissionSet permissions) |
|||
{ |
|||
var matching = permissions.Where(x => x.StartsWith("squidex.apps.")); |
|||
|
|||
var result = |
|||
matching |
|||
.Select(x => x.Id.Split('.')).Where(x => x.Length > 2) |
|||
.Select(x => x[2]) |
|||
.Distinct() |
|||
.ToArray(); |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -1,151 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Net.Http.Headers; |
|||
using Squidex.Areas.Api.Controllers.Apps.Models; |
|||
using Squidex.Domain.Apps.Entities.Apps; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Shared; |
|||
using Squidex.Web; |
|||
|
|||
namespace Squidex.Areas.Api.Controllers.Apps |
|||
{ |
|||
/// <summary>
|
|||
/// Manages and configures app patterns.
|
|||
/// </summary>
|
|||
[ApiExplorerSettings(GroupName = nameof(Apps))] |
|||
public sealed class AppPatternsController : ApiController |
|||
{ |
|||
public AppPatternsController(ICommandBus commandBus) |
|||
: base(commandBus) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get app patterns.
|
|||
/// </summary>
|
|||
/// <param name="app">The name of the app.</param>
|
|||
/// <returns>
|
|||
/// 200 => Patterns returned.
|
|||
/// 404 => App not found.
|
|||
/// </returns>
|
|||
/// <remarks>
|
|||
/// Gets all configured regex patterns for the app with the specified name.
|
|||
/// </remarks>
|
|||
[HttpGet] |
|||
[Route("apps/{app}/patterns/")] |
|||
[ProducesResponseType(typeof(PatternsDto), StatusCodes.Status200OK)] |
|||
[ApiPermissionOrAnonymous(Permissions.AppPatternsRead)] |
|||
[ApiCosts(0)] |
|||
public IActionResult GetPatterns(string app) |
|||
{ |
|||
var response = Deferred.Response(() => |
|||
{ |
|||
return GetResponse(App); |
|||
}); |
|||
|
|||
Response.Headers[HeaderNames.ETag] = App.ToEtag(); |
|||
|
|||
return Ok(response); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create a new app pattern.
|
|||
/// </summary>
|
|||
/// <param name="app">The name of the app.</param>
|
|||
/// <param name="request">Pattern to be added to the app.</param>
|
|||
/// <returns>
|
|||
/// 201 => Pattern created.
|
|||
/// 400 => Pattern request not valid.
|
|||
/// 404 => App not found.
|
|||
/// </returns>
|
|||
[HttpPost] |
|||
[Route("apps/{app}/patterns/")] |
|||
[ProducesResponseType(typeof(PatternsDto), 201)] |
|||
[ApiPermissionOrAnonymous(Permissions.AppPatternsCreate)] |
|||
[ApiCosts(1)] |
|||
public async Task<IActionResult> PostPattern(string app, [FromBody] UpdatePatternDto request) |
|||
{ |
|||
var command = request.ToAddCommand(); |
|||
|
|||
var response = await InvokeCommandAsync(command); |
|||
|
|||
return CreatedAtAction(nameof(GetPatterns), new { app }, response); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Update an app pattern.
|
|||
/// </summary>
|
|||
/// <param name="app">The name of the app.</param>
|
|||
/// <param name="id">The id of the pattern to be updated.</param>
|
|||
/// <param name="request">Pattern to be updated for the app.</param>
|
|||
/// <returns>
|
|||
/// 200 => Pattern updated.
|
|||
/// 400 => Pattern request not valid.
|
|||
/// 404 => Pattern or app not found.
|
|||
/// </returns>
|
|||
[HttpPut] |
|||
[Route("apps/{app}/patterns/{id}/")] |
|||
[ProducesResponseType(typeof(PatternsDto), StatusCodes.Status200OK)] |
|||
[ApiPermissionOrAnonymous(Permissions.AppPatternsUpdate)] |
|||
[ApiCosts(1)] |
|||
public async Task<IActionResult> PutPattern(string app, DomainId id, [FromBody] UpdatePatternDto request) |
|||
{ |
|||
var command = request.ToUpdateCommand(id); |
|||
|
|||
var response = await InvokeCommandAsync(command); |
|||
|
|||
return Ok(response); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Delete an app pattern.
|
|||
/// </summary>
|
|||
/// <param name="app">The name of the app.</param>
|
|||
/// <param name="id">The id of the pattern to be deleted.</param>
|
|||
/// <returns>
|
|||
/// 200 => Pattern deleted.
|
|||
/// 404 => Pattern or app not found.
|
|||
/// </returns>
|
|||
/// <remarks>
|
|||
/// Schemas using this pattern will still function using the same Regular Expression.
|
|||
/// </remarks>
|
|||
[HttpDelete] |
|||
[Route("apps/{app}/patterns/{id}/")] |
|||
[ProducesResponseType(typeof(PatternsDto), StatusCodes.Status200OK)] |
|||
[ApiPermissionOrAnonymous(Permissions.AppPatternsDelete)] |
|||
[ApiCosts(1)] |
|||
public async Task<IActionResult> DeletePattern(string app, DomainId id) |
|||
{ |
|||
var command = new DeletePattern { PatternId = id }; |
|||
|
|||
var response = await InvokeCommandAsync(command); |
|||
|
|||
return Ok(response); |
|||
} |
|||
|
|||
private async Task<PatternsDto> InvokeCommandAsync(ICommand command) |
|||
{ |
|||
var context = await CommandBus.PublishAsync(command); |
|||
|
|||
var result = context.Result<IAppEntity>(); |
|||
var response = GetResponse(result); |
|||
|
|||
return response; |
|||
} |
|||
|
|||
private PatternsDto GetResponse(IAppEntity result) |
|||
{ |
|||
return PatternsDto.FromApp(result, Resources); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Squidex.Domain.Apps.Entities.Apps; |
|||
using Squidex.Infrastructure.Reflection; |
|||
using Squidex.Infrastructure.Validation; |
|||
using Squidex.Web; |
|||
|
|||
namespace Squidex.Areas.Api.Controllers.Apps.Models |
|||
{ |
|||
public sealed class AppSettingsDto : Resource |
|||
{ |
|||
/// <summary>
|
|||
/// The configured app patterns.
|
|||
/// </summary>
|
|||
[LocalizedRequired] |
|||
public List<PatternDto> Patterns { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The configured UI editors.
|
|||
/// </summary>
|
|||
[LocalizedRequired] |
|||
public List<EditorDto> Editors { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Hide the scheduler for content items.
|
|||
/// </summary>
|
|||
public bool HideScheduler { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The version of the app.
|
|||
/// </summary>
|
|||
public long Version { get; set; } |
|||
|
|||
public static AppSettingsDto FromApp(IAppEntity app, Resources resources) |
|||
{ |
|||
var settings = app.Settings; |
|||
|
|||
var result = new AppSettingsDto |
|||
{ |
|||
HideScheduler = settings.HideScheduler, |
|||
Patterns = |
|||
settings.Patterns |
|||
.Select(x => SimpleMapper.Map(x, new PatternDto())).ToList(), |
|||
Editors = |
|||
settings.Editors |
|||
.Select(x => SimpleMapper.Map(x, new EditorDto())).ToList(), |
|||
Version = app.Version |
|||
}; |
|||
|
|||
return result.CreateLinks(resources); |
|||
} |
|||
|
|||
private AppSettingsDto CreateLinks(Resources resources) |
|||
{ |
|||
var values = new { app = resources.App }; |
|||
|
|||
AddSelfLink(resources.Url<AppsController>(x => nameof(x.GetAppSettings), values)); |
|||
|
|||
if (resources.CanUpdateSettings) |
|||
{ |
|||
AddPutLink("update", resources.Url<AppsController>(x => nameof(x.PutAppSettings), values)); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure.Validation; |
|||
|
|||
namespace Squidex.Areas.Api.Controllers.Apps.Models |
|||
{ |
|||
public sealed class EditorDto |
|||
{ |
|||
/// <summary>
|
|||
/// The name of the editor.
|
|||
/// </summary>
|
|||
[LocalizedRequired] |
|||
public string Name { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The url to the editor.
|
|||
/// </summary>
|
|||
[LocalizedRequired] |
|||
public string Url { get; set; } |
|||
} |
|||
} |
|||
@ -1,47 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Linq; |
|||
using Squidex.Domain.Apps.Entities.Apps; |
|||
using Squidex.Infrastructure.Validation; |
|||
using Squidex.Web; |
|||
|
|||
namespace Squidex.Areas.Api.Controllers.Apps.Models |
|||
{ |
|||
public sealed class PatternsDto : Resource |
|||
{ |
|||
/// <summary>
|
|||
/// The patterns.
|
|||
/// </summary>
|
|||
[LocalizedRequired] |
|||
public PatternDto[] Items { get; set; } |
|||
|
|||
public static PatternsDto FromApp(IAppEntity app, Resources resources) |
|||
{ |
|||
var result = new PatternsDto |
|||
{ |
|||
Items = app.Patterns.Select(x => PatternDto.FromPattern(x.Key, x.Value, resources)).ToArray() |
|||
}; |
|||
|
|||
return result.CreateLinks(resources); |
|||
} |
|||
|
|||
private PatternsDto CreateLinks(Resources resources) |
|||
{ |
|||
var values = new { app = resources.App }; |
|||
|
|||
AddSelfLink(resources.Url<AppPatternsController>(x => nameof(x.GetPatterns), values)); |
|||
|
|||
if (resources.CanCreatePattern) |
|||
{ |
|||
AddPostLink("create", resources.Url<AppPatternsController>(x => nameof(x.PostPattern), values)); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Linq; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Infrastructure.Collections; |
|||
|
|||
namespace Squidex.Areas.Api.Controllers.Apps.Models |
|||
{ |
|||
public sealed class UpdateAppSettingsDto |
|||
{ |
|||
/// <summary>
|
|||
/// The configured app patterns.
|
|||
/// </summary>
|
|||
[Required] |
|||
public List<PatternDto> Patterns { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The configured UI editors.
|
|||
/// </summary>
|
|||
[Required] |
|||
public List<EditorDto> Editors { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Hide the scheduler for content items.
|
|||
/// </summary>
|
|||
public bool HideScheduler { get; set; } |
|||
|
|||
public UpdateAppSettings ToCommand() |
|||
{ |
|||
return new UpdateAppSettings |
|||
{ |
|||
Settings = new AppSettings |
|||
{ |
|||
HideScheduler = HideScheduler, |
|||
Patterns = |
|||
Patterns?.Select(x => new Pattern(x.Name, x.Regex) |
|||
{ |
|||
Message = x.Message |
|||
}).ToReadOnlyCollection()!, |
|||
Editors = |
|||
Editors?.Select(x => new Editor(x.Name, x.Url)).ToReadOnlyCollection()! |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -1,44 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Reflection; |
|||
using Squidex.Infrastructure.Validation; |
|||
|
|||
namespace Squidex.Areas.Api.Controllers.Apps.Models |
|||
{ |
|||
public class UpdatePatternDto |
|||
{ |
|||
/// <summary>
|
|||
/// The name of the suggestion.
|
|||
/// </summary>
|
|||
[LocalizedRequired] |
|||
public string Name { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The regex pattern.
|
|||
/// </summary>
|
|||
[LocalizedRequired] |
|||
public string Pattern { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The regex message.
|
|||
/// </summary>
|
|||
public string? Message { get; set; } |
|||
|
|||
public AddPattern ToAddCommand() |
|||
{ |
|||
return SimpleMapper.Map(this, new AddPattern()); |
|||
} |
|||
|
|||
public UpdatePattern ToUpdateCommand(DomainId id) |
|||
{ |
|||
return SimpleMapper.Map(this, new UpdatePattern { PatternId = id }); |
|||
} |
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using FluentAssertions; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Model.Apps |
|||
{ |
|||
public class AppPatternJsonTests |
|||
{ |
|||
[Fact] |
|||
public void Should_serialize_and_deserialize() |
|||
{ |
|||
var patterns = AppPatterns.Empty; |
|||
|
|||
var guid1 = DomainId.NewGuid(); |
|||
var guid2 = DomainId.NewGuid(); |
|||
var guid3 = DomainId.NewGuid(); |
|||
|
|||
patterns = patterns.Add(guid1, "Name1", "Pattern1", "Default"); |
|||
patterns = patterns.Add(guid2, "Name2", "Pattern2", "Default"); |
|||
patterns = patterns.Add(guid3, "Name3", "Pattern3", "Default"); |
|||
patterns = patterns.Update(guid2, "Name2 Update", "Pattern2 Update", "Default2"); |
|||
patterns = patterns.Update(guid3, "Name3 Update", "Pattern3 Update", "Default3"); |
|||
patterns = patterns.Remove(guid1); |
|||
|
|||
var serialized = patterns.SerializeAndDeserialize(); |
|||
|
|||
serialized.Should().BeEquivalentTo(patterns); |
|||
} |
|||
} |
|||
} |
|||
@ -1,86 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Linq; |
|||
using FluentAssertions; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable SA1310 // Field names must not contain underscore
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Model.Apps |
|||
{ |
|||
public class AppPatternsTests |
|||
{ |
|||
private readonly AppPatterns patterns_0; |
|||
private readonly DomainId firstId = DomainId.NewGuid(); |
|||
private readonly DomainId id = DomainId.NewGuid(); |
|||
|
|||
public AppPatternsTests() |
|||
{ |
|||
patterns_0 = AppPatterns.Empty.Add(firstId, "Default", "Default Pattern", "Message"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_add_pattern() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(id, "NewPattern", "New Pattern", "Message"); |
|||
|
|||
patterns_1[id].Should().BeEquivalentTo(new AppPattern("NewPattern", "New Pattern") with { Message = "Message" }); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_do_nothing_if_adding_pattern_with_same_id() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(firstId, "Default", "Default Pattern", "Message"); |
|||
|
|||
Assert.Same(patterns_1, patterns_0); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_update_pattern() |
|||
{ |
|||
var patterns_1 = patterns_0.Update(firstId, "UpdatePattern", "Update Pattern"); |
|||
|
|||
patterns_1[firstId].Should().BeEquivalentTo(new AppPattern("UpdatePattern", "Update Pattern")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_patterns_if_pattern_is_updated_with_same_values() |
|||
{ |
|||
var patterns_1 = patterns_0.Update(firstId, "Default", "Default Pattern", "Message"); |
|||
|
|||
Assert.Same(patterns_0, patterns_1); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_patterns_if_pattern_to_update_not_found() |
|||
{ |
|||
var patterns_1 = patterns_0.Update(id, "NewPattern", "NewPattern", "Message"); |
|||
|
|||
Assert.Same(patterns_0, patterns_1); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_remove_pattern() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(id, "Name1", "Pattern1"); |
|||
var patterns_2 = patterns_1.Remove(firstId); |
|||
|
|||
Assert.Equal(id, patterns_2.Keys.Single()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_do_nothing_if_remove_pattern_not_found() |
|||
{ |
|||
var patterns_1 = patterns_0.Remove(id); |
|||
|
|||
Assert.Same(patterns_0, patterns_1); |
|||
} |
|||
} |
|||
} |
|||
@ -1,192 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.TestHelpers; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Validation; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable SA1310 // Field names must not contain underscore
|
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards |
|||
{ |
|||
public class GuardAppPatternsTests : IClassFixture<TranslationsFixture> |
|||
{ |
|||
private readonly DomainId patternId = DomainId.NewGuid(); |
|||
private readonly AppPatterns patterns_0 = AppPatterns.Empty; |
|||
|
|||
[Fact] |
|||
public void CanAdd_should_throw_exception_if_name_empty() |
|||
{ |
|||
var command = new AddPattern { PatternId = patternId, Name = string.Empty, Pattern = ".*" }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanAdd(command, App(patterns_0)), |
|||
new ValidationError("Name is required.", "Name")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanAdd_should_throw_exception_if_pattern_empty() |
|||
{ |
|||
var command = new AddPattern { PatternId = patternId, Name = "any", Pattern = string.Empty }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanAdd(command, App(patterns_0)), |
|||
new ValidationError("Pattern is required.", "Pattern")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanAdd_should_throw_exception_if_pattern_not_valid() |
|||
{ |
|||
var command = new AddPattern { PatternId = patternId, Name = "any", Pattern = "[0-9{1}" }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanAdd(command, App(patterns_0)), |
|||
new ValidationError("Pattern is not a valid value.", "Pattern")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanAdd_should_throw_exception_if_name_exists() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(DomainId.NewGuid(), "any", "[a-z]", "Message"); |
|||
|
|||
var command = new AddPattern { PatternId = patternId, Name = "any", Pattern = ".*" }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanAdd(command, App(patterns_1)), |
|||
new ValidationError("A pattern with the same name already exists.")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanAdd_should_throw_exception_if_pattern_exists() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(DomainId.NewGuid(), "any", "[a-z]", "Message"); |
|||
|
|||
var command = new AddPattern { PatternId = patternId, Name = "other", Pattern = "[a-z]" }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanAdd(command, App(patterns_1)), |
|||
new ValidationError("This pattern already exists but with another name.")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanAdd_should_not_throw_exception_if_command_is_valid() |
|||
{ |
|||
var command = new AddPattern { PatternId = patternId, Name = "any", Pattern = ".*" }; |
|||
|
|||
GuardAppPatterns.CanAdd(command, App(patterns_0)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanDelete_should_throw_exception_if_pattern_not_found() |
|||
{ |
|||
var command = new DeletePattern { PatternId = patternId }; |
|||
|
|||
Assert.Throws<DomainObjectNotFoundException>(() => GuardAppPatterns.CanDelete(command, App(patterns_0))); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanDelete_should_not_throw_exception_if_command_is_valid() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(patternId, "any", ".*", "Message"); |
|||
|
|||
var command = new DeletePattern { PatternId = patternId }; |
|||
|
|||
GuardAppPatterns.CanDelete(command, App(patterns_1)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdate_should_throw_exception_if_name_empty() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(patternId, "any", ".*", "Message"); |
|||
|
|||
var command = new UpdatePattern { PatternId = patternId, Name = string.Empty, Pattern = ".*" }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanUpdate(command, App(patterns_1)), |
|||
new ValidationError("Name is required.", "Name")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdate_should_throw_exception_if_pattern_empty() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(patternId, "any", ".*", "Message"); |
|||
|
|||
var command = new UpdatePattern { PatternId = patternId, Name = "any", Pattern = string.Empty }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanUpdate(command, App(patterns_1)), |
|||
new ValidationError("Pattern is required.", "Pattern")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdate_should_throw_exception_if_pattern_not_valid() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(patternId, "any", ".*", "Message"); |
|||
|
|||
var command = new UpdatePattern { PatternId = patternId, Name = "any", Pattern = "[0-9{1}" }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanUpdate(command, App(patterns_1)), |
|||
new ValidationError("Pattern is not a valid value.", "Pattern")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdate_should_throw_exception_if_name_exists() |
|||
{ |
|||
var id1 = DomainId.NewGuid(); |
|||
var id2 = DomainId.NewGuid(); |
|||
|
|||
var patterns_1 = patterns_0.Add(id1, "Pattern1", "[0-5]", "Message"); |
|||
var patterns_2 = patterns_1.Add(id2, "Pattern2", "[0-4]", "Message"); |
|||
|
|||
var command = new UpdatePattern { PatternId = id2, Name = "Pattern1", Pattern = "[0-4]" }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanUpdate(command, App(patterns_2)), |
|||
new ValidationError("A pattern with the same name already exists.")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdate_should_throw_exception_if_pattern_exists() |
|||
{ |
|||
var id1 = DomainId.NewGuid(); |
|||
var id2 = DomainId.NewGuid(); |
|||
|
|||
var patterns_1 = patterns_0.Add(id1, "Pattern1", "[0-5]", "Message"); |
|||
var patterns_2 = patterns_1.Add(id2, "Pattern2", "[0-4]", "Message"); |
|||
|
|||
var command = new UpdatePattern { PatternId = id2, Name = "Pattern2", Pattern = "[0-5]" }; |
|||
|
|||
ValidationAssert.Throws(() => GuardAppPatterns.CanUpdate(command, App(patterns_2)), |
|||
new ValidationError("This pattern already exists but with another name.")); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdate_should_throw_exception_if_pattern_does_not_exists() |
|||
{ |
|||
var command = new UpdatePattern { PatternId = patternId, Name = "Pattern1", Pattern = ".*" }; |
|||
|
|||
Assert.Throws<DomainObjectNotFoundException>(() => GuardAppPatterns.CanUpdate(command, App(patterns_0))); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CanUpdate_should_not_throw_exception_if_pattern_exist_with_valid_command() |
|||
{ |
|||
var patterns_1 = patterns_0.Add(patternId, "any", ".*", "Message"); |
|||
|
|||
var command = new UpdatePattern { PatternId = patternId, Name = "Pattern1", Pattern = ".*" }; |
|||
|
|||
GuardAppPatterns.CanUpdate(command, App(patterns_1)); |
|||
} |
|||
|
|||
private static IAppEntity App(AppPatterns patterns) |
|||
{ |
|||
var app = A.Fake<IAppEntity>(); |
|||
|
|||
A.CallTo(() => app.Patterns) |
|||
.Returns(patterns); |
|||
|
|||
return app; |
|||
} |
|||
} |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
<sqx-title message="i18n:common.clusterPageTitle"></sqx-title> |
|||
|
|||
<sqx-panel desiredWidth="*" minWidth="50rem" isFullSize="true"> |
|||
<sqx-panel desiredWidth="*" minWidth="50rem" [isFullSize]="true"> |
|||
<div inner> |
|||
<iframe src="./orleans"></iframe> |
|||
</div> |
|||
|
|||
@ -1,5 +1,5 @@ |
|||
<sqx-title message="i18n:api.graphqlPageTitle"></sqx-title> |
|||
|
|||
<sqx-panel desiredWidth="*" minWidth="50rem" isFullSize="true"> |
|||
<sqx-panel desiredWidth="*" minWidth="50rem" [isFullSize]="true"> |
|||
<div inner #graphiQLContainer></div> |
|||
</sqx-panel> |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue