Browse Source

JScript editor.

pull/104/head
Sebastian Stehle 9 years ago
parent
commit
824c88bed3
  1. 2
      Squidex.sln.DotSettings
  2. 2
      src/Squidex.Domain.Apps.Core/ContentEnricher.cs
  3. 2
      src/Squidex.Domain.Apps.Core/ContentValidator.cs
  4. 2
      src/Squidex.Domain.Apps.Core/Contents/IdContentData.cs
  5. 10
      src/Squidex.Domain.Apps.Core/Contents/NamedContentData.cs
  6. 4
      src/Squidex.Domain.Apps.Core/Schemas/Field_Generic.cs
  7. 2
      src/Squidex.Domain.Apps.Core/Schemas/Schema.cs
  8. 4
      src/Squidex.Domain.Apps.Core/Schemas/Validators/AssetsValidator.cs
  9. 4
      src/Squidex.Domain.Apps.Core/Schemas/Validators/ReferencesValidator.cs
  10. 4
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/ConstantVisitor.cs
  11. 2
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs
  12. 2
      src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj
  13. 2
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryGraphType.cs
  14. 2
      src/Squidex.Domain.Apps.Read/Webhooks/WebhookSender.cs
  15. 2
      src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs
  16. 16
      src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs
  17. 2
      src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs
  18. 1
      src/Squidex.Domain.Users/UserExtensions.cs
  19. 2
      src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs
  20. 2
      src/Squidex.Infrastructure.Redis/RedisSubscription.cs
  21. 2
      src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs
  22. 4
      src/Squidex.Infrastructure/CollectionExtensions.cs
  23. 2
      src/Squidex.Infrastructure/DomainObjectException.cs
  24. 2
      src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs
  25. 2
      src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs
  26. 5
      src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs
  27. 2
      src/Squidex.Infrastructure/Language.cs
  28. 2
      src/Squidex.Infrastructure/PropertiesBag.cs
  29. 6
      src/Squidex.Infrastructure/PropertyValue.cs
  30. 2
      src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs
  31. 2
      src/Squidex.Infrastructure/StringExtensions.cs
  32. 1
      src/Squidex.Infrastructure/TypeNameRegistry.cs
  33. 1
      src/Squidex/Config/Identity/IdentityUsage.cs
  34. 1
      src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs
  35. 4
      src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs
  36. 30
      src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs
  37. 4
      src/Squidex/Controllers/ContentApi/ContentsController.cs
  38. 2
      src/Squidex/Controllers/ContentApi/Generator/SchemaSwaggerGenerator.cs
  39. 22
      src/Squidex/Controllers/ContentApi/Models/ContentDto.cs
  40. 1
      src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs
  41. 2
      src/Squidex/Pipeline/CommandHandlers/ETagCommandMiddleware.cs
  42. 2
      src/Squidex/Pipeline/Swagger/SwaggerHelper.cs
  43. 2
      src/Squidex/Squidex.csproj
  44. 4
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  45. 1
      src/Squidex/app/framework/angular/jscript-editor.component.html
  46. 8
      src/Squidex/app/framework/angular/jscript-editor.component.scss
  47. 108
      src/Squidex/app/framework/angular/jscript-editor.component.ts
  48. 1
      src/Squidex/app/framework/declarations.ts
  49. 3
      src/Squidex/app/framework/module.ts
  50. 59
      src/Squidex/app/shared/services/schemas.service.spec.ts
  51. 117
      src/Squidex/app/shared/services/schemas.service.ts
  52. 24
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs
  53. 7
      tests/Squidex.Infrastructure.Tests/Caching/InvalidatingMemoryCacheTest.cs
  54. 4
      tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj

2
Squidex.sln.DotSettings

@ -24,8 +24,10 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToAutoProperty/@EntryIndexedValue">DO_NOT_SHOW</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToAutoProperty/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToAutoPropertyWhenPossible/@EntryIndexedValue">DO_NOT_SHOW</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToAutoPropertyWhenPossible/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToConstant_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToConstant_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InheritdocConsiderUsage/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MemberCanBePrivate_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MemberCanBePrivate_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=Mvc_002EViewNotResolved/@EntryIndexedValue">DO_NOT_SHOW</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=Mvc_002EViewNotResolved/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterOnlyUsedForPreconditionCheck_002ELocal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReturnTypeCanBeEnumerable_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReturnTypeCanBeEnumerable_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnnecessaryWhitespace/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnnecessaryWhitespace/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedAutoPropertyAccessor_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedAutoPropertyAccessor_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>

2
src/Squidex.Domain.Apps.Core/ContentEnricher.cs

