From c3415d28b86c8bc77c039c99058b7db47c0c425b Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Thu, 27 May 2021 21:09:01 +0200 Subject: [PATCH] Cleanup/middlewares (#713) * Another ng test * Cleanup * Formatting. * Simplified templates. --- .../Samples/Middleware/TemplateInstance.cs | 38 ++ .../Samples/Middleware/TemplateMiddleware.cs | 54 +++ .../Samples/Middleware/TemplatePlugin.cs | 25 ++ backend/i18n/frontend_en.json | 4 - backend/i18n/frontend_it.json | 4 - backend/i18n/frontend_nl.json | 4 - backend/i18n/source/frontend_en.json | 4 - backend/i18n/source/frontend_it.json | 4 - backend/i18n/source/frontend_nl.json | 4 - .../Squidex.Translator/Processes/Helper.cs | 2 +- .../AlwaysCreateClientCommandMiddleware.cs | 10 +- .../Templates/Builders/ArrayFieldBuilder.cs | 10 +- .../Apps/Templates/Builders/FieldBuilder.cs | 18 +- ...BlogCommandMiddleware.cs => CreateBlog.cs} | 47 +-- .../CreateIdentityCommandMiddleware.cs | 281 --------------- .../CreateIdentityV2CommandMiddleware.cs | 332 ------------------ ...eCommandMiddleware.cs => CreateProfile.cs} | 59 +--- .../Apps/Templates/DefaultScripts.cs | 19 - .../Apps/Templates/ITemplate.cs | 18 + .../Templates/TemplateCommandMiddleware.cs | 53 +++ .../Types/Contents/ContentGraphType.cs | 1 - .../Squidex/Config/Domain/CommandsServices.cs | 14 +- .../Apps/Templates/TemplatesTests.cs | 18 +- .../apps/pages/apps-page.component.html | 34 -- 24 files changed, 256 insertions(+), 801 deletions(-) create mode 100644 backend/extensions/Squidex.Extensions/Samples/Middleware/TemplateInstance.cs create mode 100644 backend/extensions/Squidex.Extensions/Samples/Middleware/TemplateMiddleware.cs create mode 100644 backend/extensions/Squidex.Extensions/Samples/Middleware/TemplatePlugin.cs rename backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/{CreateBlogCommandMiddleware.cs => CreateBlog.cs} (72%) delete mode 100644 backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs delete mode 100644 backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityV2CommandMiddleware.cs rename backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/{CreateProfileCommandMiddleware.cs => CreateProfile.cs} (82%) create mode 100644 backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/ITemplate.cs create mode 100644 backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplateCommandMiddleware.cs diff --git a/backend/extensions/Squidex.Extensions/Samples/Middleware/TemplateInstance.cs b/backend/extensions/Squidex.Extensions/Samples/Middleware/TemplateInstance.cs new file mode 100644 index 000000000..4e7c53d1b --- /dev/null +++ b/backend/extensions/Squidex.Extensions/Samples/Middleware/TemplateInstance.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using Squidex.Domain.Apps.Entities.Apps.Templates; +using Squidex.Domain.Apps.Entities.Apps.Templates.Builders; + +namespace Squidex.Extensions.Samples.Middleware +{ + public class TemplateInstance : ITemplate + { + public string Name { get; } = "custom2"; + + public Task RunAsync(PublishTemplate publish) + { + var schema = + SchemaBuilder.Create("Blogs") + .AddString("Title", f => f + .Length(100) + .Required()) + .AddString("Slug", f => f + .Length(100) + .Required() + .Disabled()) + .AddString("Text", f => f + .Length(1000) + .Required() + .AsRichText()) + .Build(); + + return publish(schema); + } + } +} diff --git a/backend/extensions/Squidex.Extensions/Samples/Middleware/TemplateMiddleware.cs b/backend/extensions/Squidex.Extensions/Samples/Middleware/TemplateMiddleware.cs new file mode 100644 index 000000000..60736e1b2 --- /dev/null +++ b/backend/extensions/Squidex.Extensions/Samples/Middleware/TemplateMiddleware.cs @@ -0,0 +1,54 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.Apps.Commands; +using Squidex.Domain.Apps.Entities.Apps.Templates.Builders; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; + +namespace Squidex.Extensions.Samples.Middleware +{ + public sealed class TemplateMiddleware : ICustomCommandMiddleware + { + public async Task HandleAsync(CommandContext context, NextDelegate next) + { + await next(context); + + if (context.Command is CreateApp createApp && context.IsCompleted && createApp.Template == "custom") + { + var appId = NamedId.Of(createApp.AppId, createApp.Name); + + var publish = new Func(command => + { + command.AppId = appId; + + return context.CommandBus.PublishAsync(command); + }); + + var schema = + SchemaBuilder.Create("Pages") + .AddString("Title", f => f + .Length(100) + .Required()) + .AddString("Slug", f => f + .Length(100) + .Required() + .Disabled()) + .AddString("Text", f => f + .Length(1000) + .Required() + .AsRichText()) + .Build(); + + await publish(schema); + } + } + } +} diff --git a/backend/extensions/Squidex.Extensions/Samples/Middleware/TemplatePlugin.cs b/backend/extensions/Squidex.Extensions/Samples/Middleware/TemplatePlugin.cs new file mode 100644 index 000000000..bae84baa6 --- /dev/null +++ b/backend/extensions/Squidex.Extensions/Samples/Middleware/TemplatePlugin.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Domain.Apps.Entities.Apps.Templates; +using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.Plugins; + +namespace Squidex.Extensions.Samples.Middleware +{ + public sealed class TemplatePlugin : IPlugin + { + public void ConfigureServices(IServiceCollection services, IConfiguration config) + { + services.AddSingleton(); + + services.AddSingleton(); + } + } +} diff --git a/backend/i18n/frontend_en.json b/backend/i18n/frontend_en.json index 53c074cfc..b49c90738 100644 --- a/backend/i18n/frontend_en.json +++ b/backend/i18n/frontend_en.json @@ -23,10 +23,6 @@ "apps.createBlogApp": "New Blog Sample", "apps.createBlogAppDescription": "Start with our ready to use blog.", "apps.createFailed": "Failed to create app. Please reload.", - "apps.createIdentityApp": "New Identity App", - "apps.createIdentityAppDescription": "Create app for Squidex Identity.", - "apps.createIdentityAppV2": "New Identity App V2", - "apps.createIdentityAppV2Description": "Create app for Squidex Identity V2.", "apps.createProfileApp": "New Profile Sample", "apps.createProfileAppDescription": "Create your profile page.", "apps.createWithTemplate": "Create {template} Sample", diff --git a/backend/i18n/frontend_it.json b/backend/i18n/frontend_it.json index 3bf76d595..c133c893d 100644 --- a/backend/i18n/frontend_it.json +++ b/backend/i18n/frontend_it.json @@ -23,10 +23,6 @@ "apps.createBlogApp": "Nuovo blog", "apps.createBlogAppDescription": "Inizia con un blog.", "apps.createFailed": "Non è stato possibile creare l'app. Per favore ricarica.", - "apps.createIdentityApp": "Nuova Identity App", - "apps.createIdentityAppDescription": "Crea un app per Squidex Identity.", - "apps.createIdentityAppV2": "Nuova Identity App V2", - "apps.createIdentityAppV2Description": "Creare un app per Squidex Identity V2.", "apps.createProfileApp": "Nuovo Profilo", "apps.createProfileAppDescription": "Crea la tua pagina del profilo.", "apps.createWithTemplate": "Create un esempio di {template}", diff --git a/backend/i18n/frontend_nl.json b/backend/i18n/frontend_nl.json index 0bf9db833..b2a16b822 100644 --- a/backend/i18n/frontend_nl.json +++ b/backend/i18n/frontend_nl.json @@ -23,10 +23,6 @@ "apps.createBlogApp": "Nieuw blogvoorbeeld", "apps.createBlogAppDescription": "Begin met onze gebruiksklare blog.", "apps.createFailed": "Maken van app is mislukt. Laad opnieuw.", - "apps.createIdentityApp": "Nieuwe identiteits-app", - "apps.createIdentityAppDescription": "Maak een app voor Squidex Identity.", - "apps.createIdentityAppV2": "Nieuwe identiteits-app V2", - "apps.createIdentityAppV2Description": "Maak een app voor Squidex Identity V2.", "apps.createProfileApp": "Nieuw profielvoorbeeld", "apps.createProfileAppDescription": "Maak uw profielpagina.", "apps.createWithTemplate": "Maak {sjabloon} voorbeeld", diff --git a/backend/i18n/source/frontend_en.json b/backend/i18n/source/frontend_en.json index 53c074cfc..b49c90738 100644 --- a/backend/i18n/source/frontend_en.json +++ b/backend/i18n/source/frontend_en.json @@ -23,10 +23,6 @@ "apps.createBlogApp": "New Blog Sample", "apps.createBlogAppDescription": "Start with our ready to use blog.", "apps.createFailed": "Failed to create app. Please reload.", - "apps.createIdentityApp": "New Identity App", - "apps.createIdentityAppDescription": "Create app for Squidex Identity.", - "apps.createIdentityAppV2": "New Identity App V2", - "apps.createIdentityAppV2Description": "Create app for Squidex Identity V2.", "apps.createProfileApp": "New Profile Sample", "apps.createProfileAppDescription": "Create your profile page.", "apps.createWithTemplate": "Create {template} Sample", diff --git a/backend/i18n/source/frontend_it.json b/backend/i18n/source/frontend_it.json index 62aaa1801..902d4767a 100644 --- a/backend/i18n/source/frontend_it.json +++ b/backend/i18n/source/frontend_it.json @@ -23,10 +23,6 @@ "apps.createBlogApp": "Nuovo blog", "apps.createBlogAppDescription": "Inizia con un blog.", "apps.createFailed": "Non è stato possibile creare l'app. Per favore ricarica.", - "apps.createIdentityApp": "Nuova Identity App", - "apps.createIdentityAppDescription": "Crea un app per Squidex Identity.", - "apps.createIdentityAppV2": "Nuova Identity App V2", - "apps.createIdentityAppV2Description": "Creare un app per Squidex Identity V2.", "apps.createProfileApp": "Nuovo Profilo", "apps.createProfileAppDescription": "Crea la tua pagina del profilo.", "apps.createWithTemplate": "Create un esempio di {template}", diff --git a/backend/i18n/source/frontend_nl.json b/backend/i18n/source/frontend_nl.json index f6019dea5..8a1b57ebf 100644 --- a/backend/i18n/source/frontend_nl.json +++ b/backend/i18n/source/frontend_nl.json @@ -23,10 +23,6 @@ "apps.createBlogApp": "Nieuw blogvoorbeeld", "apps.createBlogAppDescription": "Begin met onze gebruiksklare blog.", "apps.createFailed": "Maken van app is mislukt. Laad opnieuw.", - "apps.createIdentityApp": "Nieuwe identiteits-app", - "apps.createIdentityAppDescription": "Maak een app voor Squidex Identity.", - "apps.createIdentityAppV2": "Nieuwe identiteits-app V2", - "apps.createIdentityAppV2Description": "Maak een app voor Squidex Identity V2.", "apps.createProfileApp": "Nieuw profielvoorbeeld", "apps.createProfileAppDescription": "Maak uw profielpagina.", "apps.createWithTemplate": "Maak {sjabloon} voorbeeld", diff --git a/backend/i18n/translator/Squidex.Translator/Processes/Helper.cs b/backend/i18n/translator/Squidex.Translator/Processes/Helper.cs index 4791557ac..7eaa2b191 100644 --- a/backend/i18n/translator/Squidex.Translator/Processes/Helper.cs +++ b/backend/i18n/translator/Squidex.Translator/Processes/Helper.cs @@ -27,7 +27,7 @@ namespace Squidex.Translator.Processes foreach (var (locale, texts) in service.Translations.Where(x => x.Key != service.MainLocale)) { Console.WriteLine(); - Console.WriteLine("Checking {0}", locale); + Console.WriteLine("----- CHECKING <{0}> -----", locale); var notTranslated = mainTranslations.Keys.Except(texts.Keys).ToList(); var notRequired = texts.Keys.Except(mainTranslations.Keys).ToList(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/AlwaysCreateClientCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/AlwaysCreateClientCommandMiddleware.cs index 7d6465cce..3b777549e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/AlwaysCreateClientCommandMiddleware.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/AlwaysCreateClientCommandMiddleware.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Threading.Tasks; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure; @@ -22,9 +23,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates { var appId = NamedId.Of(createApp.AppId, createApp.Name); - var command = new AttachClient { Id = "default", AppId = appId }; + var publish = new Func(command => + { + command.AppId = appId; - await context.CommandBus.PublishAsync(command); + return context.CommandBus.PublishAsync(command); + }); + + await publish(new AttachClient { Id = "default" }); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/ArrayFieldBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/ArrayFieldBuilder.cs index 519cb5537..6ccf9028c 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/ArrayFieldBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/ArrayFieldBuilder.cs @@ -15,10 +15,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { public class ArrayFieldBuilder : FieldBuilder { - protected new UpsertSchemaField field + private UpsertSchemaField TypedField { - get => base.Field as UpsertSchemaField; - init => base.Field = value; + get => (UpsertSchemaField)Field; } public ArrayFieldBuilder(UpsertSchemaField field, CreateSchema schema) @@ -100,11 +99,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders } }; - field.Nested ??= Array.Empty(); - field.Nested = field.Nested.Union(new[] { nestedField }).ToArray(); + TypedField.Nested ??= Array.Empty(); + TypedField.Nested = TypedField.Nested.Union(new[] { nestedField }).ToArray(); return nestedField; } - } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs index 899fdaf8d..f0b5615bb 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs @@ -31,14 +31,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { Field.Properties = Field.Properties with { Label = label }; - return this as T; + return (T)(object)this; } public T Hints(string? hints) { Field.Properties = Field.Properties with { Hints = hints }; - return this as T; + return (T)(object)this; } public T Localizable() @@ -48,26 +48,26 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders localizableField.Partitioning = Partitioning.Language.Key; } - return this as T; + return (T)(object)this; } public T Disabled() { Field.IsDisabled = true; - return this as T; + return (T)(object)this; } public T Required() { Field.Properties = Field.Properties with { IsRequired = true }; - return this as T; + return (T)(object)this; } - protected void Properties(Func updater) where T : FieldProperties + protected void Properties(Func updater) where TProperties : FieldProperties { - Field.Properties = updater((T)Field.Properties); + Field.Properties = updater((TProperties)Field.Properties); } public T ShowInList() @@ -75,7 +75,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders Schema.FieldsInLists ??= new FieldNames(); Schema.FieldsInLists.Add(Field.Name); - return this as T; + return (T)(object)this; } public T ShowInReferences() @@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders Schema.FieldsInReferences ??= new FieldNames(); Schema.FieldsInReferences.Add(Field.Name); - return this as T; + return (T)(object)this; } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlog.cs similarity index 72% rename from backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs rename to backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlog.cs index dc457afb7..19f21afb8 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlog.cs @@ -5,51 +5,26 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Threading.Tasks; using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Templates.Builders; using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Infrastructure; -using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities.Apps.Templates { - public sealed class CreateBlogCommandMiddleware : ICommandMiddleware + public sealed class CreateBlog : ITemplate { - private const string TemplateName = "Blog"; + public string Name { get; } = "blog"; - public async Task HandleAsync(CommandContext context, NextDelegate next) + public Task RunAsync(PublishTemplate publish) { - if (context.IsCompleted && context.Command is CreateApp createApp && IsRightTemplate(createApp)) - { - var appId = NamedId.Of(createApp.AppId, createApp.Name); - - var publish = new Func(command => - { - if (command is IAppCommand appCommand) - { - appCommand.AppId = appId; - } - - return context.CommandBus.PublishAsync(command); - }); - - await Task.WhenAll( - CreatePagesAsync(publish), - CreatePostsAsync(publish)); - } - - await next(context); - } - - private static bool IsRightTemplate(CreateApp createApp) - { - return string.Equals(createApp.Template, TemplateName, StringComparison.OrdinalIgnoreCase); + return Task.WhenAll( + CreatePagesAsync(publish), + CreatePostsAsync(publish)); } - private static async Task CreatePostsAsync(Func publish) + private static async Task CreatePostsAsync(PublishTemplate publish) { var postsId = await CreatePostsSchemaAsync(publish); @@ -68,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates }); } - private static async Task CreatePagesAsync(Func publish) + private static async Task CreatePagesAsync(PublishTemplate publish) { var pagesId = await CreatePagesSchemaAsync(publish); @@ -87,7 +62,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates }); } - private static async Task> CreatePostsSchemaAsync(Func publish) + private static async Task> CreatePostsSchemaAsync(PublishTemplate publish) { var schema = SchemaBuilder.Create("Posts") @@ -112,7 +87,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates return NamedId.Of(schema.SchemaId, schema.Name); } - private static async Task> CreatePagesSchemaAsync(Func publish) + private static async Task> CreatePagesSchemaAsync(PublishTemplate publish) { var schema = SchemaBuilder.Create("Pages") @@ -137,4 +112,4 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates return NamedId.Of(schema.SchemaId, schema.Name); } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs deleted file mode 100644 index 97d30a927..000000000 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs +++ /dev/null @@ -1,281 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Threading.Tasks; -using Squidex.Domain.Apps.Entities.Apps.Commands; -using Squidex.Domain.Apps.Entities.Apps.Templates.Builders; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Commands; - -namespace Squidex.Domain.Apps.Entities.Apps.Templates -{ - public sealed class CreateIdentityCommandMiddleware : ICommandMiddleware - { - private const string TemplateName = "Identity"; - - public async Task HandleAsync(CommandContext context, NextDelegate next) - { - if (context.IsCompleted && context.Command is CreateApp createApp && IsRightTemplate(createApp)) - { - var appId = NamedId.Of(createApp.AppId, createApp.Name); - - var publish = new Func(command => - { - if (command is IAppCommand appCommand) - { - appCommand.AppId = appId; - } - - return context.CommandBus.PublishAsync(command); - }); - - await Task.WhenAll( - CreateApiResourcesSchemaAsync(publish), - CreateAuthenticationSchemeSchemaAsync(publish), - CreateClientsSchemaAsync(publish), - CreateIdentityResourcesSchemaAsync(publish), - CreateSettingsSchemaAsync(publish), - CreateUsersSchemaAsync(publish)); - } - - await next(context); - } - - private static bool IsRightTemplate(CreateApp createApp) - { - return string.Equals(createApp.Template, TemplateName, StringComparison.OrdinalIgnoreCase); - } - - private static async Task> CreateAuthenticationSchemeSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Authentication Schemes") - .AddString("Provider", f => f - .AsDropDown("Facebook", "Google", "Microsoft", "Twitter") - .Required() - .ShowInList() - .Hints("The name and type of the provider.")) - .AddString("Client Id", f => f - .Required() - .ShowInList() - .Hints("The client id that you must configure at the external provider.")) - .AddString("Client Secret", f => f - .Required() - .Hints("The client secret that you must configure at the external provider.")) - .AddTags("Scopes", f => f - .Hints("Additional scopes you want from the provider.")) - .Build(); - - await publish(schema); - - return NamedId.Of(schema.SchemaId, schema.Name); - } - - private static Task CreateClientsSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Clients") - .AddString("Client Id", f => f - .Required() - .Hints("Unique id of the client.")) - .AddString("Client Name", f => f - .Localizable() - .Hints("Client display name (used for logging and consent screen).")) - .AddString("Client Uri", f => f - .Localizable() - .Hints("URI to further information about client (used on consent screen).")) - .AddAssets("Logo", f => f - .MustBeImage() - .Hints("URI to client logo (used on consent screen).")) - .AddTags("Client Secrets", f => f - .Hints("Client secrets - only relevant for flows that require a secret.")) - .AddTags("Allowed Scopes", f => f - .Hints("Specifies the api scopes that the client is allowed to request.")) - .AddTags("Allowed Grant Types", f => f - .Hints("Specifies the allowed grant types (legal combinations of AuthorizationCode, Implicit, Hybrid, ResourceOwner, ClientCredentials).")) - .AddTags("Redirect Uris", f => f - .Hints("Specifies allowed URIs to return tokens or authorization codes to")) - .AddTags("Post Logout Redirect Uris", f => f - .Hints("Specifies allowed URIs to redirect to after logout.")) - .AddTags("Allowed Cors Origins", f => f - .Hints("Gets or sets the allowed CORS origins for JavaScript clients.")) - .AddBoolean("Require Consent", f => f - .AsToggle() - .Hints("Specifies whether a consent screen is required.")) - .AddBoolean("Allow Offline Access", f => f - .AsToggle() - .Hints("Gets or sets a value indicating whether to allow offline access.")) - .Build(); - - return publish(schema); - } - - private static Task CreateSettingsSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Settings").Singleton() - .AddString("Site Name", f => f - .Localizable() - .Hints("The name of your website.")) - .AddAssets("Logo", f => f - .MustBeImage() - .Hints("Logo that is rendered in the header.")) - .AddString("Footer Text", f => f - .Localizable() - .Hints("The optional footer text.")) - .AddString("PrivacyPolicyUrl", f => f - .Localizable() - .Hints("The link to your privacy policies.")) - .AddString("LegalUrl", f => f - .Localizable() - .Hints("The link to your legal information.")) - .AddString("Email Confirmation Text", f => f - .AsTextArea() - .Localizable() - .Hints("The text for the confirmation email.")) - .AddString("Email Confirmation Subject", f => f - .AsTextArea() - .Localizable() - .Hints("The subject for the confirmation email.")) - .AddString("Email Password Reset Text", f => f - .AsTextArea() - .Localizable() - .Hints("The text for the password reset email.")) - .AddString("Email Password Reset Subject", f => f - .AsTextArea() - .Localizable() - .Hints("The subject for the password reset email.")) - .AddString("Terms of Service Url", f => f - .Localizable() - .Hints("The link to your tems of service.")) - .AddString("Bootstrap Url", f => f - .Hints("The link to a custom bootstrap theme.")) - .AddString("Styles Url", f => f - .Hints("The link to a stylesheet.")) - .AddString("SMTP From", f => f - .Hints("The SMTP sender address.")) - .AddString("SMTP Server", f => f - .Hints("The smpt server.")) - .AddString("SMTP Username", f => f - .Hints("The username for your SMTP server.")) - .AddString("SMTP Password", f => f - .Hints("The password for your SMTP server.")) - .AddString("Google Analytics Id", f => f - .Hints("The id to your google analytics account.")) - .Build(); - - return publish(schema); - } - - private static async Task CreateUsersSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Users") - .AddString("Username", f => f - .Required() - .ShowInList() - .Hints("The unique username to login.")) - .AddString("Email", f => f - .Pattern(@"^[a-zA-Z0-9.!#$%&’*+\\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)*$", "Must be an email address.") - .Required() - .ShowInList() - .Hints("The unique email to login.")) - .AddString("Phone Number", f => f - .Hints("Phone number of the user.")) - .AddTags("Roles", f => f - .Hints("The roles of the user.")) - .AddJson("Claims", f => f - .Hints("The claims of the user.")) - .AddBoolean("Email Confirmed", f => f - .AsToggle() - .Hints("Indicates if the email is confirmed.")) - .AddBoolean("Phone Number Confirmed", f => f - .AsToggle() - .Hints("Indicates if the phone number is confirmed.")) - .AddBoolean("LockoutEnabled", f => f - .AsToggle() - .Hints("Toggle on to lock out the user.")) - .AddDateTime("Lockout End Date Utc", f => f - .AsDateTime() - .Disabled() - .Hints("Indicates when the lockout ends.")) - .AddTags("Login Keys", f => f - .Disabled() - .Hints("Login information for querying.")) - .AddJson("Logins", f => f - .Disabled() - .Hints("Login information.")) - .AddJson("Tokens", f => f - .Disabled() - .Hints("Login tokens.")) - .AddNumber("Access Failed Count", f => f - .Disabled() - .Hints("The number of failed login attempts.")) - .AddString("Password Hash", f => f - .Disabled() - .Hints("The hashed password.")) - .AddString("Normalized Email", f => f - .Disabled() - .Hints("The normalized email for querying.")) - .AddString("Normalized Username", f => f - .Disabled() - .Hints("The normalized user name for querying.")) - .AddString("Security Stamp", f => f - .Disabled() - .Hints("Internal security stamp")) - .WithScripts(DefaultScripts.GenerateUsername) - .Build(); - - await publish(schema); - } - - private static Task CreateApiResourcesSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("API Resources") - .AddString("Name", f => f - .Required() - .ShowInList() - .Hints("The unique name of the API.")) - .AddString("Display Name", f => f - .Localizable() - .Hints("The display name of the API.")) - .AddString("Description", f => f - .Localizable() - .Hints("The description name of the API.")) - .AddTags("User Claims", f => f - .Hints("List of accociated user claims that should be included when this resource is requested.")) - .Build(); - - return publish(schema); - } - - private static Task CreateIdentityResourcesSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Identity Resources") - .AddString("Name", f => f - .Required() - .ShowInList() - .Hints("The unique name of the identity information.")) - .AddString("Display Name", f => f - .Localizable() - .Hints("The display name of the identity information.")) - .AddString("Description", f => f - .Localizable() - .Hints("The description name of the identity information.")) - .AddTags("User Claims", f => f - .Hints("List of accociated user claims that should be included when this resource is requested.")) - .AddBoolean("Required", f => f - .Hints("Specifies whether the user can de-select the scope on the consent screen.")) - .Build(); - - return publish(schema); - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityV2CommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityV2CommandMiddleware.cs deleted file mode 100644 index 5cc6f3a9d..000000000 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityV2CommandMiddleware.cs +++ /dev/null @@ -1,332 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Threading.Tasks; -using Squidex.Domain.Apps.Entities.Apps.Commands; -using Squidex.Domain.Apps.Entities.Apps.Templates.Builders; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Commands; - -namespace Squidex.Domain.Apps.Entities.Apps.Templates -{ - public sealed class CreateIdentityV2CommandMiddleware : ICommandMiddleware - { - private const string TemplateName = "IdentityV2"; - - public async Task HandleAsync(CommandContext context, NextDelegate next) - { - if (context.IsCompleted && context.Command is CreateApp createApp && IsRightTemplate(createApp)) - { - var appId = NamedId.Of(createApp.AppId, createApp.Name); - - var publish = new Func>(command => - { - if (command is IAppCommand appCommand) - { - appCommand.AppId = appId; - } - - return context.CommandBus.PublishAsync(command); - }); - - var apiScopeId = await CreateApiScopesSchemaAsync(publish); - - await Task.WhenAll( - CreateApiResourcesSchemaAsync(publish, apiScopeId), - CreateAuthenticationSchemeSchemaAsync(publish), - CreateClientsSchemaAsync(publish), - CreateIdentityResourcesSchemaAsync(publish), - CreateSettingsSchemaAsync(publish), - CreateUsersSchemaAsync(publish)); - } - - await next(context); - } - - private static bool IsRightTemplate(CreateApp createApp) - { - return string.Equals(createApp.Template, TemplateName, StringComparison.OrdinalIgnoreCase); - } - - private static Task CreateAuthenticationSchemeSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Authentication Schemes") - .AddString("Provider", f => f - .AsDropDown("Facebook", "Google", "Microsoft", "Twitter") - .Unique() - .Required() - .ShowInList() - .Hints("The name and type of the provider.")) - .AddString("Client Id", f => f - .Required() - .ShowInList() - .Hints("The client id that you must configure at the external provider.")) - .AddString("Client Secret", f => f - .Required() - .Hints("The client secret that you must configure at the external provider.")) - .AddTags("Scopes", f => f - .Hints("Additional scopes you want from the provider.")) - .Build(); - - return publish(schema); - } - - private static Task CreateClientsSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Clients") - .AddString("Client Id", f => f - .Unique() - .Required() - .Hints("Unique id of the client.")) - .AddString("Client Name", f => f - .Localizable() - .Hints("Client display name (used for logging and consent screen).")) - .AddString("Client Uri", f => f - .Localizable() - .Hints("URI to further information about client (used on consent screen).")) - .AddAssets("Logo", f => f - .MustBeImage() - .Hints("URI to client logo (used on consent screen).")) - .AddBoolean("Require Consent", f => f - .AsToggle() - .Hints("Specifies whether a consent screen is required.")) - .AddBoolean("Disabled", f => f - .AsToggle() - .Hints("Enable or disable the client.")) - .AddBoolean("Allow Offline Access", f => f - .AsToggle() - .Hints("Gets or sets a value indicating whether to allow offline access.")) - .AddTags("Allowed Grant Types", f => f - .WithAllowedValues("implicit", "hybrid", "authorization_code", "client_credentials") - .Hints("Specifies the allowed grant types.")) - .AddTags("Client Secrets", f => f - .Hints("Client secrets - only relevant for flows that require a secret.")) - .AddTags("Allowed Scopes", f => f - .Hints("Specifies the api scopes that the client is allowed to request.")) - .AddTags("Redirect Uris", f => f - .Hints("Specifies allowed URIs to return tokens or authorization codes to")) - .AddTags("Post Logout Redirect Uris", f => f - .Hints("Specifies allowed URIs to redirect to after logout.")) - .AddTags("Allowed Cors Origins", f => f - .Hints("Gets or sets the allowed CORS origins for JavaScript clients.")) - .Build(); - - return publish(schema); - } - - private static Task CreateSettingsSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Settings").Singleton() - .AddString("Site Name", f => f - .Localizable() - .Hints("The name of your website.")) - .AddAssets("Logo", f => f - .MustBeImage() - .Hints("Logo that is rendered in the header.")) - .AddString("Footer Text", f => f - .Localizable() - .Hints("The optional footer text.")) - .AddString("PrivacyPolicyUrl", f => f - .Localizable() - .Hints("The link to your privacy policies.")) - .AddString("LegalUrl", f => f - .Localizable() - .Hints("The link to your legal information.")) - .AddString("Email Confirmation Text", f => f - .AsTextArea() - .Localizable() - .Hints("The text for the confirmation email.")) - .AddString("Email Confirmation Subject", f => f - .AsTextArea() - .Localizable() - .Hints("The subject for the confirmation email.")) - .AddString("Email Password Reset Text", f => f - .AsTextArea() - .Localizable() - .Hints("The text for the password reset email.")) - .AddString("Email Password Reset Subject", f => f - .AsTextArea() - .Localizable() - .Hints("The subject for the password reset email.")) - .AddString("Terms of Service Url", f => f - .Localizable() - .Hints("The link to your tems of service.")) - .AddString("Bootstrap Url", f => f - .Hints("The link to a custom bootstrap theme.")) - .AddString("Styles Url", f => f - .Hints("The link to a stylesheet.")) - .AddString("SMTP From", f => f - .Hints("The SMTP sender address.")) - .AddString("SMTP Server", f => f - .Hints("The smpt server.")) - .AddString("SMTP Username", f => f - .Hints("The username for your SMTP server.")) - .AddString("SMTP Password", f => f - .Hints("The password for your SMTP server.")) - .AddString("Google Analytics Id", f => f - .Hints("The id to your google analytics account.")) - .Build(); - - return publish(schema); - } - - private static async Task CreateUsersSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Users") - .AddString("Username", f => f - .Required() - .ShowInList() - .Hints("The unique username to login.")) - .AddString("Email", f => f - .Pattern(@"^[a-zA-Z0-9.!#$%&’*+\\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)*$", "Must be an email address.") - .Required() - .ShowInList() - .Hints("The unique email to login.")) - .AddString("Phone Number", f => f - .Hints("Phone number of the user.")) - .AddTags("Roles", f => f - .Hints("The roles of the user.")) - .AddJson("Claims", f => f - .Hints("The claims of the user.")) - .AddBoolean("Email Confirmed", f => f - .AsToggle() - .Hints("Indicates if the email is confirmed.")) - .AddBoolean("Phone Number Confirmed", f => f - .AsToggle() - .Hints("Indicates if the phone number is confirmed.")) - .AddBoolean("LockoutEnabled", f => f - .AsToggle() - .Hints("Toggle on to lock out the user.")) - .AddDateTime("Lockout End Date Utc", f => f - .AsDateTime() - .Disabled() - .Hints("Indicates when the lockout ends.")) - .AddTags("Login Keys", f => f - .Disabled() - .Hints("Login information for querying.")) - .AddJson("Logins", f => f - .Disabled() - .Hints("Login information.")) - .AddJson("Tokens", f => f - .Disabled() - .Hints("Login tokens.")) - .AddNumber("Access Failed Count", f => f - .Disabled() - .Hints("The number of failed login attempts.")) - .AddString("Password Hash", f => f - .Disabled() - .Hints("The hashed password.")) - .AddString("Normalized Email", f => f - .Disabled() - .Hints("The normalized email for querying.")) - .AddString("Normalized Username", f => f - .Disabled() - .Hints("The normalized user name for querying.")) - .AddString("Security Stamp", f => f - .Disabled() - .Hints("Internal security stamp")) - .WithScripts(DefaultScripts.GenerateUsername) - .Build(); - - await publish(schema); - } - - private static async Task> CreateApiScopesSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("API Scopes") - .AddString("Name", f => f - .Unique() - .Required() - .ShowInList() - .Hints("The unique name of the API scope.")) - .AddString("Display Name", f => f - .Localizable() - .Hints("The display name of the API scope.")) - .AddString("Description", f => f - .Localizable() - .Hints("The description name of the API scope.")) - .AddBoolean("Disabled", f => f - .AsToggle() - .Hints("Enable or disable the scope.")) - .AddBoolean("Emphasize", f => f - .AsToggle() - .Hints("Emphasize the API scope for important scopes.")) - .AddTags("User Claims", f => f - .Hints("List of accociated user claims that should be included when this resource is requested.")) - .Build(); - - await publish(schema); - - return NamedId.Of(schema.SchemaId, schema.Name); - } - - private static Task CreateApiResourcesSchemaAsync(Func publish, NamedId scopeId) - { - var schema = - SchemaBuilder.Create("API Resources") - .AddString("Name", f => f - .Unique() - .Required() - .ShowInList() - .Hints("The unique name of the API.")) - .AddString("Display Name", f => f - .Localizable() - .Hints("The display name of the API.")) - .AddString("Description", f => f - .Localizable() - .Hints("The description name of the API.")) - .AddBoolean("Disabled", f => f - .AsToggle() - .Hints("Enable or disable the API.")) - .AddReferences("Scopes", f => f - .WithSchemaId(scopeId.Id) - .Hints("The scopes for this API.")) - .AddTags("User Claims", f => f - .Hints("List of accociated user claims that should be included when this resource is requested.")) - .Build(); - - return publish(schema); - } - - private static Task CreateIdentityResourcesSchemaAsync(Func publish) - { - var schema = - SchemaBuilder.Create("Identity Resources") - .AddString("Name", f => f - .Unique() - .Required() - .ShowInList() - .Hints("The unique name of the identity information.")) - .AddString("Display Name", f => f - .Localizable() - .Hints("The display name of the identity information.")) - .AddString("Description", f => f - .Localizable() - .Hints("The description name of the identity information.")) - .AddBoolean("Required", f => f - .AsToggle() - .Hints("Specifies whether the user can de-select the scope on the consent screen.")) - .AddBoolean("Disabled", f => f - .AsToggle() - .Hints("Enable or disable the scope.")) - .AddBoolean("Emphasize", f => f - .AsToggle() - .Hints("Emphasize the API scope for important scopes.")) - .AddTags("User Claims", f => f - .Hints("List of accociated user claims that should be included when this resource is requested.")) - .Build(); - - return publish(schema); - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfile.cs similarity index 82% rename from backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs rename to backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfile.cs index 307756d75..92cbbc06a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfile.cs @@ -5,55 +5,30 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Threading.Tasks; using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Templates.Builders; using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Infrastructure; -using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities.Apps.Templates { - public sealed class CreateProfileCommandMiddleware : ICommandMiddleware + public sealed class CreateProfile : ITemplate { - private const string TemplateName = "Profile"; + public string Name { get; } = "profile"; - public async Task HandleAsync(CommandContext context, NextDelegate next) + public Task RunAsync(PublishTemplate publish) { - if (context.IsCompleted && context.Command is CreateApp createApp && IsRightTemplate(createApp)) - { - var appId = NamedId.Of(createApp.AppId, createApp.Name); - - var publish = new Func(command => - { - if (command is IAppCommand appCommand) - { - appCommand.AppId = appId; - } - - return context.CommandBus.PublishAsync(command); - }); - - await Task.WhenAll( - CreateBasicsAsync(publish), - CreateEducationSchemaAsync(publish), - CreateExperienceSchemaAsync(publish), - CreateProjectsSchemaAsync(publish), - CreatePublicationsSchemaAsync(publish), - CreateSkillsSchemaAsync(publish)); - } - - await next(context); - } - - private static bool IsRightTemplate(CreateApp createApp) - { - return string.Equals(createApp.Template, TemplateName, StringComparison.OrdinalIgnoreCase); + return Task.WhenAll( + CreateBasicsAsync(publish), + CreateEducationSchemaAsync(publish), + CreateExperienceSchemaAsync(publish), + CreateProjectsSchemaAsync(publish), + CreatePublicationsSchemaAsync(publish), + CreateSkillsSchemaAsync(publish)); } - private static async Task CreateBasicsAsync(Func publish) + private static async Task CreateBasicsAsync(PublishTemplate publish) { var postsId = await CreateBasicsSchemaAsync(publish); @@ -75,7 +50,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates }); } - private static async Task> CreateBasicsSchemaAsync(Func publish) + private static async Task> CreateBasicsSchemaAsync(PublishTemplate publish) { var command = SchemaBuilder.Create("Basics") @@ -118,7 +93,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates return NamedId.Of(command.SchemaId, command.Name); } - private static async Task> CreateProjectsSchemaAsync(Func publish) + private static async Task> CreateProjectsSchemaAsync(PublishTemplate publish) { var schema = SchemaBuilder.Create("Projects") @@ -148,7 +123,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates return NamedId.Of(schema.SchemaId, schema.Name); } - private static async Task> CreateExperienceSchemaAsync(Func publish) + private static async Task> CreateExperienceSchemaAsync(PublishTemplate publish) { var schema = SchemaBuilder.Create("Experience") @@ -175,7 +150,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates return NamedId.Of(schema.SchemaId, schema.Name); } - private static async Task> CreateEducationSchemaAsync(Func publish) + private static async Task> CreateEducationSchemaAsync(PublishTemplate publish) { var schema = SchemaBuilder.Create("Education") @@ -202,7 +177,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates return NamedId.Of(schema.SchemaId, schema.Name); } - private static async Task> CreatePublicationsSchemaAsync(Func publish) + private static async Task> CreatePublicationsSchemaAsync(PublishTemplate publish) { var command = SchemaBuilder.Create("Publications") @@ -224,7 +199,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates return NamedId.Of(command.SchemaId, command.Name); } - private static async Task> CreateSkillsSchemaAsync(Func publish) + private static async Task> CreateSkillsSchemaAsync(PublishTemplate publish) { var command = SchemaBuilder.Create("Skills") diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/DefaultScripts.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/DefaultScripts.cs index 0144dc10f..e4d86e7f2 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/DefaultScripts.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/DefaultScripts.cs @@ -20,29 +20,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates replace(data); }"; - private const string ScriptToGenerateUsername = @" - var data = ctx.data; - - if (data.userName && data.userName.iv) { - data.normalizedUserName = { iv: data.userName.iv.toUpperCase() }; - } - - if (data.email && data.email.iv) { - data.normalizedEmail = { iv: data.email.iv.toUpperCase() }; - } - - replace(data);"; - public static readonly SchemaScripts GenerateSlug = new SchemaScripts { Create = ScriptToGenerateSlug, Update = ScriptToGenerateSlug }; - - public static readonly SchemaScripts GenerateUsername = new SchemaScripts - { - Create = ScriptToGenerateUsername, - Update = ScriptToGenerateUsername - }; } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/ITemplate.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/ITemplate.cs new file mode 100644 index 000000000..da645065f --- /dev/null +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/ITemplate.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; + +namespace Squidex.Domain.Apps.Entities.Apps.Templates +{ + public interface ITemplate + { + string Name { get; } + + Task RunAsync(PublishTemplate publish); + } +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplateCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplateCommandMiddleware.cs new file mode 100644 index 000000000..5799be4cd --- /dev/null +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplateCommandMiddleware.cs @@ -0,0 +1,53 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Squidex.Domain.Apps.Entities.Apps.Commands; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; + +namespace Squidex.Domain.Apps.Entities.Apps.Templates +{ + public delegate Task PublishTemplate(IAppCommand command); + + public sealed class TemplateCommandMiddleware : ICommandMiddleware + { + private readonly Dictionary templates; + + public TemplateCommandMiddleware(IEnumerable templates) + { + Guard.NotNull(templates, nameof(templates)); + + this.templates = templates.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); + } + + public async Task HandleAsync(CommandContext context, NextDelegate next) + { + if (context.IsCompleted && context.Command is CreateApp createApp && !string.IsNullOrWhiteSpace(createApp.Template)) + { + if (templates.TryGetValue(createApp.Template, out var template)) + { + var appId = NamedId.Of(createApp.AppId, createApp.Name); + + var publish = new PublishTemplate(command => + { + command.AppId = appId; + + return context.CommandBus.PublishAsync(command); + }); + + await template.RunAsync(publish); + } + } + + await next(context); + } + } +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs index 52cc6bf77..98363c24f 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Collections.Generic; using System.Linq; using GraphQL.Types; diff --git a/backend/src/Squidex/Config/Domain/CommandsServices.cs b/backend/src/Squidex/Config/Domain/CommandsServices.cs index 0071a78a4..ea42029db 100644 --- a/backend/src/Squidex/Config/Domain/CommandsServices.cs +++ b/backend/src/Squidex/Config/Domain/CommandsServices.cs @@ -1,4 +1,4 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) @@ -103,17 +103,17 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As(); - services.AddSingletonAs() + services.AddSingletonAs() .As(); - services.AddSingletonAs() - .As(); + services.AddSingletonAs() + .As(); - services.AddSingletonAs() - .As(); + services.AddSingletonAs() + .As(); services.AddSingletonAs() .As(); } } -} \ No newline at end of file +} diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Templates/TemplatesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Templates/TemplatesTests.cs index 420034ba0..523d687f9 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Templates/TemplatesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Templates/TemplatesTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using FakeItEasy; using Squidex.Domain.Apps.Entities.Apps.Commands; @@ -22,24 +23,27 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates public static readonly IEnumerable TemplateTests = new[] { - new object[] { new CreateBlogCommandMiddleware(), "blog" }, - new object[] { new CreateIdentityCommandMiddleware(), "identity" }, - new object[] { new CreateProfileCommandMiddleware(), "profile" } + new object[] { new CreateBlog() }, + new object[] { new CreateProfile() } }; [Theory] [MemberData(nameof(TemplateTests))] - public async Task Should_create_schemas(ICommandMiddleware middleware, string template) + public async Task Should_create_schemas(ITemplate template) { - var command = new CreateApp { AppId = DomainId.NewGuid(), Name = "my-app", Template = template }; + var appId = NamedId.Of(DomainId.NewGuid(), "my-app"); + + var command = new CreateApp { AppId = appId.Id, Name = appId.Name, Template = template.Name }; var context = new CommandContext(command, commandBus) .Complete(); - await middleware.HandleAsync(context); + var sut = new TemplateCommandMiddleware(Enumerable.Repeat(template, 1)); + + await sut.HandleAsync(context); - A.CallTo(() => commandBus.PublishAsync(A._)) + A.CallTo(() => commandBus.PublishAsync(A.That.Matches(x => x.AppId == appId))) .MustHaveHappened(); } } diff --git a/frontend/app/features/apps/pages/apps-page.component.html b/frontend/app/features/apps/pages/apps-page.component.html index 5a9fec029..52b35f508 100644 --- a/frontend/app/features/apps/pages/apps-page.component.html +++ b/frontend/app/features/apps/pages/apps-page.component.html @@ -53,40 +53,6 @@ -
-
-
- -
- -

{{ 'apps.createIdentityApp' | sqxTranslate }}

- -
-
{{ 'apps.createIdentityAppDescription' | sqxTranslate }}
- -
-
-
- -
-
-
- -
- -

{{ 'apps.createIdentityAppV2' | sqxTranslate }}

- -
-
{{ 'apps.createIdentityAppV2Description' | sqxTranslate }}
- -
-
-
-