From 824c88bed3089f4434afa5a0a5df4c1b249fa9a9 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 2 Sep 2017 20:53:01 +0200 Subject: [PATCH] JScript editor. --- Squidex.sln.DotSettings | 2 + .../ContentEnricher.cs | 2 +- .../ContentValidator.cs | 2 +- .../Contents/IdContentData.cs | 2 +- .../Contents/NamedContentData.cs | 10 +- .../Schemas/Field_Generic.cs | 4 +- .../Schemas/Schema.cs | 2 +- .../Schemas/Validators/AssetsValidator.cs | 4 +- .../Schemas/Validators/ReferencesValidator.cs | 4 +- .../Contents/Visitors/ConstantVisitor.cs | 4 +- .../Contents/Visitors/PropertyVisitor.cs | 2 +- .../Squidex.Domain.Apps.Read.MongoDb.csproj | 2 +- .../GraphQL/Types/ContentQueryGraphType.cs | 2 + .../Webhooks/WebhookSender.cs | 2 +- .../Apps/AppContributors.cs | 2 +- .../Contents/ContentCommandMiddleware.cs | 16 +-- .../Schemas/SchemaDomainObject.cs | 2 +- src/Squidex.Domain.Users/UserExtensions.cs | 1 + .../CQRS/Events/GetEventStoreSubscription.cs | 2 +- .../RedisSubscription.cs | 2 +- .../CQRS/DomainObjectBase.cs | 2 + .../CollectionExtensions.cs | 4 +- .../DomainObjectException.cs | 2 + .../Json/NamedGuidIdConverter.cs | 2 +- .../Json/NamedLongIdConverter.cs | 2 +- .../Json/PropertiesBagConverter.cs | 5 +- src/Squidex.Infrastructure/Language.cs | 2 +- src/Squidex.Infrastructure/PropertiesBag.cs | 2 +- src/Squidex.Infrastructure/PropertyValue.cs | 6 +- .../Reflection/PropertiesTypeAccessor.cs | 2 +- .../StringExtensions.cs | 2 +- .../TypeNameRegistry.cs | 1 + src/Squidex/Config/Identity/IdentityUsage.cs | 1 + .../Config/Identity/MicrosoftIdentityUsage.cs | 1 - .../Swagger/XmlResponseTypesProcessor.cs | 4 +- .../Api/Schemas/Models/SchemaDetailsDto.cs | 30 +++++ .../ContentApi/ContentsController.cs | 4 +- .../Generator/SchemaSwaggerGenerator.cs | 2 +- .../ContentApi/Models/ContentDto.cs | 22 ++++ .../Pipeline/ApiExceptionFilterAttribute.cs | 1 - .../CommandHandlers/ETagCommandMiddleware.cs | 2 +- src/Squidex/Pipeline/Swagger/SwaggerHelper.cs | 2 +- src/Squidex/Squidex.csproj | 2 +- .../pages/content/content-page.component.ts | 4 +- .../angular/jscript-editor.component.html | 1 + .../angular/jscript-editor.component.scss | 8 ++ .../angular/jscript-editor.component.ts | 108 ++++++++++++++++ src/Squidex/app/framework/declarations.ts | 1 + src/Squidex/app/framework/module.ts | 3 + .../shared/services/schemas.service.spec.ts | 59 ++++++++- .../app/shared/services/schemas.service.ts | 117 ++++++++++++++++-- .../Schemas/SchemaDomainObjectTests.cs | 24 ++-- .../Caching/InvalidatingMemoryCacheTest.cs | 7 +- .../Squidex.Infrastructure.Tests.csproj | 4 +- 54 files changed, 417 insertions(+), 91 deletions(-) create mode 100644 src/Squidex/app/framework/angular/jscript-editor.component.html create mode 100644 src/Squidex/app/framework/angular/jscript-editor.component.scss create mode 100644 src/Squidex/app/framework/angular/jscript-editor.component.ts diff --git a/Squidex.sln.DotSettings b/Squidex.sln.DotSettings index cf928c49d..34ec3dc16 100644 --- a/Squidex.sln.DotSettings +++ b/Squidex.sln.DotSettings @@ -24,8 +24,10 @@ DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW + DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW + DO_NOT_SHOW DO_NOT_SHOW WARNING DO_NOT_SHOW diff --git a/src/Squidex.Domain.Apps.Core/ContentEnricher.cs b/src/Squidex.Domain.Apps.Core/ContentEnricher.cs index 71a8643ac..2d633ad13 100644 --- a/src/Squidex.Domain.Apps.Core/ContentEnricher.cs +++ b/src/Squidex.Domain.Apps.Core/ContentEnricher.cs @@ -64,7 +64,7 @@ namespace Squidex.Domain.Apps.Core var key = partitionItem.Key; - if (!fieldData.TryGetValue(key, out JToken value) || value == null || value.Type == JTokenType.Null) + if (!fieldData.TryGetValue(key, out var value) || value == null || value.Type == JTokenType.Null) { fieldData.AddValue(key, defaultValue); } diff --git a/src/Squidex.Domain.Apps.Core/ContentValidator.cs b/src/Squidex.Domain.Apps.Core/ContentValidator.cs index aa8d87e1c..e1a84501f 100644 --- a/src/Squidex.Domain.Apps.Core/ContentValidator.cs +++ b/src/Squidex.Domain.Apps.Core/ContentValidator.cs @@ -50,7 +50,7 @@ namespace Squidex.Domain.Apps.Core { var fieldName = fieldData.Key; - if (!schema.FieldsByName.TryGetValue(fieldData.Key, out Field field)) + if (!schema.FieldsByName.TryGetValue(fieldData.Key, out var field)) { errors.AddError(" is not a known field", fieldName); } diff --git a/src/Squidex.Domain.Apps.Core/Contents/IdContentData.cs b/src/Squidex.Domain.Apps.Core/Contents/IdContentData.cs index 6e0192ade..1e3cbb89f 100644 --- a/src/Squidex.Domain.Apps.Core/Contents/IdContentData.cs +++ b/src/Squidex.Domain.Apps.Core/Contents/IdContentData.cs @@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Core.Contents foreach (var fieldValue in this) { - if (!schema.FieldsById.TryGetValue(fieldValue.Key, out Field field)) + if (!schema.FieldsById.TryGetValue(fieldValue.Key, out var field)) { continue; } diff --git a/src/Squidex.Domain.Apps.Core/Contents/NamedContentData.cs b/src/Squidex.Domain.Apps.Core/Contents/NamedContentData.cs index cf9dfe856..e0f16d071 100644 --- a/src/Squidex.Domain.Apps.Core/Contents/NamedContentData.cs +++ b/src/Squidex.Domain.Apps.Core/Contents/NamedContentData.cs @@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Core.Contents foreach (var fieldValue in this) { - if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out Field field)) + if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out var field)) { continue; } @@ -100,7 +100,7 @@ namespace Squidex.Domain.Apps.Core.Contents foreach (var fieldValue in this) { - if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out Field field) || (excludeHidden && field.IsHidden)) + if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out var field) || (excludeHidden && field.IsHidden)) { continue; } @@ -114,7 +114,7 @@ namespace Squidex.Domain.Apps.Core.Contents { var languageCode = languageConfig.Key; - if (fieldValues.TryGetValue(languageCode, out JToken value)) + if (fieldValues.TryGetValue(languageCode, out var value)) { fieldResult.Add(languageCode, value); } @@ -126,7 +126,7 @@ namespace Squidex.Domain.Apps.Core.Contents } else { - if (fieldValues.TryGetValue(codeForInvariant, out JToken value)) + if (fieldValues.TryGetValue(codeForInvariant, out var value)) { fieldResult.Add(codeForInvariant, value); } @@ -168,7 +168,7 @@ namespace Squidex.Domain.Apps.Core.Contents foreach (var language in languagePreferences) { - if (fieldValues.TryGetValue(language, out JToken value) && value != null) + if (fieldValues.TryGetValue(language, out var value) && value != null) { result[fieldValue.Key] = value; diff --git a/src/Squidex.Domain.Apps.Core/Schemas/Field_Generic.cs b/src/Squidex.Domain.Apps.Core/Schemas/Field_Generic.cs index 7749529ad..98a2778de 100644 --- a/src/Squidex.Domain.Apps.Core/Schemas/Field_Generic.cs +++ b/src/Squidex.Domain.Apps.Core/Schemas/Field_Generic.cs @@ -46,9 +46,7 @@ namespace Squidex.Domain.Apps.Core.Schemas newProperties.Freeze(); - var typedProperties = newProperties as T; - - if (typedProperties == null) + if (!(newProperties is T typedProperties)) { throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties)); } diff --git a/src/Squidex.Domain.Apps.Core/Schemas/Schema.cs b/src/Squidex.Domain.Apps.Core/Schemas/Schema.cs index d9e9605b5..296457f17 100644 --- a/src/Squidex.Domain.Apps.Core/Schemas/Schema.cs +++ b/src/Squidex.Domain.Apps.Core/Schemas/Schema.cs @@ -179,7 +179,7 @@ namespace Squidex.Domain.Apps.Core.Schemas { Guard.NotNull(updater, nameof(updater)); - if (!fieldsById.TryGetValue(fieldId, out Field field)) + if (!fieldsById.TryGetValue(fieldId, out var field)) { throw new DomainObjectNotFoundException(fieldId.ToString(), "Fields", typeof(Field)); } diff --git a/src/Squidex.Domain.Apps.Core/Schemas/Validators/AssetsValidator.cs b/src/Squidex.Domain.Apps.Core/Schemas/Validators/AssetsValidator.cs index df95acbc9..f724048ec 100644 --- a/src/Squidex.Domain.Apps.Core/Schemas/Validators/AssetsValidator.cs +++ b/src/Squidex.Domain.Apps.Core/Schemas/Validators/AssetsValidator.cs @@ -26,9 +26,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Validators public async Task ValidateAsync(object value, ValidationContext context, Action addError) { - var assets = value as AssetsValue; - - if (assets == null || assets.AssetIds.Count == 0) + if (!(value is AssetsValue assets) || assets.AssetIds.Count == 0) { if (isRequired && !context.IsOptional) { diff --git a/src/Squidex.Domain.Apps.Core/Schemas/Validators/ReferencesValidator.cs b/src/Squidex.Domain.Apps.Core/Schemas/Validators/ReferencesValidator.cs index 1a5bb0d8e..c315c7cd4 100644 --- a/src/Squidex.Domain.Apps.Core/Schemas/Validators/ReferencesValidator.cs +++ b/src/Squidex.Domain.Apps.Core/Schemas/Validators/ReferencesValidator.cs @@ -28,9 +28,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Validators public async Task ValidateAsync(object value, ValidationContext context, Action addError) { - var references = value as ReferencesValue; - - if (references == null || references.ContentIds.Count == 0) + if (!(value is ReferencesValue references) || references.ContentIds.Count == 0) { if (isRequired && !context.IsOptional) { diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/ConstantVisitor.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/ConstantVisitor.cs index 4bb1e8d82..a9b31d806 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/ConstantVisitor.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/ConstantVisitor.cs @@ -45,9 +45,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors { var value = Visit(nodeIn.Source); - if (value is DateTimeOffset) + if (value is DateTimeOffset dateTimeOffset) { - return Instant.FromDateTimeOffset((DateTimeOffset)value); + return Instant.FromDateTimeOffset(dateTimeOffset); } return InstantPattern.General.Parse(Visit(nodeIn.Source).ToString()).Value; diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs index b61cbfe62..69da62ec0 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs @@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors { var edmName = propertyNames[1].UnescapeEdmField(); - if (!schema.FieldsByName.TryGetValue(edmName, out Field field)) + if (!schema.FieldsByName.TryGetValue(edmName, out var field)) { throw new NotSupportedException(); } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj b/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj index 8f024eb56..62570d28e 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryGraphType.cs b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryGraphType.cs index c64196385..17048e873 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryGraphType.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryGraphType.cs @@ -14,6 +14,8 @@ using GraphQL.Types; using Squidex.Domain.Apps.Read.Schemas; using Squidex.Infrastructure; +// ReSharper disable ImpureMethodCallOnReadonlyValueField + namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types { public sealed class ContentQueryGraphType : ObjectGraphType diff --git a/src/Squidex.Domain.Apps.Read/Webhooks/WebhookSender.cs b/src/Squidex.Domain.Apps.Read/Webhooks/WebhookSender.cs index 319285f80..4ec930948 100644 --- a/src/Squidex.Domain.Apps.Read/Webhooks/WebhookSender.cs +++ b/src/Squidex.Domain.Apps.Read/Webhooks/WebhookSender.cs @@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Read.Webhooks { try { - HttpRequestMessage request = BuildRequest(job); + var request = BuildRequest(job); HttpResponseMessage response = null; var responseString = string.Empty; diff --git a/src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs b/src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs index 3e9bbc698..a4a3d90aa 100644 --- a/src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs +++ b/src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs @@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Write.Apps private void ThrowIfFound(string contributorId, PermissionLevel permission, Func message) { - if (contributors.TryGetValue(contributorId, out PermissionLevel currentPermission) && currentPermission == permission) + if (contributors.TryGetValue(contributorId, out var currentPermission) && currentPermission == permission) { var error = new ValidationError("Contributor is already part of the app with same permissions", "ContributorId"); diff --git a/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs b/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs index cbc816575..1d991ce55 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs @@ -68,8 +68,8 @@ namespace Squidex.Domain.Apps.Write.Contents var schemaAndApp = await ResolveSchemaAndAppAsync(command); var scriptContext = CreateScriptContext(content, command, command.Data); - command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.Schema.ScriptCreate, "create content", true); - command.Data.Enrich(schemaAndApp.Schema.Schema, schemaAndApp.App.PartitionResolver); + command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.SchemaEntity.ScriptCreate, "create content", true); + command.Data.Enrich(schemaAndApp.SchemaEntity.Schema, schemaAndApp.AppEntity.PartitionResolver); await ValidateAsync(schemaAndApp, command, () => "Failed to create content", false); @@ -86,7 +86,7 @@ namespace Squidex.Domain.Apps.Write.Contents var schemaAndApp = await ResolveSchemaAndAppAsync(command); var scriptContext = CreateScriptContext(content, command, command.Data); - command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.Schema.ScriptUpdate, "update content", true); + command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.SchemaEntity.ScriptUpdate, "update content", true); await ValidateAsync(schemaAndApp, command, () => "Failed to update content", false); @@ -103,7 +103,7 @@ namespace Squidex.Domain.Apps.Write.Contents var schemaAndApp = await ResolveSchemaAndAppAsync(command); var scriptContext = CreateScriptContext(content, command, command.Data); - command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.Schema.ScriptUpdate, "patch content", true); + command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.SchemaEntity.ScriptUpdate, "patch content", true); await ValidateAsync(schemaAndApp, command, () => "Failed to patch content", true); @@ -120,7 +120,7 @@ namespace Squidex.Domain.Apps.Write.Contents var schemaAndApp = await ResolveSchemaAndAppAsync(command); var scriptContext = CreateScriptContext(content, command); - scriptEngine.Execute(scriptContext, schemaAndApp.Schema.ScriptPublish, "publish content"); + scriptEngine.Execute(scriptContext, schemaAndApp.SchemaEntity.ScriptPublish, "publish content"); content.Publish(command); }); @@ -133,7 +133,7 @@ namespace Squidex.Domain.Apps.Write.Contents var schemaAndApp = await ResolveSchemaAndAppAsync(command); var scriptContext = CreateScriptContext(content, command); - scriptEngine.Execute(scriptContext, schemaAndApp.Schema.ScriptUnpublish, "unpublish content"); + scriptEngine.Execute(scriptContext, schemaAndApp.SchemaEntity.ScriptUnpublish, "unpublish content"); content.Unpublish(command); }); @@ -146,7 +146,7 @@ namespace Squidex.Domain.Apps.Write.Contents var schemaAndApp = await ResolveSchemaAndAppAsync(command); var scriptContext = CreateScriptContext(content, command); - scriptEngine.Execute(scriptContext, schemaAndApp.Schema.ScriptDelete, "delete content"); + scriptEngine.Execute(scriptContext, schemaAndApp.SchemaEntity.ScriptDelete, "delete content"); content.Delete(command); }); @@ -199,7 +199,7 @@ namespace Squidex.Domain.Apps.Write.Contents return new ScriptContext { ContentId = content.Id, Data = data, OldData = content.Data, User = ScriptUser.Create(command.Principal) }; } - private async Task<(ISchemaEntity Schema, IAppEntity App)> ResolveSchemaAndAppAsync(SchemaCommand command) + private async Task<(ISchemaEntity SchemaEntity, IAppEntity AppEntity)> ResolveSchemaAndAppAsync(SchemaCommand command) { var taskForApp = appProvider.FindAppByIdAsync(command.AppId.Id); var taskForSchema = schemas.FindSchemaByIdAsync(command.SchemaId.Id); diff --git a/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs b/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs index 884394840..163d07413 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs @@ -300,7 +300,7 @@ namespace Squidex.Domain.Apps.Write.Schemas { SimpleMapper.Map(fieldCommand, @event); - if (schema.FieldsById.TryGetValue(fieldCommand.FieldId, out Field field)) + if (schema.FieldsById.TryGetValue(fieldCommand.FieldId, out var field)) { @event.FieldId = new NamedId(field.Id, field.Name); } diff --git a/src/Squidex.Domain.Users/UserExtensions.cs b/src/Squidex.Domain.Users/UserExtensions.cs index 3685cfe62..5abd04dea 100644 --- a/src/Squidex.Domain.Users/UserExtensions.cs +++ b/src/Squidex.Domain.Users/UserExtensions.cs @@ -12,6 +12,7 @@ using Squidex.Infrastructure; using Squidex.Shared.Identity; using Squidex.Shared.Users; +// ReSharper disable PossibleNullReferenceException // ReSharper disable InvertIf namespace Squidex.Domain.Users diff --git a/src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs b/src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs index 1ce470428..db0952db4 100644 --- a/src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs +++ b/src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs @@ -276,7 +276,7 @@ namespace Squidex.Infrastructure.CQRS.Events { var addressParts = projectionHost.Split(':'); - if (addressParts.Length < 2 || !int.TryParse(addressParts[1], out int port)) + if (addressParts.Length < 2 || !int.TryParse(addressParts[1], out var port)) { port = 2113; } diff --git a/src/Squidex.Infrastructure.Redis/RedisSubscription.cs b/src/Squidex.Infrastructure.Redis/RedisSubscription.cs index 26d18526c..957ebdcc6 100644 --- a/src/Squidex.Infrastructure.Redis/RedisSubscription.cs +++ b/src/Squidex.Infrastructure.Redis/RedisSubscription.cs @@ -67,7 +67,7 @@ namespace Squidex.Infrastructure return; } - if (!Guid.TryParse(parts[0], out Guid sender)) + if (!Guid.TryParse(parts[0], out var sender)) { return; } diff --git a/src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs b/src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs index ee39eee27..1219bd4e0 100644 --- a/src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs +++ b/src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs @@ -10,6 +10,8 @@ using System; using System.Collections.Generic; using Squidex.Infrastructure.CQRS.Events; +// ReSharper disable ImpureMethodCallOnReadonlyValueField + namespace Squidex.Infrastructure.CQRS { public abstract class DomainObjectBase : IAggregate, IEquatable diff --git a/src/Squidex.Infrastructure/CollectionExtensions.cs b/src/Squidex.Infrastructure/CollectionExtensions.cs index b26ef92bb..7221d7b34 100644 --- a/src/Squidex.Infrastructure/CollectionExtensions.cs +++ b/src/Squidex.Infrastructure/CollectionExtensions.cs @@ -109,7 +109,7 @@ namespace Squidex.Infrastructure public static TValue GetOrCreate(this IReadOnlyDictionary dictionary, TKey key, Func creator) { - if (!dictionary.TryGetValue(key, out TValue result)) + if (!dictionary.TryGetValue(key, out var result)) { result = creator(key); } @@ -119,7 +119,7 @@ namespace Squidex.Infrastructure public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func creator) { - if (!dictionary.TryGetValue(key, out TValue result)) + if (!dictionary.TryGetValue(key, out var result)) { result = creator(key); diff --git a/src/Squidex.Infrastructure/DomainObjectException.cs b/src/Squidex.Infrastructure/DomainObjectException.cs index bfa481488..e30dd6b62 100644 --- a/src/Squidex.Infrastructure/DomainObjectException.cs +++ b/src/Squidex.Infrastructure/DomainObjectException.cs @@ -8,6 +8,8 @@ using System; +// ReSharper disable SuggestBaseTypeForParameter + namespace Squidex.Infrastructure { public class DomainObjectException : Exception diff --git a/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs b/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs index c6161a3cd..b71bf8951 100644 --- a/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs @@ -35,7 +35,7 @@ namespace Squidex.Infrastructure.Json throw new JsonException("Named id must have more than 2 parts divided by commata"); } - if (!Guid.TryParse(parts[0], out Guid id)) + if (!Guid.TryParse(parts[0], out var id)) { throw new JsonException("Named id must be a valid guid"); } diff --git a/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs b/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs index 1bf9d7ead..2eeb6f690 100644 --- a/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs @@ -35,7 +35,7 @@ namespace Squidex.Infrastructure.Json throw new JsonException("Named id must have more than 2 parts divided by commata"); } - if (!long.TryParse(parts[0], out long id)) + if (!long.TryParse(parts[0], out var id)) { throw new JsonException("Named id must be a valid long"); } diff --git a/src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs b/src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs index bac8391ab..f75bf6578 100644 --- a/src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs +++ b/src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs @@ -7,7 +7,6 @@ // ========================================================================== using System; -using System.Reflection; using Newtonsoft.Json; using NodaTime; using NodaTime.Extensions; @@ -38,9 +37,9 @@ namespace Squidex.Infrastructure.Json var value = reader.Value; - if (value is DateTime) + if (value is DateTime dateTime) { - properties.Set(key, ((DateTime)value).ToInstant()); + properties.Set(key, dateTime.ToInstant()); } else { diff --git a/src/Squidex.Infrastructure/Language.cs b/src/Squidex.Infrastructure/Language.cs index 8a8e165b8..962e98137 100644 --- a/src/Squidex.Infrastructure/Language.cs +++ b/src/Squidex.Infrastructure/Language.cs @@ -111,7 +111,7 @@ namespace Squidex.Infrastructure input = match.Groups[0].Value; } - if (TryGetLanguage(input.ToLowerInvariant(), out Language result)) + if (TryGetLanguage(input.ToLowerInvariant(), out var result)) { return result; } diff --git a/src/Squidex.Infrastructure/PropertiesBag.cs b/src/Squidex.Infrastructure/PropertiesBag.cs index 90167f6b4..cb8e62fd2 100644 --- a/src/Squidex.Infrastructure/PropertiesBag.cs +++ b/src/Squidex.Infrastructure/PropertiesBag.cs @@ -99,7 +99,7 @@ namespace Squidex.Infrastructure throw new ArgumentException($"The property names '{newPropertyName}' are equal.", newPropertyName); } - if (!internalDictionary.TryGetValue(oldPropertyName, out PropertyValue property)) + if (!internalDictionary.TryGetValue(oldPropertyName, out var property)) { return false; } diff --git a/src/Squidex.Infrastructure/PropertyValue.cs b/src/Squidex.Infrastructure/PropertyValue.cs index 0030015f9..d68184bc1 100644 --- a/src/Squidex.Infrastructure/PropertyValue.cs +++ b/src/Squidex.Infrastructure/PropertyValue.cs @@ -58,7 +58,7 @@ namespace Squidex.Infrastructure { result = null; - if (!Parsers.TryGetValue(binder.Type, out Func parser)) + if (!Parsers.TryGetValue(binder.Type, out var parser)) { return false; } @@ -145,12 +145,12 @@ namespace Squidex.Infrastructure private T? ToNullableOrParseValue(IFormatProvider culture, Func parser) where T : struct { - return TryParse(culture, parser, out T result) ? result : (T?)null; + return TryParse(culture, parser, out var result) ? result : (T?)null; } private T ToOrParseValue(IFormatProvider culture, Func parser) { - return TryParse(culture, parser, out T result) ? result : default(T); + return TryParse(culture, parser, out var result) ? result : default(T); } private bool TryParse(IFormatProvider culture, Func parser, out T result) diff --git a/src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs b/src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs index 303b0b529..7f93c16fb 100644 --- a/src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs +++ b/src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs @@ -68,7 +68,7 @@ namespace Squidex.Infrastructure.Reflection { Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); - if (!accessors.TryGetValue(propertyName, out IPropertyAccessor accessor)) + if (!accessors.TryGetValue(propertyName, out var accessor)) { throw new ArgumentException("Property does not exist.", nameof(propertyName)); } diff --git a/src/Squidex.Infrastructure/StringExtensions.cs b/src/Squidex.Infrastructure/StringExtensions.cs index 93a56333e..293f9f09f 100644 --- a/src/Squidex.Infrastructure/StringExtensions.cs +++ b/src/Squidex.Infrastructure/StringExtensions.cs @@ -356,7 +356,7 @@ namespace Squidex.Infrastructure var lower = char.ToLowerInvariant(character); - if (LowerCaseDiacritics.TryGetValue(character, out string replacement)) + if (LowerCaseDiacritics.TryGetValue(character, out var replacement)) { if (singleCharDiactric) { diff --git a/src/Squidex.Infrastructure/TypeNameRegistry.cs b/src/Squidex.Infrastructure/TypeNameRegistry.cs index b08259a04..fcbec6d90 100644 --- a/src/Squidex.Infrastructure/TypeNameRegistry.cs +++ b/src/Squidex.Infrastructure/TypeNameRegistry.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Reflection; +// ReSharper disable PossibleNullReferenceException // ReSharper disable InvertIf namespace Squidex.Infrastructure diff --git a/src/Squidex/Config/Identity/IdentityUsage.cs b/src/Squidex/Config/Identity/IdentityUsage.cs index f08cbb6f4..e1efcd336 100644 --- a/src/Squidex/Config/Identity/IdentityUsage.cs +++ b/src/Squidex/Config/Identity/IdentityUsage.cs @@ -19,6 +19,7 @@ using Squidex.Infrastructure.Log; using Squidex.Shared.Identity; using Squidex.Shared.Users; +// ReSharper disable ConvertIfStatementToConditionalTernaryExpression // ReSharper disable InvertIf namespace Squidex.Config.Identity diff --git a/src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs b/src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs index 60690d3f7..40088d10b 100644 --- a/src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs +++ b/src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs @@ -6,7 +6,6 @@ // All rights reserved. // ========================================================================== -using Microsoft.AspNetCore.Authentication.MicrosoftAccount; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; diff --git a/src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs b/src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs index aa36c8604..79b89c719 100644 --- a/src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs +++ b/src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs @@ -35,7 +35,7 @@ namespace Squidex.Config.Swagger { var statusCode = match.Groups["Code"].Value; - if (!operation.Responses.TryGetValue(statusCode, out SwaggerResponse response)) + if (!operation.Responses.TryGetValue(statusCode, out var response)) { response = new SwaggerResponse(); @@ -72,7 +72,7 @@ namespace Squidex.Config.Swagger private static void RemoveOkResponse(SwaggerOperation operation) { - if (operation.Responses.TryGetValue("200", out SwaggerResponse response) && + if (operation.Responses.TryGetValue("200", out var response) && response.Description != null && response.Description.Contains("=>")) { diff --git a/src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs b/src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs index 56d3a968b..11b5beef7 100644 --- a/src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs +++ b/src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs @@ -33,6 +33,36 @@ namespace Squidex.Controllers.Api.Schemas.Models /// public bool IsPublished { get; set; } + /// + /// The script that is executed for each query when querying contents. + /// + public string ScriptQuery { get; set; } + + /// + /// The script that is executed when creating a content. + /// + public string ScriptCreate { get; set; } + + /// + /// The script that is executed when updating a content. + /// + public string ScriptUpdate { get; set; } + + /// + /// The script that is executed when deleting a content. + /// + public string ScriptDelete { get; set; } + + /// + /// The script that is executed when publishing a content. + /// + public string ScriptPublish { get; set; } + + /// + /// The script that is executed when unpublishing a content. + /// + public string ScriptUnpublish { get; set; } + /// /// The list of fields. /// diff --git a/src/Squidex/Controllers/ContentApi/ContentsController.cs b/src/Squidex/Controllers/ContentApi/ContentsController.cs index 50399d900..6d5433432 100644 --- a/src/Squidex/Controllers/ContentApi/ContentsController.cs +++ b/src/Squidex/Controllers/ContentApi/ContentsController.cs @@ -44,7 +44,7 @@ namespace Squidex.Controllers.ContentApi public ContentsController( ICommandBus commandBus, - ISchemaProvider schemas, + ISchemaProvider schemas, IScriptEngine scriptEngine, IContentRepository contentRepository, IGraphQLService graphQL) @@ -196,7 +196,7 @@ namespace Squidex.Controllers.ContentApi var context = await CommandBus.PublishAsync(command); var result = context.Result>(); - var response = result.IdOrValue; + var response = ContentDto.Create(command, result); return CreatedAtAction(nameof(GetContent), new { id = command.ContentId }, response); } diff --git a/src/Squidex/Controllers/ContentApi/Generator/SchemaSwaggerGenerator.cs b/src/Squidex/Controllers/ContentApi/Generator/SchemaSwaggerGenerator.cs index 096eb1e15..030118f70 100644 --- a/src/Squidex/Controllers/ContentApi/Generator/SchemaSwaggerGenerator.cs +++ b/src/Squidex/Controllers/ContentApi/Generator/SchemaSwaggerGenerator.cs @@ -150,7 +150,7 @@ namespace Squidex.Controllers.ContentApi.Generator operation.AddBodyParameter("data", dataSchema, SchemaBodyDescription); operation.AddQueryParameter("publish", JsonObjectType.Boolean, "Set to true to autopublish content."); - operation.AddResponse("201", $"{schemaName} created.", dataSchema); + operation.AddResponse("201", $"{schemaName} created.", contentSchema); operation.Security = EditorSecurity; }); diff --git a/src/Squidex/Controllers/ContentApi/Models/ContentDto.cs b/src/Squidex/Controllers/ContentApi/Models/ContentDto.cs index 182b1e5cc..3457d10e8 100644 --- a/src/Squidex/Controllers/ContentApi/Models/ContentDto.cs +++ b/src/Squidex/Controllers/ContentApi/Models/ContentDto.cs @@ -9,7 +9,10 @@ using System; using System.ComponentModel.DataAnnotations; using NodaTime; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Write.Contents.Commands; using Squidex.Infrastructure; +using Squidex.Infrastructure.CQRS.Commands; namespace Squidex.Controllers.ContentApi.Models { @@ -57,5 +60,24 @@ namespace Squidex.Controllers.ContentApi.Models /// The version of the content. /// public long Version { get; set; } + + public static ContentDto Create(CreateContent command, EntityCreatedResult result) + { + var now = SystemClock.Instance.GetCurrentInstant(); + + var response = new ContentDto + { + Id = command.ContentId, + Data = result.IdOrValue, + Version = result.Version, + Created = now, + CreatedBy = command.Actor, + LastModified = now, + LastModifiedBy = command.Actor, + IsPublished = command.Publish + }; + + return response; + } } } diff --git a/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs b/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs index c04254de8..cee8ef4f4 100644 --- a/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs +++ b/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs @@ -9,7 +9,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Security; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Squidex.Controllers.Api; diff --git a/src/Squidex/Pipeline/CommandHandlers/ETagCommandMiddleware.cs b/src/Squidex/Pipeline/CommandHandlers/ETagCommandMiddleware.cs index 1d48310c3..2bd4f6639 100644 --- a/src/Squidex/Pipeline/CommandHandlers/ETagCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandHandlers/ETagCommandMiddleware.cs @@ -29,7 +29,7 @@ namespace Squidex.Pipeline.CommandHandlers var headers = httpContextAccessor.HttpContext.Request.Headers; var headerMatch = headers["If-Match"].ToString(); - if (!string.IsNullOrWhiteSpace(headerMatch) && long.TryParse(headerMatch, NumberStyles.Any, CultureInfo.InvariantCulture, out long expectedVersion)) + if (!string.IsNullOrWhiteSpace(headerMatch) && long.TryParse(headerMatch, NumberStyles.Any, CultureInfo.InvariantCulture, out var expectedVersion)) { context.Command.ExpectedVersion = expectedVersion; } diff --git a/src/Squidex/Pipeline/Swagger/SwaggerHelper.cs b/src/Squidex/Pipeline/Swagger/SwaggerHelper.cs index c6816edf8..bdc218da7 100644 --- a/src/Squidex/Pipeline/Swagger/SwaggerHelper.cs +++ b/src/Squidex/Pipeline/Swagger/SwaggerHelper.cs @@ -97,7 +97,7 @@ namespace Squidex.Pipeline.Swagger { SquidexRoles.AppOwner, "App contributor with Owner permission." }, { SquidexRoles.AppEditor, "Client (writer) or App contributor with Editor permission." }, { SquidexRoles.AppReader, "Client (readonly) or App contributor with Editor permission." }, - { SquidexRoles.AppDeveloper, "App contributor with Developer permission." }, + { SquidexRoles.AppDeveloper, "App contributor with Developer permission." } }, Description = securityDescription }; diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index ac910196a..4553e254d 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -71,7 +71,7 @@ - + diff --git a/src/Squidex/app/features/content/pages/content/content-page.component.ts b/src/Squidex/app/features/content/pages/content/content-page.component.ts index 0bcb87ed5..ff244cbac 100644 --- a/src/Squidex/app/features/content/pages/content/content-page.component.ts +++ b/src/Squidex/app/features/content/pages/content/content-page.component.ts @@ -129,8 +129,8 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone } else { this.appNameOnce() .switchMap(app => this.contentsService.putContent(app, this.schema.name, this.contentId!, requestDto, this.version)) - .subscribe(() => { - this.content = this.content.update(requestDto, this.authService.user.token); + .subscribe(dto => { + this.content = this.content.update(dto, this.authService.user.token); this.emitContentUpdated(this.content); this.notifyInfo('Content saved successfully.'); diff --git a/src/Squidex/app/framework/angular/jscript-editor.component.html b/src/Squidex/app/framework/angular/jscript-editor.component.html new file mode 100644 index 000000000..bb84a4048 --- /dev/null +++ b/src/Squidex/app/framework/angular/jscript-editor.component.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/Squidex/app/framework/angular/jscript-editor.component.scss b/src/Squidex/app/framework/angular/jscript-editor.component.scss new file mode 100644 index 000000000..07b2c640b --- /dev/null +++ b/src/Squidex/app/framework/angular/jscript-editor.component.scss @@ -0,0 +1,8 @@ +@import '_mixins'; +@import '_vars'; + +.editor { + background: $color-dark-foreground; + border: 1px solid $color-input; + height: 30rem; +} \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/jscript-editor.component.ts b/src/Squidex/app/framework/angular/jscript-editor.component.ts new file mode 100644 index 000000000..f151e4e55 --- /dev/null +++ b/src/Squidex/app/framework/angular/jscript-editor.component.ts @@ -0,0 +1,108 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { AfterViewInit, Component, forwardRef, ElementRef, ViewChild } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Subject } from 'rxjs'; + +import { ResourceLoaderService } from './../services/resource-loader.service'; + +declare var ace: any; + +const NOOP = () => { /* NOOP */ }; + +export const SQX_JSCRIPT_EDITOR_CONTROL_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => JscriptEditorComponent), multi: true +}; + +@Component({ + selector: 'sqx-jscript-editor', + styleUrls: ['./jscript-editor.component.scss'], + templateUrl: './jscript-editor.component.html', + providers: [SQX_JSCRIPT_EDITOR_CONTROL_VALUE_ACCESSOR] +}) +export class JscriptEditorComponent implements ControlValueAccessor, AfterViewInit { + private changeCallback: (value: any) => void = NOOP; + private touchedCallback: () => void = NOOP; + private valueChanged = new Subject(); + private aceEditor: any; + private oldValue: string; + private isDisabled = false; + + @ViewChild('editor') + public editor: ElementRef; + + constructor( + private readonly resourceLoader: ResourceLoaderService + ) { + } + + public writeValue(value: any) { + this.oldValue = value; + + if (this.aceEditor) { + this.setValue(value); + } + } + + public setDisabledState(isDisabled: boolean): void { + this.isDisabled = isDisabled; + + if (this.aceEditor) { + this.aceEditor.setReadOnly(isDisabled); + } + } + + public registerOnChange(fn: any) { + this.changeCallback = fn; + } + + public registerOnTouched(fn: any) { + this.touchedCallback = fn; + } + + public ngAfterViewInit() { + this.valueChanged.debounceTime(500) + .subscribe(() => { + this.changeValue(); + }); + + this.resourceLoader.loadScript('https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js').then(() => { + this.aceEditor = ace.edit(this.editor.nativeElement); + + this.aceEditor.getSession().setMode('ace/mode/javascript'); + this.aceEditor.setReadOnly(this.isDisabled); + this.aceEditor.setFontSize(14); + + this.setValue(this.oldValue); + + this.aceEditor.on('blur', () => { + this.changeValue(); + this.touchedCallback(); + }); + + this.aceEditor.on('change', () => { + this.valueChanged.next(); + }); + }); + } + + private changeValue() { + const newValue = this.aceEditor.getValue(); + + if (this.oldValue !== newValue) { + this.changeCallback(newValue); + } + + this.oldValue = newValue; + } + + private setValue(value: any) { + this.aceEditor.setValue(value); + this.aceEditor.clearSelection(); + } +} \ No newline at end of file diff --git a/src/Squidex/app/framework/declarations.ts b/src/Squidex/app/framework/declarations.ts index 70226367b..c17562594 100644 --- a/src/Squidex/app/framework/declarations.ts +++ b/src/Squidex/app/framework/declarations.ts @@ -21,6 +21,7 @@ export * from './angular/geolocation-editor.component'; export * from './angular/http-extensions-impl'; export * from './angular/image-source.directive'; export * from './angular/indeterminate-value.directive'; +export * from './angular/jscript-editor.component'; export * from './angular/json-editor.component'; export * from './angular/lowercase-input.directive'; export * from './angular/markdown-editor.component'; diff --git a/src/Squidex/app/framework/module.ts b/src/Squidex/app/framework/module.ts index a48b0b1b4..3205ba722 100644 --- a/src/Squidex/app/framework/module.ts +++ b/src/Squidex/app/framework/module.ts @@ -33,6 +33,7 @@ import { GeolocationEditorComponent, ImageSourceDirective, IndeterminateValueDirective, + JscriptEditorComponent, JsonEditorComponent, KNumberPipe, LocalCacheService, @@ -96,6 +97,7 @@ import { GeolocationEditorComponent, ImageSourceDirective, IndeterminateValueDirective, + JscriptEditorComponent, JsonEditorComponent, KNumberPipe, LowerCaseInputDirective, @@ -143,6 +145,7 @@ import { GeolocationEditorComponent, ImageSourceDirective, IndeterminateValueDirective, + JscriptEditorComponent, JsonEditorComponent, KNumberPipe, LowerCaseInputDirective, diff --git a/src/Squidex/app/shared/services/schemas.service.spec.ts b/src/Squidex/app/shared/services/schemas.service.spec.ts index d53ab12ff..241489dd6 100644 --- a/src/Squidex/app/shared/services/schemas.service.spec.ts +++ b/src/Squidex/app/shared/services/schemas.service.spec.ts @@ -22,6 +22,7 @@ import { SchemasService, UpdateFieldDto, UpdateSchemaDto, + UpdateSchemaScriptsDto, Version } from './../'; @@ -62,6 +63,31 @@ describe('SchemaDto', () => { expect(schema_2.lastModified).toEqual(now); expect(schema_2.lastModifiedBy).toEqual('me'); }); + + it('should update scripts properties and user info when configure scripts', () => { + const newScripts = + new UpdateSchemaScriptsDto( + '', + '', + '', + '', + '', + ''); + + const now = DateTime.now(); + + const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null, []); + const schema_2 = schema_1.configureScripts(newScripts, 'me', now); + + expect(schema_2.scriptQuery).toEqual(''); + expect(schema_2.scriptCreate).toEqual(''); + expect(schema_2.scriptUpdate).toEqual(''); + expect(schema_2.scriptDelete).toEqual(''); + expect(schema_2.scriptPublish).toEqual(''); + expect(schema_2.scriptUnpublish).toEqual(''); + expect(schema_2.lastModified).toEqual(now); + expect(schema_2.lastModifiedBy).toEqual('me'); + }); }); describe('SchemaDetailsDto', () => { @@ -361,7 +387,13 @@ describe('SchemasService', () => { fieldType: 'References' } } - ] + ], + scriptsQuery: '', + scriptsCreate: '', + scriptsUpdate: '', + scriptsDelete: '', + scriptsPublish: '', + scriptsUnpublish: '' }); expect(schema).toEqual( @@ -378,7 +410,13 @@ describe('SchemasService', () => { new FieldDto(6, 'field6', true, true, true, 'language', createProperties('Geolocation')), new FieldDto(7, 'field7', true, true, true, 'language', createProperties('Assets')), new FieldDto(8, 'field8', true, true, true, 'language', createProperties('References')) - ])); + ], + '', + '', + '', + '', + '', + '')); })); it('should provide entry from cache if not found', @@ -460,7 +498,22 @@ describe('SchemasService', () => { expect(req.request.method).toEqual('PUT'); expect(req.request.headers.get('If-Match')).toBe(version.value); - req.flush({ id: 123 }); + req.flush({}); + })); + + it('should make put request to update schema scripts', + inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { + + const dto = new UpdateSchemaScriptsDto(); + + schemasService.putSchemaScripts('my-app', 'my-schema', dto, version).subscribe(); + + const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/scripts'); + + expect(req.request.method).toEqual('PUT'); + expect(req.request.headers.get('If-Match')).toBe(version.value); + + req.flush({}); })); it('should make put request to update field', diff --git a/src/Squidex/app/shared/services/schemas.service.ts b/src/Squidex/app/shared/services/schemas.service.ts index 88eb82ad9..17ad6c953 100644 --- a/src/Squidex/app/shared/services/schemas.service.ts +++ b/src/Squidex/app/shared/services/schemas.service.ts @@ -121,7 +121,13 @@ export class SchemaDto { export class SchemaDetailsDto extends SchemaDto { constructor(id: string, name: string, properties: SchemaPropertiesDto, isPublished: boolean, createdBy: string, lastModifiedBy: string, created: DateTime, lastModified: DateTime, version: Version, - public readonly fields: FieldDto[] + public readonly fields: FieldDto[], + public readonly scriptQuery?: string, + public readonly scriptCreate?: string, + public readonly scriptUpdate?: string, + public readonly scriptDelete?: string, + public readonly scriptPublish?: string, + public readonly scriptUnpublish?: string ) { super(id, name, properties, isPublished, createdBy, lastModifiedBy, created, lastModified, version); } @@ -135,7 +141,13 @@ export class SchemaDetailsDto extends SchemaDto { this.createdBy, user, this.created, now || DateTime.now(), this.version, - this.fields); + this.fields, + this.scriptQuery, + this.scriptCreate, + this.scriptUpdate, + this.scriptDelete, + this.scriptPublish, + this.scriptUnpublish); } public unpublish(user: string, now?: DateTime): SchemaDetailsDto { @@ -147,7 +159,31 @@ export class SchemaDetailsDto extends SchemaDto { this.createdBy, user, this.created, now || DateTime.now(), this.version, - this.fields); + this.fields, + this.scriptQuery, + this.scriptCreate, + this.scriptUpdate, + this.scriptDelete, + this.scriptPublish, + this.scriptUnpublish); + } + + public configureScripts(scripts: UpdateSchemaScriptsDto, user: string, now?: DateTime): SchemaDetailsDto { + return new SchemaDetailsDto( + this.id, + this.name, + this.properties, + this.isPublished, + this.createdBy, user, + this.created, now || DateTime.now(), + this.version, + this.fields, + scripts.scriptQuery, + scripts.scriptCreate, + scripts.scriptUpdate, + scripts.scriptDelete, + scripts.scriptPublish, + scripts.scriptUnpublish); } public update(properties: SchemaPropertiesDto, user: string, now?: DateTime): SchemaDetailsDto { @@ -159,7 +195,13 @@ export class SchemaDetailsDto extends SchemaDto { this.createdBy, user, this.created, now || DateTime.now(), this.version, - this.fields); + this.fields, + this.scriptQuery, + this.scriptCreate, + this.scriptUpdate, + this.scriptDelete, + this.scriptPublish, + this.scriptUnpublish); } public addField(field: FieldDto, user: string, now?: DateTime): SchemaDetailsDto { @@ -171,7 +213,13 @@ export class SchemaDetailsDto extends SchemaDto { this.createdBy, user, this.created, now || DateTime.now(), this.version, - [...this.fields, field]); + [...this.fields, field], + this.scriptQuery, + this.scriptCreate, + this.scriptUpdate, + this.scriptDelete, + this.scriptPublish, + this.scriptUnpublish); } public updateField(field: FieldDto, user: string, now?: DateTime): SchemaDetailsDto { @@ -183,7 +231,13 @@ export class SchemaDetailsDto extends SchemaDto { this.createdBy, user, this.created, now || DateTime.now(), this.version, - this.fields.map(f => f.fieldId === field.fieldId ? field : f)); + this.fields.map(f => f.fieldId === field.fieldId ? field : f), + this.scriptQuery, + this.scriptCreate, + this.scriptUpdate, + this.scriptDelete, + this.scriptPublish, + this.scriptUnpublish); } public replaceFields(fields: FieldDto[], user: string, now?: DateTime): SchemaDetailsDto { @@ -195,7 +249,13 @@ export class SchemaDetailsDto extends SchemaDto { this.createdBy, user, this.created, now || DateTime.now(), this.version, - fields); + fields, + this.scriptQuery, + this.scriptCreate, + this.scriptUpdate, + this.scriptDelete, + this.scriptPublish, + this.scriptUnpublish); } public removeField(field: FieldDto, user: string, now?: DateTime): SchemaDetailsDto { @@ -207,7 +267,13 @@ export class SchemaDetailsDto extends SchemaDto { this.createdBy, user, this.created, now || DateTime.now(), this.version, - this.fields.filter(f => f.fieldId !== field.fieldId)); + this.fields.filter(f => f.fieldId !== field.fieldId), + this.scriptQuery, + this.scriptCreate, + this.scriptUpdate, + this.scriptDelete, + this.scriptPublish, + this.scriptUnpublish); } } @@ -616,6 +682,18 @@ export class CreateSchemaDto { } } +export class UpdateSchemaScriptsDto { + constructor( + public readonly scriptQuery?: string, + public readonly scriptCreate?: string, + public readonly scriptUpdate?: string, + public readonly scriptDelete?: string, + public readonly scriptPublish?: string, + public readonly scriptUnpublish?: string + ) { + } +} + @Injectable() export class SchemasService { constructor( @@ -681,7 +759,13 @@ export class SchemasService { DateTime.parseISO_UTC(response.created), DateTime.parseISO_UTC(response.lastModified), new Version(response.version.toString()), - fields); + fields, + response.scriptsQuery, + response.scriptsCreate, + response.scriptsUpdate, + response.scriptsDelete, + response.scriptsPublish, + response.scriptsUnpublish); }) .catch(error => { if (error instanceof HttpErrorResponse && error.status === 404) { @@ -714,7 +798,13 @@ export class SchemasService { now, now, version, - dto.fields || []); + dto.fields || [], + response.scriptsQuery, + response.scriptsCreate, + response.scriptsUpdate, + response.scriptsDelete, + response.scriptsPublish, + response.scriptsUnpublish); }) .do(schema => { this.localCache.set(`schema.${appName}.${schema.id}`, schema, 5000); @@ -750,6 +840,13 @@ export class SchemasService { .pretifyError('Failed to delete schema. Please reload.'); } + public putSchemaScripts(appName: string, schemaName: string, dto: UpdateSchemaScriptsDto, version?: Version): Observable { + const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/scripts`); + + return HTTP.putVersioned(this.http, url, dto, version) + .pretifyError('Failed to update schema scripts. Please reload.'); + } + public putSchema(appName: string, schemaName: string, dto: UpdateSchemaDto, version?: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`); diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs index 5ef7f53eb..27a27f72d 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs @@ -187,24 +187,24 @@ namespace Squidex.Domain.Apps.Write.Schemas sut.ConfigureScripts(CreateCommand(new ConfigureScripts { - ScriptCreate = "", - ScriptUpdate = "", - ScriptDelete = "", - ScriptPublish = "", - ScriptUnpublish = "", - ScriptQuery = "", + ScriptQuery = "", + ScriptCreate = "", + ScriptUpdate = "", + ScriptDelete = "", + ScriptPublish = "", + ScriptUnpublish = "" })); sut.GetUncomittedEvents() .ShouldHaveSameEvents( CreateEvent(new ScriptsConfigured { - ScriptCreate = "", - ScriptUpdate = "", - ScriptDelete = "", - ScriptPublish = "", - ScriptUnpublish = "", - ScriptQuery = "", + ScriptQuery = "", + ScriptCreate = "", + ScriptUpdate = "", + ScriptDelete = "", + ScriptPublish = "", + ScriptUnpublish = "" }) ); } diff --git a/tests/Squidex.Infrastructure.Tests/Caching/InvalidatingMemoryCacheTest.cs b/tests/Squidex.Infrastructure.Tests/Caching/InvalidatingMemoryCacheTest.cs index 56ea36462..9a29394f7 100644 --- a/tests/Squidex.Infrastructure.Tests/Caching/InvalidatingMemoryCacheTest.cs +++ b/tests/Squidex.Infrastructure.Tests/Caching/InvalidatingMemoryCacheTest.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; using Xunit; +// ReSharper disable NotAccessedVariable // ReSharper disable RedundantAssignment namespace Squidex.Infrastructure.Caching @@ -98,12 +99,12 @@ namespace Squidex.Infrastructure.Caching [Fact] public void Should_use_inner_cache_to_get_value() { - object currentOut = 123; + object outValue; - A.CallTo(() => cache.TryGetValue("a-key", out currentOut)) + A.CallTo(() => cache.TryGetValue("a-key", out outValue)) .Returns(true); - var exists = sut.TryGetValue("a-key", out object result); + var exists = sut.TryGetValue("a-key", out var result); Assert.Equal(123, result); Assert.True(exists); diff --git a/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj b/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj index a41525658..d99b3ad5f 100644 --- a/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj +++ b/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj @@ -13,8 +13,8 @@ - - + +