@ -64,7 +64,7 @@ namespace Squidex.Domain.Apps.Core
var key = partitionItem.Key; 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); fieldData.AddValue(key, defaultValue);
} }

2
src/Squidex.Domain.Apps.Core/ContentValidator.cs

@ -50,7 +50,7 @@ namespace Squidex.Domain.Apps.Core
{ {
var fieldName = fieldData.Key; var fieldName = fieldData.Key;
if (!schema.FieldsByName.TryGetValue(fieldData.Key, out Field field)) if (!schema.FieldsByName.TryGetValue(fieldData.Key, out var field))
{ {
errors.AddError("<FIELD> is not a known field", fieldName); errors.AddError("<FIELD> is not a known field", fieldName);
} }

2
src/Squidex.Domain.Apps.Core/Contents/IdContentData.cs

@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Core.Contents
foreach (var fieldValue in this) foreach (var fieldValue in this)
{ {
if (!schema.FieldsById.TryGetValue(fieldValue.Key, out Field field)) if (!schema.FieldsById.TryGetValue(fieldValue.Key, out var field))
{ {
continue; continue;
} }

10
src/Squidex.Domain.Apps.Core/Contents/NamedContentData.cs

@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Core.Contents
foreach (var fieldValue in this) foreach (var fieldValue in this)
{ {
if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out Field field)) if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out var field))
{ {
continue; continue;
} }
@ -100,7 +100,7 @@ namespace Squidex.Domain.Apps.Core.Contents
foreach (var fieldValue in this) 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; continue;
} }
@ -114,7 +114,7 @@ namespace Squidex.Domain.Apps.Core.Contents
{ {
var languageCode = languageConfig.Key; var languageCode = languageConfig.Key;
if (fieldValues.TryGetValue(languageCode, out JToken value)) if (fieldValues.TryGetValue(languageCode, out var value))
{ {
fieldResult.Add(languageCode, value); fieldResult.Add(languageCode, value);
} }
@ -126,7 +126,7 @@ namespace Squidex.Domain.Apps.Core.Contents
} }
else else
{ {
if (fieldValues.TryGetValue(codeForInvariant, out JToken value)) if (fieldValues.TryGetValue(codeForInvariant, out var value))
{ {
fieldResult.Add(codeForInvariant, value); fieldResult.Add(codeForInvariant, value);
} }
@ -168,7 +168,7 @@ namespace Squidex.Domain.Apps.Core.Contents
foreach (var language in languagePreferences) 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; result[fieldValue.Key] = value;

4
src/Squidex.Domain.Apps.Core/Schemas/Field_Generic.cs

@ -46,9 +46,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
newProperties.Freeze(); newProperties.Freeze();
var typedProperties = newProperties as T; if (!(newProperties is T typedProperties))
if (typedProperties == null)
{ {
throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties)); throw new ArgumentException($"Properties must be of type '{typeof(T)}", nameof(newProperties));
} }

2
src/Squidex.Domain.Apps.Core/Schemas/Schema.cs

@ -179,7 +179,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
Guard.NotNull(updater, nameof(updater)); 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)); throw new DomainObjectNotFoundException(fieldId.ToString(), "Fields", typeof(Field));
} }

