diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigurePreviewUrls.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigurePreviewUrls.cs new file mode 100644 index 000000000..0ce6dbcf5 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigurePreviewUrls.cs @@ -0,0 +1,16 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; + +namespace Squidex.Domain.Apps.Entities.Schemas.Commands +{ + public sealed class ConfigurePreviewUrls : SchemaCommand + { + public Dictionary PreviewUrls { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs index 943c3791c..820bf7b21 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -118,6 +119,19 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards }); } + public static void CanConfigurePreviewUrls(ConfigurePreviewUrls command) + { + Guard.NotNull(command, nameof(command)); + + Validate.It(() => "Cannot configure preview urls.", error => + { + if (command.PreviewUrls == null) + { + error("Preview Urls is required.", nameof(command.PreviewUrls)); + } + }); + } + public static void CanPublish(Schema schema, PublishSchema command) { Guard.NotNull(command, nameof(command)); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs b/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs index 98913f9e3..871d04314 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Collections.Generic; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; @@ -40,5 +41,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas string ScriptChange { get; } Schema SchemaDef { get; } + + Dictionary PreviewUrls { get; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs index 9ffda0930..2aff00cc3 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs @@ -179,6 +179,14 @@ namespace Squidex.Domain.Apps.Entities.Schemas ChangeCategory(c); }); + case ConfigurePreviewUrls configurePreviewUrls: + return UpdateAsync(configurePreviewUrls, c => + { + GuardSchema.CanConfigurePreviewUrls(c); + + ConfigurePreviewUrls(c); + }); + case DeleteSchema deleteSchema: return UpdateAsync(deleteSchema, c => { @@ -288,6 +296,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas RaiseEvent(SimpleMapper.Map(command, new SchemaCategoryChanged())); } + public void ConfigurePreviewUrls(ConfigurePreviewUrls command) + { + RaiseEvent(SimpleMapper.Map(command, new SchemaPreviewUrlsConfigured())); + } + public void Delete(DeleteSchema command) { RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted())); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs index 59aebd11e..6e8a837f3 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Collections.Generic; using System.Runtime.Serialization; using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; @@ -55,6 +56,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State [DataMember] public string ScriptChange { get; set; } + [DataMember] + public Dictionary PreviewUrls { get; set; } + [DataMember] public Schema SchemaDef { get; set; } @@ -166,6 +170,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State Category = @event.Name; } + protected void On(SchemaPreviewUrlsConfigured @event, FieldRegistry registry) + { + PreviewUrls = @event.PreviewUrls; + } + protected void On(SchemaPublished @event, FieldRegistry registry) { SchemaDef = SchemaDef.Publish(); diff --git a/src/Squidex.Domain.Apps.Events/Schemas/SchemaPreviewUrlsConfigured.cs b/src/Squidex.Domain.Apps.Events/Schemas/SchemaPreviewUrlsConfigured.cs new file mode 100644 index 000000000..3983af224 --- /dev/null +++ b/src/Squidex.Domain.Apps.Events/Schemas/SchemaPreviewUrlsConfigured.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using Squidex.Infrastructure.EventSourcing; + +namespace Squidex.Domain.Apps.Events.Schemas +{ + [EventType(nameof(SchemaPreviewUrlsConfigured))] + public sealed class SchemaPreviewUrlsConfigured : SchemaEvent + { + public Dictionary PreviewUrls { get; set; } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs index 11d441abd..e9ef7f5e0 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs @@ -71,6 +71,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// public string ScriptChange { get; set; } + /// + /// The preview Urls. + /// + public Dictionary PreviewUrls { get; set; } + /// /// The list of fields. /// diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs b/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs index 79271b493..3b42d6a8f 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; @@ -155,7 +156,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The schema object that needs to updated. /// /// 204 => Schema has been updated. - /// 400 => Schema properties are not valid. /// 404 => Schema or app not found. /// [HttpPut] @@ -169,6 +169,27 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + /// + /// Update the preview urls. + /// + /// The name of the app. + /// The name of the schema. + /// The preview urls for the schema. + /// + /// 204 => Schema has been updated. + /// 404 => Schema or app not found. + /// + [HttpPut] + [Route("apps/{app}/schemas/{name}/preview-urls")] + [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiCosts(1)] + public async Task PutPreviewUrls(string app, string name, [FromBody] Dictionary request) + { + await CommandBus.PublishAsync(new ConfigurePreviewUrls { PreviewUrls = request ?? new Dictionary() }); + + return NoContent(); + } + /// /// Update the scripts of a schema. /// diff --git a/src/Squidex/app/shared/state/comments.state.ts b/src/Squidex/app/shared/state/comments.state.ts index 1e45dab85..966fd8673 100644 --- a/src/Squidex/app/shared/state/comments.state.ts +++ b/src/Squidex/app/shared/state/comments.state.ts @@ -48,7 +48,7 @@ export class CommentsState extends State { private readonly commentsService: CommentsService, private readonly dialogs: DialogService ) { - super({ comments: ImmutableArray.empty(), version: new Version('') }); + super({ comments: ImmutableArray.empty(), version: new Version('-1') }); } public load(): Observable { diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs index 27aacd8ab..6743bc7f9 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs @@ -466,6 +466,23 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards GuardSchema.CanReorder(schema_0, command); } + [Fact] + public void CanConfigurePreviewUrls_should_throw_exception_if_preview_urls_null() + { + var command = new ConfigurePreviewUrls { PreviewUrls = null }; + + ValidationAssert.Throws(() => GuardSchema.CanConfigurePreviewUrls(command), + new ValidationError("Preview Urls is required.", "PreviewUrls")); + } + + [Fact] + public void CanConfigurePreviewUrls_should_not_throw_exception_if_valid() + { + var command = new ConfigurePreviewUrls { PreviewUrls = new Dictionary() }; + + GuardSchema.CanConfigurePreviewUrls(command); + } + [Fact] public void CanChangeCategory_should_not_throw_exception() { diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs index cd880f849..b16a1fa97 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs @@ -225,6 +225,31 @@ namespace Squidex.Domain.Apps.Entities.Schemas ); } + [Fact] + public async Task ConfigurePreviewUrls_should_create_events_and_update_state() + { + var command = new ConfigurePreviewUrls + { + PreviewUrls = new Dictionary + { + ["Web"] = "web-url" + } + }; + + await ExecuteCreateAsync(); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(new EntitySavedResult(1)); + + Assert.Equal(command.PreviewUrls, sut.Snapshot.PreviewUrls); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new SchemaPreviewUrlsConfigured { PreviewUrls = command.PreviewUrls }) + ); + } + [Fact] public async Task Delete_should_create_events_and_update_state() {