4
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<string> addError) public async Task ValidateAsync(object value, ValidationContext context, Action<string> addError)
{ {
var assets = value as AssetsValue; if (!(value is AssetsValue assets) || assets.AssetIds.Count == 0)
if (assets == null || assets.AssetIds.Count == 0)
{ {
if (isRequired && !context.IsOptional) if (isRequired && !context.IsOptional)
{ {

4
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<string> addError) public async Task ValidateAsync(object value, ValidationContext context, Action<string> addError)
{ {
var references = value as ReferencesValue; if (!(value is ReferencesValue references) || references.ContentIds.Count == 0)
if (references == null || references.ContentIds.Count == 0)
{ {
if (isRequired && !context.IsOptional) if (isRequired && !context.IsOptional)
{ {

4
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); 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; return InstantPattern.General.Parse(Visit(nodeIn.Source).ToString()).Value;

2
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(); var edmName = propertyNames[1].UnescapeEdmField();
if (!schema.FieldsByName.TryGetValue(edmName, out Field field)) if (!schema.FieldsByName.TryGetValue(edmName, out var field))
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }

2
src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj

@ -14,7 +14,7 @@
<ProjectReference Include="..\Squidex.Domain.Apps.Read\Squidex.Domain.Apps.Read.csproj" /> <ProjectReference Include="..\Squidex.Domain.Apps.Read\Squidex.Domain.Apps.Read.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.OData.Core" Version="7.2.0" /> <PackageReference Include="Microsoft.OData.Core" Version="7.3.1" />
<PackageReference Include="MongoDB.Driver" Version="2.4.4" /> <PackageReference Include="MongoDB.Driver" Version="2.4.4" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" /> <PackageReference Include="System.ValueTuple" Version="4.4.0" />
</ItemGroup> </ItemGroup>

2
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.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
// ReSharper disable ImpureMethodCallOnReadonlyValueField
namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{ {
public sealed class ContentQueryGraphType : ObjectGraphType public sealed class ContentQueryGraphType : ObjectGraphType

2
src/Squidex.Domain.Apps.Read/Webhooks/WebhookSender.cs

@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Read.Webhooks
{ {
try try
{ {
HttpRequestMessage request = BuildRequest(job); var request = BuildRequest(job);
HttpResponseMessage response = null; HttpResponseMessage response = null;
var responseString = string.Empty; var responseString = string.Empty;

2
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<string> message) private void ThrowIfFound(string contributorId, PermissionLevel permission, Func<string> 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"); var error = new ValidationError("Contributor is already part of the app with same permissions", "ContributorId");

16
src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs

@ -68,8 +68,8 @@ namespace Squidex.Domain.Apps.Write.Contents
var schemaAndApp = await ResolveSchemaAndAppAsync(command); var schemaAndApp = await ResolveSchemaAndAppAsync(command);
var scriptContext = CreateScriptContext(content, command, command.Data); var scriptContext = CreateScriptContext(content, command, command.Data);
command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.Schema.ScriptCreate, "create content", true); command.Data = scriptEngine.ExecuteAndTransform(scriptContext, schemaAndApp.SchemaEntity.ScriptCreate, "create content", true);
command.Data.Enrich(schemaAndApp.Schema.Schema, schemaAndApp.App.PartitionResolver); command.Data.Enrich(schemaAndApp.SchemaEntity.Schema, schemaAndApp.AppEntity.PartitionResolver);
await ValidateAsync(schemaAndApp, command, () => "Failed to create content", false); 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 schemaAndApp = await ResolveSchemaAndAppAsync(command);
var scriptContext = CreateScriptContext(content, command, command.Data); 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); 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 schemaAndApp = await ResolveSchemaAndAppAsync(command);
var scriptContext = CreateScriptContext(content, command, command.Data); 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); 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 schemaAndApp = await ResolveSchemaAndAppAsync(command);
var scriptContext = CreateScriptContext(content, 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); content.Publish(command);
}); });
@ -133,7 +133,7 @@ namespace Squidex.Domain.Apps.Write.Contents
var schemaAndApp = await ResolveSchemaAndAppAsync(command); var schemaAndApp = await ResolveSchemaAndAppAsync(command);
var scriptContext = CreateScriptContext(content, 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); content.Unpublish(command);
}); });
@ -146,7 +146,7 @@ namespace Squidex.Domain.Apps.Write.Contents
var schemaAndApp = await ResolveSchemaAndAppAsync(command); var schemaAndApp = await ResolveSchemaAndAppAsync(command);
var scriptContext = CreateScriptContext(content, 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); 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) }; 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 taskForApp = appProvider.FindAppByIdAsync(command.AppId.Id);
var taskForSchema = schemas.FindSchemaByIdAsync(command.SchemaId.Id); var taskForSchema = schemas.FindSchemaByIdAsync(command.SchemaId.Id);

2
src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs

@ -300,7 +300,7 @@ namespace Squidex.Domain.Apps.Write.Schemas
{ {
SimpleMapper.Map(fieldCommand, @event); 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<long>(field.Id, field.Name); @event.FieldId = new NamedId<long>(field.Id, field.Name);
} }

1
src/Squidex.Domain.Users/UserExtensions.cs

@ -12,6 +12,7 @@ using Squidex.Infrastructure;
using Squidex.Shared.Identity; using Squidex.Shared.Identity;
using Squidex.Shared.Users; using Squidex.Shared.Users;
// ReSharper disable PossibleNullReferenceException
// ReSharper disable InvertIf // ReSharper disable InvertIf
namespace Squidex.Domain.Users namespace Squidex.Domain.Users

2
src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs

@ -276,7 +276,7 @@ namespace Squidex.Infrastructure.CQRS.Events
{ {
var addressParts = projectionHost.Split(':'); 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; port = 2113;
} }

2
src/Squidex.Infrastructure.Redis/RedisSubscription.cs

@ -67,7 +67,7 @@ namespace Squidex.Infrastructure
return; return;
} }
if (!Guid.TryParse(parts[0], out Guid sender)) if (!Guid.TryParse(parts[0], out var sender))
{ {
return; return;
} }

2
src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs

@ -10,6 +10,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
// ReSharper disable ImpureMethodCallOnReadonlyValueField
namespace Squidex.Infrastructure.CQRS namespace Squidex.Infrastructure.CQRS
{ {
public abstract class DomainObjectBase : IAggregate, IEquatable<IAggregate> public abstract class DomainObjectBase : IAggregate, IEquatable<IAggregate>

4
src/Squidex.Infrastructure/CollectionExtensions.cs

@ -109,7 +109,7 @@ namespace Squidex.Infrastructure
public static TValue GetOrCreate<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> creator) public static TValue GetOrCreate<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> creator)
{ {
if (!dictionary.TryGetValue(key, out TValue result)) if (!dictionary.TryGetValue(key, out var result))
{ {
result = creator(key); result = creator(key);
} }
@ -119,7 +119,7 @@ namespace Squidex.Infrastructure
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> creator) public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> creator)
{ {
if (!dictionary.TryGetValue(key, out TValue result)) if (!dictionary.TryGetValue(key, out var result))
{ {
result = creator(key); result = creator(key);

2
src/Squidex.Infrastructure/DomainObjectException.cs

@ -8,6 +8,8 @@
using System; using System;
// ReSharper disable SuggestBaseTypeForParameter
namespace Squidex.Infrastructure namespace Squidex.Infrastructure
{ {
public class DomainObjectException : Exception public class DomainObjectException : Exception

2
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"); 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"); throw new JsonException("Named id must be a valid guid");
} }

2
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"); 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"); throw new JsonException("Named id must be a valid long");
} }

5
src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs

@ -7,7 +7,6 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Reflection;
using Newtonsoft.Json; using Newtonsoft.Json;
using NodaTime; using NodaTime;
using NodaTime.Extensions; using NodaTime.Extensions;
@ -38,9 +37,9 @@ namespace Squidex.Infrastructure.Json
var value = reader.Value; 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 else
{ {

2
src/Squidex.Infrastructure/Language.cs

@ -111,7 +111,7 @@ namespace Squidex.Infrastructure
input = match.Groups[0].Value; input = match.Groups[0].Value;
} }
if (TryGetLanguage(input.ToLowerInvariant(), out Language result)) if (TryGetLanguage(input.ToLowerInvariant(), out var result))
{ {
return result; return result;
} }

2
src/Squidex.Infrastructure/PropertiesBag.cs

@ -99,7 +99,7 @@ namespace Squidex.Infrastructure
throw new ArgumentException($"The property names '{newPropertyName}' are equal.", newPropertyName); 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; return false;
} }

6
src/Squidex.Infrastructure/PropertyValue.cs

@ -58,7 +58,7 @@ namespace Squidex.Infrastructure
{ {
result = null; result = null;
if (!Parsers.TryGetValue(binder.Type, out Func<PropertyValue, CultureInfo, object> parser)) if (!Parsers.TryGetValue(binder.Type, out var parser))
{ {
return false; return false;
} }
@ -145,12 +145,12 @@ namespace Squidex.Infrastructure
private T? ToNullableOrParseValue<T>(IFormatProvider culture, Func<string, T> parser) where T : struct private T? ToNullableOrParseValue<T>(IFormatProvider culture, Func<string, T> 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<T>(IFormatProvider culture, Func<string, T> parser) private T ToOrParseValue<T>(IFormatProvider culture, Func<string, T> parser)
{ {
return TryParse(culture, parser, out T result) ? result : default(T); return TryParse(culture, parser, out var result) ? result : default(T);
} }
private bool TryParse<T>(IFormatProvider culture, Func<string, T> parser, out T result) private bool TryParse<T>(IFormatProvider culture, Func<string, T> parser, out T result)

2
src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs

@ -68,7 +68,7 @@ namespace Squidex.Infrastructure.Reflection
{ {
Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); 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)); throw new ArgumentException("Property does not exist.", nameof(propertyName));
} }

2
src/Squidex.Infrastructure/StringExtensions.cs

@ -356,7 +356,7 @@ namespace Squidex.Infrastructure
var lower = char.ToLowerInvariant(character); var lower = char.ToLowerInvariant(character);
if (LowerCaseDiacritics.TryGetValue(character, out string replacement)) if (LowerCaseDiacritics.TryGetValue(character, out var replacement))
{ {
if (singleCharDiactric) if (singleCharDiactric)
{ {

1
src/Squidex.Infrastructure/TypeNameRegistry.cs

@ -10,6 +10,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
// ReSharper disable PossibleNullReferenceException
// ReSharper disable InvertIf // ReSharper disable InvertIf
namespace Squidex.Infrastructure namespace Squidex.Infrastructure

1
src/Squidex/Config/Identity/IdentityUsage.cs

@ -19,6 +19,7 @@ using Squidex.Infrastructure.Log;
using Squidex.Shared.Identity; using Squidex.Shared.Identity;
using Squidex.Shared.Users; using Squidex.Shared.Users;
// ReSharper disable ConvertIfStatementToConditionalTernaryExpression
// ReSharper disable InvertIf // ReSharper disable InvertIf
namespace Squidex.Config.Identity namespace Squidex.Config.Identity

1
src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs

@ -6,7 +6,6 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;

4
src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs

@ -35,7 +35,7 @@ namespace Squidex.Config.Swagger
{ {
var statusCode = match.Groups["Code"].Value; 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(); response = new SwaggerResponse();
@ -72,7 +72,7 @@ namespace Squidex.Config.Swagger
private static void RemoveOkResponse(SwaggerOperation operation) 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 != null &&
response.Description.Contains("=>")) response.Description.Contains("=>"))
{ {

30
src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs

@ -33,6 +33,36 @@ namespace Squidex.Controllers.Api.Schemas.Models
/// </summary> /// </summary>
public bool IsPublished { get; set; } public bool IsPublished { get; set; }
/// <summary>
/// The script that is executed for each query when querying contents.
/// </summary>
public string ScriptQuery { get; set; }
/// <summary>
/// The script that is executed when creating a content.
/// </summary>
public string ScriptCreate { get; set; }
/// <summary>
/// The script that is executed when updating a content.
/// </summary>
public string ScriptUpdate { get; set; }
/// <summary>
/// The script that is executed when deleting a content.
/// </summary>
public string ScriptDelete { get; set; }
/// <summary>
/// The script that is executed when publishing a content.
/// </summary>
public string ScriptPublish { get; set; }
/// <summary>
/// The script that is executed when unpublishing a content.
/// </summary>
public string ScriptUnpublish { get; set; }
/// <summary> /// <summary>
/// The list of fields. /// The list of fields.
/// </summary> /// </summary>

4
src/Squidex/Controllers/ContentApi/ContentsController.cs

@ -44,7 +44,7 @@ namespace Squidex.Controllers.ContentApi
public ContentsController( public ContentsController(
ICommandBus commandBus, ICommandBus commandBus,
ISchemaProvider schemas, ISchemaProvider schemas,
IScriptEngine scriptEngine, IScriptEngine scriptEngine,
IContentRepository contentRepository, IContentRepository contentRepository,
IGraphQLService graphQL) IGraphQLService graphQL)
@ -196,7 +196,7 @@ namespace Squidex.Controllers.ContentApi
var context = await CommandBus.PublishAsync(command); var context = await CommandBus.PublishAsync(command);
var result = context.Result<EntityCreatedResult<NamedContentData>>(); var result = context.Result<EntityCreatedResult<NamedContentData>>();
var response = result.IdOrValue; var response = ContentDto.Create(command, result);
return CreatedAtAction(nameof(GetContent), new { id = command.ContentId }, response); return CreatedAtAction(nameof(GetContent), new { id = command.ContentId }, response);
} }

2
src/Squidex/Controllers/ContentApi/Generator/SchemaSwaggerGenerator.cs

@ -150,7 +150,7 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.AddBodyParameter("data", dataSchema, SchemaBodyDescription); operation.AddBodyParameter("data", dataSchema, SchemaBodyDescription);
operation.AddQueryParameter("publish", JsonObjectType.Boolean, "Set to true to autopublish content."); 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; operation.Security = EditorSecurity;
}); });

22
src/Squidex/Controllers/ContentApi/Models/ContentDto.cs

@ -9,7 +9,10 @@
using System; using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Write.Contents.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
namespace Squidex.Controllers.ContentApi.Models namespace Squidex.Controllers.ContentApi.Models
{ {
@ -57,5 +60,24 @@ namespace Squidex.Controllers.ContentApi.Models
/// The version of the content. /// The version of the content.
/// </summary> /// </summary>
public long Version { get; set; } public long Version { get; set; }
public static ContentDto Create(CreateContent command, EntityCreatedResult<NamedContentData> 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;
}
} }
} }

1
src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs

@ -9,7 +9,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Squidex.Controllers.Api; using Squidex.Controllers.Api;

2
src/Squidex/Pipeline/CommandHandlers/ETagCommandMiddleware.cs

@ -29,7 +29,7 @@ namespace Squidex.Pipeline.CommandHandlers
var headers = httpContextAccessor.HttpContext.Request.Headers; var headers = httpContextAccessor.HttpContext.Request.Headers;
var headerMatch = headers["If-Match"].ToString(); 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; context.Command.ExpectedVersion = expectedVersion;
} }

2
src/Squidex/Pipeline/Swagger/SwaggerHelper.cs

@ -97,7 +97,7 @@ namespace Squidex.Pipeline.Swagger
{ SquidexRoles.AppOwner, "App contributor with Owner permission." }, { SquidexRoles.AppOwner, "App contributor with Owner permission." },
{ SquidexRoles.AppEditor, "Client (writer) or App contributor with Editor permission." }, { SquidexRoles.AppEditor, "Client (writer) or App contributor with Editor permission." },
{ SquidexRoles.AppReader, "Client (readonly) 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 Description = securityDescription
}; };

2
src/Squidex/Squidex.csproj

@ -71,7 +71,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" />
<PackageReference Include="Microsoft.Data.Edm" Version="5.8.2" /> <PackageReference Include="Microsoft.Data.Edm" Version="5.8.2" />
<PackageReference Include="Microsoft.OData.Core" Version="7.2.0" /> <PackageReference Include="Microsoft.OData.Core" Version="7.3.1" />
<PackageReference Include="MongoDB.Driver" Version="2.4.4" /> <PackageReference Include="MongoDB.Driver" Version="2.4.4" />
<PackageReference Include="NJsonSchema" Version="9.5.0" /> <PackageReference Include="NJsonSchema" Version="9.5.0" />
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="2.0.0" /> <PackageReference Include="NodaTime.Serialization.JsonNet" Version="2.0.0" />

4
src/Squidex/app/features/content/pages/content/content-page.component.ts

@ -129,8 +129,8 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
} else { } else {
this.appNameOnce() this.appNameOnce()
.switchMap(app => this.contentsService.putContent(app, this.schema.name, this.contentId!, requestDto, this.version)) .switchMap(app => this.contentsService.putContent(app, this.schema.name, this.contentId!, requestDto, this.version))
.subscribe(() => { .subscribe(dto => {
this.content = this.content.update(requestDto, this.authService.user.token); this.content = this.content.update(dto, this.authService.user.token);
this.emitContentUpdated(this.content); this.emitContentUpdated(this.content);
this.notifyInfo('Content saved successfully.'); this.notifyInfo('Content saved successfully.');

1
src/Squidex/app/framework/angular/jscript-editor.component.html

@ -0,0 +1 @@
<div class="editor" #editor></div>

8
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;
}

108
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();
}
}

1
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/http-extensions-impl';
export * from './angular/image-source.directive'; export * from './angular/image-source.directive';
export * from './angular/indeterminate-value.directive'; export * from './angular/indeterminate-value.directive';
export * from './angular/jscript-editor.component';
export * from './angular/json-editor.component'; export * from './angular/json-editor.component';
export * from './angular/lowercase-input.directive'; export * from './angular/lowercase-input.directive';
export * from './angular/markdown-editor.component'; export * from './angular/markdown-editor.component';

3
src/Squidex/app/framework/module.ts

@ -33,6 +33,7 @@ import {
GeolocationEditorComponent, GeolocationEditorComponent,
ImageSourceDirective, ImageSourceDirective,
IndeterminateValueDirective, IndeterminateValueDirective,
JscriptEditorComponent,
JsonEditorComponent, JsonEditorComponent,
KNumberPipe, KNumberPipe,
LocalCacheService, LocalCacheService,
@ -96,6 +97,7 @@ import {
GeolocationEditorComponent, GeolocationEditorComponent,
ImageSourceDirective, ImageSourceDirective,
IndeterminateValueDirective, IndeterminateValueDirective,
JscriptEditorComponent,
JsonEditorComponent, JsonEditorComponent,
KNumberPipe, KNumberPipe,
LowerCaseInputDirective, LowerCaseInputDirective,
@ -143,6 +145,7 @@ import {
GeolocationEditorComponent, GeolocationEditorComponent,
ImageSourceDirective, ImageSourceDirective,
IndeterminateValueDirective, IndeterminateValueDirective,
JscriptEditorComponent,
JsonEditorComponent, JsonEditorComponent,
KNumberPipe, KNumberPipe,
LowerCaseInputDirective, LowerCaseInputDirective,

59
src/Squidex/app/shared/services/schemas.service.spec.ts

@ -22,6 +22,7 @@ import {
SchemasService, SchemasService,
UpdateFieldDto, UpdateFieldDto,
UpdateSchemaDto, UpdateSchemaDto,
UpdateSchemaScriptsDto,
Version Version
} from './../'; } from './../';
@ -62,6 +63,31 @@ describe('SchemaDto', () => {
expect(schema_2.lastModified).toEqual(now); expect(schema_2.lastModified).toEqual(now);
expect(schema_2.lastModifiedBy).toEqual('me'); expect(schema_2.lastModifiedBy).toEqual('me');
}); });
it('should update scripts properties and user info when configure scripts', () => {
const newScripts =
new UpdateSchemaScriptsDto(
'<script-query>',
'<script-create>',
'<script-update>',
'<script-delete>',
'<script-publish>',
'<script-unpublish>');
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('<script-query>');
expect(schema_2.scriptCreate).toEqual('<script-create>');
expect(schema_2.scriptUpdate).toEqual('<script-update>');
expect(schema_2.scriptDelete).toEqual('<script-delete>');
expect(schema_2.scriptPublish).toEqual('<script-publish>');
expect(schema_2.scriptUnpublish).toEqual('<script-unpublish>');
expect(schema_2.lastModified).toEqual(now);
expect(schema_2.lastModifiedBy).toEqual('me');
});
}); });
describe('SchemaDetailsDto', () => { describe('SchemaDetailsDto', () => {
@ -361,7 +387,13 @@ describe('SchemasService', () => {
fieldType: 'References' fieldType: 'References'
} }
} }
] ],
scriptsQuery: '<script-query>',
scriptsCreate: '<script-create>',
scriptsUpdate: '<script-update>',
scriptsDelete: '<script-delete>',
scriptsPublish: '<script-publish>',
scriptsUnpublish: '<script-unpublish>'
}); });
expect(schema).toEqual( expect(schema).toEqual(
@ -378,7 +410,13 @@ describe('SchemasService', () => {
new FieldDto(6, 'field6', true, true, true, 'language', createProperties('Geolocation')), new FieldDto(6, 'field6', true, true, true, 'language', createProperties('Geolocation')),
new FieldDto(7, 'field7', true, true, true, 'language', createProperties('Assets')), new FieldDto(7, 'field7', true, true, true, 'language', createProperties('Assets')),
new FieldDto(8, 'field8', true, true, true, 'language', createProperties('References')) new FieldDto(8, 'field8', true, true, true, 'language', createProperties('References'))
])); ],
'<script-query>',
'<script-create>',
'<script-update>',
'<script-delete>',
'<script-publish>',
'<script-unpublish>'));
})); }));
it('should provide entry from cache if not found', it('should provide entry from cache if not found',
@ -460,7 +498,22 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value); 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', it('should make put request to update field',

117
src/Squidex/app/shared/services/schemas.service.ts

@ -121,7 +121,13 @@ export class SchemaDto {
export class SchemaDetailsDto extends 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, 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); super(id, name, properties, isPublished, createdBy, lastModifiedBy, created, lastModified, version);
} }
@ -135,7 +141,13 @@ export class SchemaDetailsDto extends SchemaDto {
this.createdBy, user, this.createdBy, user,
this.created, now || DateTime.now(), this.created, now || DateTime.now(),
this.version, 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 { public unpublish(user: string, now?: DateTime): SchemaDetailsDto {
@ -147,7 +159,31 @@ export class SchemaDetailsDto extends SchemaDto {
this.createdBy, user, this.createdBy, user,
this.created, now || DateTime.now(), this.created, now || DateTime.now(),
this.version, 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 { public update(properties: SchemaPropertiesDto, user: string, now?: DateTime): SchemaDetailsDto {
@ -159,7 +195,13 @@ export class SchemaDetailsDto extends SchemaDto {
this.createdBy, user, this.createdBy, user,
this.created, now || DateTime.now(), this.created, now || DateTime.now(),
this.version, 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 { public addField(field: FieldDto, user: string, now?: DateTime): SchemaDetailsDto {
@ -171,7 +213,13 @@ export class SchemaDetailsDto extends SchemaDto {
this.createdBy, user, this.createdBy, user,
this.created, now || DateTime.now(), this.created, now || DateTime.now(),
this.version, 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 { public updateField(field: FieldDto, user: string, now?: DateTime): SchemaDetailsDto {
@ -183,7 +231,13 @@ export class SchemaDetailsDto extends SchemaDto {
this.createdBy, user, this.createdBy, user,
this.created, now || DateTime.now(), this.created, now || DateTime.now(),
this.version, 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 { public replaceFields(fields: FieldDto[], user: string, now?: DateTime): SchemaDetailsDto {
@ -195,7 +249,13 @@ export class SchemaDetailsDto extends SchemaDto {
this.createdBy, user, this.createdBy, user,
this.created, now || DateTime.now(), this.created, now || DateTime.now(),
this.version, 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 { public removeField(field: FieldDto, user: string, now?: DateTime): SchemaDetailsDto {
@ -207,7 +267,13 @@ export class SchemaDetailsDto extends SchemaDto {
this.createdBy, user, this.createdBy, user,
this.created, now || DateTime.now(), this.created, now || DateTime.now(),
this.version, 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() @Injectable()
export class SchemasService { export class SchemasService {
constructor( constructor(
@ -681,7 +759,13 @@ export class SchemasService {
DateTime.parseISO_UTC(response.created), DateTime.parseISO_UTC(response.created),
DateTime.parseISO_UTC(response.lastModified), DateTime.parseISO_UTC(response.lastModified),
new Version(response.version.toString()), new Version(response.version.toString()),
fields); fields,
response.scriptsQuery,
response.scriptsCreate,
response.scriptsUpdate,
response.scriptsDelete,
response.scriptsPublish,
response.scriptsUnpublish);
}) })
.catch(error => { .catch(error => {
if (error instanceof HttpErrorResponse && error.status === 404) { if (error instanceof HttpErrorResponse && error.status === 404) {
@ -714,7 +798,13 @@ export class SchemasService {
now, now,
now, now,
version, version,
dto.fields || []); dto.fields || [],
response.scriptsQuery,
response.scriptsCreate,
response.scriptsUpdate,
response.scriptsDelete,
response.scriptsPublish,
response.scriptsUnpublish);
}) })
.do(schema => { .do(schema => {
this.localCache.set(`schema.${appName}.${schema.id}`, schema, 5000); this.localCache.set(`schema.${appName}.${schema.id}`, schema, 5000);
@ -750,6 +840,13 @@ export class SchemasService {
.pretifyError('Failed to delete schema. Please reload.'); .pretifyError('Failed to delete schema. Please reload.');
} }
public putSchemaScripts(appName: string, schemaName: string, dto: UpdateSchemaScriptsDto, version?: Version): Observable<any> {
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<any> { public putSchema(appName: string, schemaName: string, dto: UpdateSchemaDto, version?: Version): Observable<any> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`);

24
tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs

@ -187,24 +187,24 @@ namespace Squidex.Domain.Apps.Write.Schemas
sut.ConfigureScripts(CreateCommand(new ConfigureScripts sut.ConfigureScripts(CreateCommand(new ConfigureScripts
{ {
ScriptCreate = "<create>", ScriptQuery = "<script-query>",
ScriptUpdate = "<update>", ScriptCreate = "<script-create>",
ScriptDelete = "<delete>", ScriptUpdate = "<script-update>",
ScriptPublish = "<publish>", ScriptDelete = "<script-delete>",
ScriptUnpublish = "<unpublish>", ScriptPublish = "<script-publish>",
ScriptQuery = "<query>", ScriptUnpublish = "<script-unpublish>"
})); }));
sut.GetUncomittedEvents() sut.GetUncomittedEvents()
.ShouldHaveSameEvents( .ShouldHaveSameEvents(
CreateEvent(new ScriptsConfigured CreateEvent(new ScriptsConfigured
{ {
ScriptCreate = "<create>", ScriptQuery = "<script-query>",
ScriptUpdate = "<update>", ScriptCreate = "<script-create>",
ScriptDelete = "<delete>", ScriptUpdate = "<script-update>",
ScriptPublish = "<publish>", ScriptDelete = "<script-delete>",
ScriptUnpublish = "<unpublish>", ScriptPublish = "<script-publish>",
ScriptQuery = "<query>", ScriptUnpublish = "<script-unpublish>"
}) })
); );
} }

7
tests/Squidex.Infrastructure.Tests/Caching/InvalidatingMemoryCacheTest.cs

@ -11,6 +11,7 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Xunit; using Xunit;
// ReSharper disable NotAccessedVariable
// ReSharper disable RedundantAssignment // ReSharper disable RedundantAssignment
namespace Squidex.Infrastructure.Caching namespace Squidex.Infrastructure.Caching
@ -98,12 +99,12 @@ namespace Squidex.Infrastructure.Caching
[Fact] [Fact]
public void Should_use_inner_cache_to_get_value() 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); .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.Equal(123, result);
Assert.True(exists); Assert.True(exists);

4
tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj

@ -13,8 +13,8 @@
<PackageReference Include="FakeItEasy" Version="4.0.0" /> <PackageReference Include="FakeItEasy" Version="4.0.0" />
<PackageReference Include="FluentAssertions" Version="4.19.4" /> <PackageReference Include="FluentAssertions" Version="4.19.4" />
<PackageReference Include="Google.Cloud.Storage.V1" Version="2.0.0" /> <PackageReference Include="Google.Cloud.Storage.V1" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" /> <PackageReference Include="System.ValueTuple" Version="4.4.0" />
<PackageReference Include="xunit" Version="2.2.0" /> <PackageReference Include="xunit" Version="2.2.0" />

Loading…
Cancel
Save