Browse Source

Minor performance improvements.

pull/335/head
Sebastian Stehle 8 years ago
parent
commit
12f6b3ddd2
  1. 3
      src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs
  2. 11
      src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs
  3. 6
      src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs
  4. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs
  5. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs
  6. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs
  7. 4
      src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs
  8. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs
  9. 164
      src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs
  10. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs
  11. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/IField.cs
  12. 18
      src/Squidex.Domain.Apps.Core.Model/Schemas/IFieldSettings.cs
  13. 5
      src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonFieldModel.cs
  14. 7
      src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs
  15. 66
      src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs
  16. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs
  17. 9
      src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs
  18. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs
  19. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs
  20. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs
  21. 9
      src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs
  22. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs
  23. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs
  24. 8
      src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs
  25. 2
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs
  26. 26
      src/Squidex.Infrastructure/CollectionExtensions.cs
  27. 2
      src/Squidex.Infrastructure/Language.cs
  28. 5
      src/Squidex.Infrastructure/Log/IArrayWriter.cs
  29. 6
      src/Squidex.Infrastructure/Log/IObjectWriter.cs
  30. 16
      src/Squidex.Infrastructure/Log/IObjectWriterFactory.cs
  31. 35
      src/Squidex.Infrastructure/Log/JsonLogWriter.cs
  32. 61
      src/Squidex.Infrastructure/Log/JsonLogWriterFactory.cs
  33. 38
      src/Squidex.Infrastructure/Log/SemanticLog.cs
  34. 2
      src/Squidex.Infrastructure/Security/PermissionSet.cs
  35. 3
      src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs
  36. 2
      src/Squidex.Infrastructure/ValidationError.cs
  37. 3
      src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs
  38. 2
      src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs
  39. 3
      src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs
  40. 3
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  41. 8
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs
  42. 3
      src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs
  43. 5
      src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs
  44. 3
      src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateLanguageDto.cs
  45. 3
      src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs
  46. 8
      src/Squidex/Areas/Api/Controllers/Comments/Models/CommentsDto.cs
  47. 1
      src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  48. 2
      src/Squidex/Areas/Api/Controllers/EventConsumers/EventConsumersController.cs
  49. 3
      src/Squidex/Areas/Api/Controllers/History/HistoryController.cs
  50. 2
      src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs
  51. 5
      src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs
  52. 2
      src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleTriggerDtoFactory.cs
  53. 1
      src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs
  54. 2
      src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerDto.cs
  55. 2
      src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs
  56. 3
      src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs
  57. 5
      src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs
  58. 6
      src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs
  59. 2
      src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs
  60. 7
      src/Squidex/Config/Domain/LoggingServices.cs
  61. 1
      src/Squidex/Config/Web/WebExtensions.cs
  62. 2
      src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs
  63. 53
      src/Squidex/Pipeline/ETagExtensions.cs
  64. 75
      src/Squidex/app/theme/icomoon/icons/control-Tags.svg
  65. 4
      tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldRegistryTests.cs
  66. 3
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/RoleExtensionsRests.cs
  67. 10
      tests/Squidex.Infrastructure.Tests/Log/JsonLogWriterTests.cs
  68. 4
      tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs
  69. 29
      tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs

3
src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -13,7 +14,7 @@ namespace Squidex.Domain.Apps.Core.Apps
{ {
public sealed class LanguageConfig : IFieldPartitionItem public sealed class LanguageConfig : IFieldPartitionItem
{ {
private static readonly Language[] DefaultFallback = new Language[0]; private static readonly Language[] DefaultFallback = Array.Empty<Language>();
private readonly Language language; private readonly Language language;
private readonly Language[] languageFallbacks; private readonly Language[] languageFallbacks;

11
src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
@ -30,11 +31,19 @@ namespace Squidex.Domain.Apps.Core.Schemas
get { return fields.ByName; } get { return fields.ByName; }
} }
public ArrayField(long id, string name, Partitioning partitioning, ArrayFieldProperties properties) public ArrayField(long id, string name, Partitioning partitioning, ArrayFieldProperties properties = null, IFieldSettings settings = null)
: base(id, name, partitioning, properties) : base(id, name, partitioning, properties)
{ {
} }
public ArrayField(long id, string name, Partitioning partitioning, NestedField[] fields, ArrayFieldProperties properties = null, IFieldSettings settings = null)
: this(id, name, partitioning, properties)
{
Guard.NotNull(fields, nameof(fields));
this.fields = new FieldCollection<NestedField>(fields);
}
[Pure] [Pure]
public ArrayField DeleteField(long fieldId) public ArrayField DeleteField(long fieldId)
{ {

6
src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs

@ -27,12 +27,12 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IArrayField)field); return visitor.Visit((IArrayField)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.Array(id, name, partitioning, this); return Fields.Array(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }

8
src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs

@ -47,14 +47,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IField<AssetsFieldProperties>)field); return visitor.Visit((IField<AssetsFieldProperties>)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.Assets(id, name, partitioning, this); return Fields.Assets(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return Fields.Assets(id, name, this); return Fields.Assets(id, name, this, settings);
} }
} }
} }

8
src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs

@ -28,14 +28,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IField<BooleanFieldProperties>)field); return visitor.Visit((IField<BooleanFieldProperties>)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.Boolean(id, name, partitioning, this); return Fields.Boolean(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return Fields.Boolean(id, name, this); return Fields.Boolean(id, name, this, settings);
} }
} }
} }

8
src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs

@ -33,14 +33,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IField<DateTimeFieldProperties>)field); return visitor.Visit((IField<DateTimeFieldProperties>)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.DateTime(id, name, partitioning, this); return Fields.DateTime(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return Fields.DateTime(id, name, this); return Fields.DateTime(id, name, this, settings);
} }
} }
} }

4
src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs

@ -21,8 +21,8 @@ namespace Squidex.Domain.Apps.Core.Schemas
public abstract T Accept<T>(IFieldVisitor<T> visitor, IField field); public abstract T Accept<T>(IFieldVisitor<T> visitor, IField field);
public abstract RootField CreateRootField(long id, string name, Partitioning partitioning); public abstract RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null);
public abstract NestedField CreateNestedField(long id, string name); public abstract NestedField CreateNestedField(long id, string name, IFieldSettings settings = null);
} }
} }

8
src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs

@ -42,18 +42,18 @@ namespace Squidex.Domain.Apps.Core.Schemas
} }
} }
public RootField CreateRootField(long id, string name, Partitioning partitioning, FieldProperties properties) public RootField CreateRootField(long id, string name, Partitioning partitioning, FieldProperties properties, IFieldSettings settings = null)
{ {
CheckProperties(properties); CheckProperties(properties);
return properties.CreateRootField(id, name, partitioning); return properties.CreateRootField(id, name, partitioning, settings);
} }
public NestedField CreateNestedField(long id, string name, FieldProperties properties) public NestedField CreateNestedField(long id, string name, FieldProperties properties, IFieldSettings settings = null)
{ {
CheckProperties(properties); CheckProperties(properties);
return properties.CreateNestedField(id, name); return properties.CreateNestedField(id, name, settings);
} }
private void CheckProperties(FieldProperties properties) private void CheckProperties(FieldProperties properties)

164
src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs

@ -13,117 +13,107 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
public static RootField<ArrayFieldProperties> Array(long id, string name, Partitioning partitioning, params NestedField[] fields) public static RootField<ArrayFieldProperties> Array(long id, string name, Partitioning partitioning, params NestedField[] fields)
{ {
var result = new ArrayField(id, name, partitioning, new ArrayFieldProperties()); return new ArrayField(id, name, partitioning, fields: fields);
if (fields != null)
{
foreach (var field in fields)
{
result = result.AddField(field);
}
}
return result;
} }
public static ArrayField Array(long id, string name, Partitioning partitioning, ArrayFieldProperties properties = null) public static ArrayField Array(long id, string name, Partitioning partitioning, ArrayFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new ArrayField(id, name, partitioning, properties ?? new ArrayFieldProperties()); return new ArrayField(id, name, partitioning, properties, settings);
} }
public static RootField<AssetsFieldProperties> Assets(long id, string name, Partitioning partitioning, AssetsFieldProperties properties = null) public static RootField<AssetsFieldProperties> Assets(long id, string name, Partitioning partitioning, AssetsFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new RootField<AssetsFieldProperties>(id, name, partitioning, properties ?? new AssetsFieldProperties()); return new RootField<AssetsFieldProperties>(id, name, partitioning, properties, settings);
} }
public static RootField<BooleanFieldProperties> Boolean(long id, string name, Partitioning partitioning, BooleanFieldProperties properties = null) public static RootField<BooleanFieldProperties> Boolean(long id, string name, Partitioning partitioning, BooleanFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new RootField<BooleanFieldProperties>(id, name, partitioning, properties ?? new BooleanFieldProperties()); return new RootField<BooleanFieldProperties>(id, name, partitioning, properties, settings);
} }
public static RootField<DateTimeFieldProperties> DateTime(long id, string name, Partitioning partitioning, DateTimeFieldProperties properties = null) public static RootField<DateTimeFieldProperties> DateTime(long id, string name, Partitioning partitioning, DateTimeFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new RootField<DateTimeFieldProperties>(id, name, partitioning, properties ?? new DateTimeFieldProperties()); return new RootField<DateTimeFieldProperties>(id, name, partitioning, properties, settings);
} }
public static RootField<GeolocationFieldProperties> Geolocation(long id, string name, Partitioning partitioning, GeolocationFieldProperties properties = null) public static RootField<GeolocationFieldProperties> Geolocation(long id, string name, Partitioning partitioning, GeolocationFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new RootField<GeolocationFieldProperties>(id, name, partitioning, properties ?? new GeolocationFieldProperties()); return new RootField<GeolocationFieldProperties>(id, name, partitioning, properties, settings);
} }
public static RootField<JsonFieldProperties> Json(long id, string name, Partitioning partitioning, JsonFieldProperties properties = null) public static RootField<JsonFieldProperties> Json(long id, string name, Partitioning partitioning, JsonFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new RootField<JsonFieldProperties>(id, name, partitioning, properties ?? new JsonFieldProperties()); return new RootField<JsonFieldProperties>(id, name, partitioning, properties, settings);
} }
public static RootField<NumberFieldProperties> Number(long id, string name, Partitioning partitioning, NumberFieldProperties properties = null) public static RootField<NumberFieldProperties> Number(long id, string name, Partitioning partitioning, NumberFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new RootField<NumberFieldProperties>(id, name, partitioning, properties ?? new NumberFieldProperties()); return new RootField<NumberFieldProperties>(id, name, partitioning, properties, settings);
} }
public static RootField<ReferencesFieldProperties> References(long id, string name, Partitioning partitioning, ReferencesFieldProperties properties = null) public static RootField<ReferencesFieldProperties> References(long id, string name, Partitioning partitioning, ReferencesFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new RootField<ReferencesFieldProperties>(id, name, partitioning, properties ?? new ReferencesFieldProperties()); return new RootField<ReferencesFieldProperties>(id, name, partitioning, properties, settings);
} }
public static RootField<StringFieldProperties> String(long id, string name, Partitioning partitioning, StringFieldProperties properties = null) public static RootField<StringFieldProperties> String(long id, string name, Partitioning partitioning, StringFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new RootField<StringFieldProperties>(id, name, partitioning, properties ?? new StringFieldProperties()); return new RootField<StringFieldProperties>(id, name, partitioning, properties, settings);
} }
public static RootField<TagsFieldProperties> Tags(long id, string name, Partitioning partitioning, TagsFieldProperties properties = null) public static RootField<TagsFieldProperties> Tags(long id, string name, Partitioning partitioning, TagsFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new RootField<TagsFieldProperties>(id, name, partitioning, properties ?? new TagsFieldProperties()); return new RootField<TagsFieldProperties>(id, name, partitioning, properties, settings);
} }
public static NestedField<AssetsFieldProperties> Assets(long id, string name, AssetsFieldProperties properties = null) public static NestedField<AssetsFieldProperties> Assets(long id, string name, AssetsFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new NestedField<AssetsFieldProperties>(id, name, properties ?? new AssetsFieldProperties()); return new NestedField<AssetsFieldProperties>(id, name, properties, settings);
} }
public static NestedField<BooleanFieldProperties> Boolean(long id, string name, BooleanFieldProperties properties = null) public static NestedField<BooleanFieldProperties> Boolean(long id, string name, BooleanFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new NestedField<BooleanFieldProperties>(id, name, properties ?? new BooleanFieldProperties()); return new NestedField<BooleanFieldProperties>(id, name, properties, settings);
} }
public static NestedField<DateTimeFieldProperties> DateTime(long id, string name, DateTimeFieldProperties properties = null) public static NestedField<DateTimeFieldProperties> DateTime(long id, string name, DateTimeFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new NestedField<DateTimeFieldProperties>(id, name, properties ?? new DateTimeFieldProperties()); return new NestedField<DateTimeFieldProperties>(id, name, properties, settings);
} }
public static NestedField<GeolocationFieldProperties> Geolocation(long id, string name, GeolocationFieldProperties properties = null) public static NestedField<GeolocationFieldProperties> Geolocation(long id, string name, GeolocationFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new NestedField<GeolocationFieldProperties>(id, name, properties ?? new GeolocationFieldProperties()); return new NestedField<GeolocationFieldProperties>(id, name, properties, settings);
} }
public static NestedField<JsonFieldProperties> Json(long id, string name, JsonFieldProperties properties = null) public static NestedField<JsonFieldProperties> Json(long id, string name, JsonFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new NestedField<JsonFieldProperties>(id, name, properties ?? new JsonFieldProperties()); return new NestedField<JsonFieldProperties>(id, name, properties, settings);
} }
public static NestedField<NumberFieldProperties> Number(long id, string name, NumberFieldProperties properties = null) public static NestedField<NumberFieldProperties> Number(long id, string name, NumberFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new NestedField<NumberFieldProperties>(id, name, properties ?? new NumberFieldProperties()); return new NestedField<NumberFieldProperties>(id, name, properties, settings);
} }
public static NestedField<ReferencesFieldProperties> References(long id, string name, ReferencesFieldProperties properties = null) public static NestedField<ReferencesFieldProperties> References(long id, string name, ReferencesFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new NestedField<ReferencesFieldProperties>(id, name, properties ?? new ReferencesFieldProperties()); return new NestedField<ReferencesFieldProperties>(id, name, properties, settings);
} }
public static NestedField<StringFieldProperties> String(long id, string name, StringFieldProperties properties = null) public static NestedField<StringFieldProperties> String(long id, string name, StringFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new NestedField<StringFieldProperties>(id, name, properties ?? new StringFieldProperties()); return new NestedField<StringFieldProperties>(id, name, properties, settings);
} }
public static NestedField<TagsFieldProperties> Tags(long id, string name, TagsFieldProperties properties = null) public static NestedField<TagsFieldProperties> Tags(long id, string name, TagsFieldProperties properties = null, IFieldSettings settings = null)
{ {
return new NestedField<TagsFieldProperties>(id, name, properties ?? new TagsFieldProperties()); return new NestedField<TagsFieldProperties>(id, name, properties, settings);
} }
public static Schema AddArray(this Schema schema, long id, string name, Partitioning partitioning, Func<ArrayField, ArrayField> handler, ArrayFieldProperties properties = null) public static Schema AddArray(this Schema schema, long id, string name, Partitioning partitioning, Func<ArrayField, ArrayField> handler, ArrayFieldProperties properties = null, IFieldSettings settings = null)
{ {
var field = Array(id, name, partitioning, properties); var field = Array(id, name, partitioning, properties, settings);
if (handler != null) if (handler != null)
{ {
@ -133,94 +123,94 @@ namespace Squidex.Domain.Apps.Core.Schemas
return schema.AddField(field); return schema.AddField(field);
} }
public static Schema AddAssets(this Schema schema, long id, string name, Partitioning partitioning, AssetsFieldProperties properties = null) public static Schema AddAssets(this Schema schema, long id, string name, Partitioning partitioning, AssetsFieldProperties properties = null, IFieldSettings settings = null)
{ {
return schema.AddField(Assets(id, name, partitioning, properties)); return schema.AddField(Assets(id, name, partitioning, properties, settings));
} }
public static Schema AddBoolean(this Schema schema, long id, string name, Partitioning partitioning, BooleanFieldProperties properties = null) public static Schema AddBoolean(this Schema schema, long id, string name, Partitioning partitioning, BooleanFieldProperties properties = null, IFieldSettings settings = null)
{ {
return schema.AddField(Boolean(id, name, partitioning, properties)); return schema.AddField(Boolean(id, name, partitioning, properties, settings));
} }
public static Schema AddDateTime(this Schema schema, long id, string name, Partitioning partitioning, DateTimeFieldProperties properties = null) public static Schema AddDateTime(this Schema schema, long id, string name, Partitioning partitioning, DateTimeFieldProperties properties = null, IFieldSettings settings = null)
{ {
return schema.AddField(DateTime(id, name, partitioning, properties)); return schema.AddField(DateTime(id, name, partitioning, properties, settings));
} }
public static Schema AddGeolocation(this Schema schema, long id, string name, Partitioning partitioning, GeolocationFieldProperties properties = null) public static Schema AddGeolocation(this Schema schema, long id, string name, Partitioning partitioning, GeolocationFieldProperties properties = null, IFieldSettings settings = null)
{ {
return schema.AddField(Geolocation(id, name, partitioning, properties)); return schema.AddField(Geolocation(id, name, partitioning, properties, settings));
} }
public static Schema AddJson(this Schema schema, long id, string name, Partitioning partitioning, JsonFieldProperties properties = null) public static Schema AddJson(this Schema schema, long id, string name, Partitioning partitioning, JsonFieldProperties properties = null, IFieldSettings settings = null)
{ {
return schema.AddField(Json(id, name, partitioning, properties)); return schema.AddField(Json(id, name, partitioning, properties, settings));
} }
public static Schema AddNumber(this Schema schema, long id, string name, Partitioning partitioning, NumberFieldProperties properties = null) public static Schema AddNumber(this Schema schema, long id, string name, Partitioning partitioning, NumberFieldProperties properties = null, IFieldSettings settings = null)
{ {
return schema.AddField(Number(id, name, partitioning, properties)); return schema.AddField(Number(id, name, partitioning, properties, settings));
} }
public static Schema AddReferences(this Schema schema, long id, string name, Partitioning partitioning, ReferencesFieldProperties properties = null) public static Schema AddReferences(this Schema schema, long id, string name, Partitioning partitioning, ReferencesFieldProperties properties = null, IFieldSettings settings = null)
{ {
return schema.AddField(References(id, name, partitioning, properties)); return schema.AddField(References(id, name, partitioning, properties, settings));
} }
public static Schema AddString(this Schema schema, long id, string name, Partitioning partitioning, StringFieldProperties properties = null) public static Schema AddString(this Schema schema, long id, string name, Partitioning partitioning, StringFieldProperties properties = null, IFieldSettings settings = null)
{ {
return schema.AddField(String(id, name, partitioning, properties)); return schema.AddField(String(id, name, partitioning, properties, settings));
} }
public static Schema AddTags(this Schema schema, long id, string name, Partitioning partitioning, TagsFieldProperties properties = null) public static Schema AddTags(this Schema schema, long id, string name, Partitioning partitioning, TagsFieldProperties properties = null, IFieldSettings settings = null)
{ {
return schema.AddField(Tags(id, name, partitioning, properties)); return schema.AddField(Tags(id, name, partitioning, properties, settings));
} }
public static ArrayField AddAssets(this ArrayField field, long id, string name, AssetsFieldProperties properties = null) public static ArrayField AddAssets(this ArrayField field, long id, string name, AssetsFieldProperties properties = null, IFieldSettings settings = null)
{ {
return field.AddField(Assets(id, name, properties)); return field.AddField(Assets(id, name, properties, settings));
} }
public static ArrayField AddBoolean(this ArrayField field, long id, string name, BooleanFieldProperties properties = null) public static ArrayField AddBoolean(this ArrayField field, long id, string name, BooleanFieldProperties properties = null, IFieldSettings settings = null)
{ {
return field.AddField(Boolean(id, name, properties)); return field.AddField(Boolean(id, name, properties, settings));
} }
public static ArrayField AddDateTime(this ArrayField field, long id, string name, DateTimeFieldProperties properties = null) public static ArrayField AddDateTime(this ArrayField field, long id, string name, DateTimeFieldProperties properties = null, IFieldSettings settings = null)
{ {
return field.AddField(DateTime(id, name, properties)); return field.AddField(DateTime(id, name, properties, settings));
} }
public static ArrayField AddGeolocation(this ArrayField field, long id, string name, GeolocationFieldProperties properties = null) public static ArrayField AddGeolocation(this ArrayField field, long id, string name, GeolocationFieldProperties properties = null, IFieldSettings settings = null)
{ {
return field.AddField(Geolocation(id, name, properties)); return field.AddField(Geolocation(id, name, properties, settings));
} }
public static ArrayField AddJson(this ArrayField field, long id, string name, JsonFieldProperties properties = null) public static ArrayField AddJson(this ArrayField field, long id, string name, JsonFieldProperties properties = null, IFieldSettings settings = null)
{ {
return field.AddField(Json(id, name, properties)); return field.AddField(Json(id, name, properties, settings));
} }
public static ArrayField AddNumber(this ArrayField field, long id, string name, NumberFieldProperties properties = null) public static ArrayField AddNumber(this ArrayField field, long id, string name, NumberFieldProperties properties = null, IFieldSettings settings = null)
{ {
return field.AddField(Number(id, name, properties)); return field.AddField(Number(id, name, properties, settings));
} }
public static ArrayField AddReferences(this ArrayField field, long id, string name, ReferencesFieldProperties properties = null) public static ArrayField AddReferences(this ArrayField field, long id, string name, ReferencesFieldProperties properties = null, IFieldSettings settings = null)
{ {
return field.AddField(References(id, name, properties)); return field.AddField(References(id, name, properties, settings));
} }
public static ArrayField AddString(this ArrayField field, long id, string name, StringFieldProperties properties = null) public static ArrayField AddString(this ArrayField field, long id, string name, StringFieldProperties properties = null, IFieldSettings settings = null)
{ {
return field.AddField(String(id, name, properties)); return field.AddField(String(id, name, properties, settings));
} }
public static ArrayField AddTags(this ArrayField field, long id, string name, TagsFieldProperties properties = null) public static ArrayField AddTags(this ArrayField field, long id, string name, TagsFieldProperties properties = null, IFieldSettings settings = null)
{ {
return field.AddField(Tags(id, name, properties)); return field.AddField(Tags(id, name, properties, settings));
} }
} }
} }

8
src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs

@ -24,14 +24,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IField<GeolocationFieldProperties>)field); return visitor.Visit((IField<GeolocationFieldProperties>)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.Geolocation(id, name, partitioning, this); return Fields.Geolocation(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return Fields.Geolocation(id, name, this); return Fields.Geolocation(id, name, this, settings);
} }
} }
} }

8
src/Squidex.Domain.Apps.Core.Model/Schemas/IField.cs

@ -7,18 +7,12 @@
namespace Squidex.Domain.Apps.Core.Schemas namespace Squidex.Domain.Apps.Core.Schemas
{ {
public interface IField public interface IField : IFieldSettings
{ {
long Id { get; } long Id { get; }
string Name { get; } string Name { get; }
bool IsLocked { get; }
bool IsDisabled { get; }
bool IsHidden { get; }
FieldProperties RawProperties { get; } FieldProperties RawProperties { get; }
T Accept<T>(IFieldVisitor<T> visitor); T Accept<T>(IFieldVisitor<T> visitor);

18
src/Squidex.Domain.Apps.Core.Model/Schemas/IFieldSettings.cs

@ -0,0 +1,18 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Core.Schemas
{
public interface IFieldSettings
{
bool IsLocked { get; }
bool IsDisabled { get; }
bool IsHidden { get; }
}
}

5
src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonFieldModel.cs

@ -5,12 +5,11 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Squidex.Domain.Apps.Core.Schemas.Json namespace Squidex.Domain.Apps.Core.Schemas.Json
{ {
public sealed class JsonFieldModel public sealed class JsonFieldModel : IFieldSettings
{ {
[JsonProperty] [JsonProperty]
public long Id { get; set; } public long Id { get; set; }
@ -34,6 +33,6 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
public FieldProperties Properties { get; set; } public FieldProperties Properties { get; set; }
[JsonProperty] [JsonProperty]
public List<JsonNestedFieldModel> Children { get; set; } public JsonNestedFieldModel[] Children { get; set; }
} }
} }

7
src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs

@ -9,7 +9,7 @@ using Newtonsoft.Json;
namespace Squidex.Domain.Apps.Core.Schemas.Json namespace Squidex.Domain.Apps.Core.Schemas.Json
{ {
public sealed class JsonNestedFieldModel public sealed class JsonNestedFieldModel : IFieldSettings
{ {
[JsonProperty] [JsonProperty]
public long Id { get; set; } public long Id { get; set; }
@ -25,5 +25,10 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
[JsonProperty] [JsonProperty]
public FieldProperties Properties { get; set; } public FieldProperties Properties { get; set; }
public bool IsLocked
{
get { return false; }
}
} }
} }

66
src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs

@ -5,15 +5,16 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas.Json namespace Squidex.Domain.Apps.Core.Schemas.Json
{ {
public sealed class JsonSchemaModel public sealed class JsonSchemaModel
{ {
private static readonly RootField[] Empty = new RootField[0]; private static readonly RootField[] Empty = Array.Empty<RootField>();
[JsonProperty] [JsonProperty]
public string Name { get; set; } public string Name { get; set; }
@ -25,7 +26,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
public SchemaProperties Properties { get; set; } public SchemaProperties Properties { get; set; }
[JsonProperty] [JsonProperty]
public List<JsonFieldModel> Fields { get; set; } public JsonFieldModel[] Fields { get; set; }
public JsonSchemaModel() public JsonSchemaModel()
{ {
@ -38,7 +39,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
Properties = schema.Properties; Properties = schema.Properties;
Fields = Fields =
schema.Fields.Select(x => schema.Fields.ToArray(x =>
new JsonFieldModel new JsonFieldModel
{ {
Id = x.Id, Id = x.Id,
@ -49,16 +50,16 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
IsDisabled = x.IsDisabled, IsDisabled = x.IsDisabled,
Partitioning = x.Partitioning.Key, Partitioning = x.Partitioning.Key,
Properties = x.RawProperties Properties = x.RawProperties
}).ToList(); });
IsPublished = schema.IsPublished; IsPublished = schema.IsPublished;
} }
private static List<JsonNestedFieldModel> CreateChildren(IField field) private static JsonNestedFieldModel[] CreateChildren(IField field)
{ {
if (field is ArrayField arrayField) if (field is ArrayField arrayField)
{ {
return arrayField.Fields.Select(x => return arrayField.Fields.ToArray(x =>
new JsonNestedFieldModel new JsonNestedFieldModel
{ {
Id = x.Id, Id = x.Id,
@ -66,7 +67,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
IsHidden = x.IsHidden, IsHidden = x.IsHidden,
IsDisabled = x.IsDisabled, IsDisabled = x.IsDisabled,
Properties = x.RawProperties Properties = x.RawProperties
}).ToList(); });
} }
return null; return null;
@ -78,55 +79,28 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
if (Fields != null) if (Fields != null)
{ {
fields = new RootField[Fields.Count]; fields = Fields.ToArray(fieldModel =>
for (var i = 0; i < fields.Length; i++)
{ {
var fieldModel = Fields[i];
var parititonKey = new Partitioning(fieldModel.Partitioning); var parititonKey = new Partitioning(fieldModel.Partitioning);
var field = registry.CreateRootField(fieldModel.Id, fieldModel.Name, parititonKey, fieldModel.Properties); RootField field;
if (field is ArrayField arrayField && fieldModel.Children?.Count > 0) if (fieldModel.Properties is ArrayFieldProperties arrayProperties && fieldModel.Children?.Length > 0)
{ {
foreach (var nestedFieldModel in fieldModel.Children) var nestedFields = fieldModel.Children.ToArray(nestedFieldModel =>
{ {
var nestedField = registry.CreateNestedField(nestedFieldModel.Id, nestedFieldModel.Name, nestedFieldModel.Properties); return registry.CreateNestedField(nestedFieldModel.Id, nestedFieldModel.Name, nestedFieldModel.Properties, nestedFieldModel);
});
if (nestedFieldModel.IsHidden)
{
nestedField = nestedField.Hide();
}
if (nestedFieldModel.IsDisabled)
{
nestedField = nestedField.Disable();
}
arrayField = arrayField.AddField(nestedField);
}
field = arrayField;
}
if (fieldModel.IsDisabled)
{
field = field.Disable();
}
if (fieldModel.IsLocked) field = new ArrayField(fieldModel.Id, fieldModel.Name, parititonKey, nestedFields, arrayProperties, fieldModel);
{
field = field.Lock();
} }
else
if (fieldModel.IsHidden)
{ {
field = field.Hide(); field = registry.CreateRootField(fieldModel.Id, fieldModel.Name, parititonKey, fieldModel.Properties, fieldModel);
} }
fields[i] = field; return field;
} });
} }
return new Schema(Name, fields, Properties, IsPublished); return new Schema(Name, fields, Properties, IsPublished);

8
src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs

@ -22,14 +22,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IField<JsonFieldProperties>)field); return visitor.Visit((IField<JsonFieldProperties>)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.Json(id, name, partitioning, this); return Fields.Json(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return Fields.Json(id, name, this); return Fields.Json(id, name, this, settings);
} }
} }
} }

9
src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs

@ -45,13 +45,20 @@ namespace Squidex.Domain.Apps.Core.Schemas
public abstract FieldProperties RawProperties { get; } public abstract FieldProperties RawProperties { get; }
protected NestedField(long id, string name) protected NestedField(long id, string name, IFieldSettings settings = null)
{ {
Guard.NotNullOrEmpty(name, nameof(name)); Guard.NotNullOrEmpty(name, nameof(name));
Guard.GreaterThan(id, 0, nameof(id)); Guard.GreaterThan(id, 0, nameof(id));
fieldId = id; fieldId = id;
fieldName = name; fieldName = name;
if (settings != null)
{
isLocked = settings.IsLocked;
isHidden = settings.IsHidden;
isDisabled = settings.IsDisabled;
}
} }
[Pure] [Pure]

8
src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs

@ -25,12 +25,10 @@ namespace Squidex.Domain.Apps.Core.Schemas
get { return properties; } get { return properties; }
} }
public NestedField(long id, string name, T properties) public NestedField(long id, string name, T properties = null, IFieldSettings settings = null)
: base(id, name) : base(id, name, settings)
{ {
Guard.NotNull(properties, nameof(properties)); SetProperties(properties ?? new T());
SetProperties(properties);
} }
[Pure] [Pure]

8
src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs

@ -35,14 +35,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IField<NumberFieldProperties>)field); return visitor.Visit((IField<NumberFieldProperties>)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.Number(id, name, partitioning, this); return Fields.Number(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return Fields.Number(id, name, this); return Fields.Number(id, name, this, settings);
} }
} }
} }

8
src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs

@ -29,14 +29,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IField<ReferencesFieldProperties>)field); return visitor.Visit((IField<ReferencesFieldProperties>)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.References(id, name, partitioning, this); return Fields.References(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return Fields.References(id, name, this); return Fields.References(id, name, this, settings);
} }
} }
} }

9
src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs

@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
public abstract FieldProperties RawProperties { get; } public abstract FieldProperties RawProperties { get; }
protected RootField(long id, string name, Partitioning partitioning) protected RootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
Guard.NotNullOrEmpty(name, nameof(name)); Guard.NotNullOrEmpty(name, nameof(name));
Guard.GreaterThan(id, 0, nameof(id)); Guard.GreaterThan(id, 0, nameof(id));
@ -61,6 +61,13 @@ namespace Squidex.Domain.Apps.Core.Schemas
fieldName = name; fieldName = name;
this.partitioning = partitioning; this.partitioning = partitioning;
if (settings != null)
{
isLocked = settings.IsLocked;
isHidden = settings.IsHidden;
isDisabled = settings.IsDisabled;
}
} }
[Pure] [Pure]

8
src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs

@ -25,12 +25,10 @@ namespace Squidex.Domain.Apps.Core.Schemas
get { return properties; } get { return properties; }
} }
public RootField(long id, string name, Partitioning partitioning, T properties) public RootField(long id, string name, Partitioning partitioning, T properties = null, IFieldSettings settings = null)
: base(id, name, partitioning) : base(id, name, partitioning, settings)
{ {
Guard.NotNull(properties, nameof(properties)); SetProperties(properties ?? new T());
SetProperties(properties);
} }
[Pure] [Pure]

8
src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs

@ -39,14 +39,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IField<StringFieldProperties>)field); return visitor.Visit((IField<StringFieldProperties>)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.String(id, name, partitioning, this); return Fields.String(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return Fields.String(id, name, this); return Fields.String(id, name, this, settings);
} }
} }
} }

8
src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs

@ -33,14 +33,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
return visitor.Visit((IField<TagsFieldProperties>)field); return visitor.Visit((IField<TagsFieldProperties>)field);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return Fields.Tags(id, name, partitioning, this); return Fields.Tags(id, name, partitioning, this, settings);
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return Fields.Tags(id, name, this); return Fields.Tags(id, name, this, settings);
} }
} }
} }

2
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs

@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var result = await Task.WhenAll(queries.Select(q => QueryInternalAsync(model, ctx, q))); var result = await Task.WhenAll(queries.Select(q => QueryInternalAsync(model, ctx, q)));
return (result.Any(x => x.HasError), result.Select(x => x.Response).ToArray()); return (result.Any(x => x.HasError), result.ToArray(x => x.Response));
} }
public async Task<(bool HasError, object Response)> QueryAsync(QueryContext context, GraphQLQuery query) public async Task<(bool HasError, object Response)> QueryAsync(QueryContext context, GraphQLQuery query)

26
src/Squidex.Infrastructure/CollectionExtensions.cs

@ -62,6 +62,32 @@ namespace Squidex.Infrastructure
return source.Concat(Enumerable.Repeat(value, 1)); return source.Concat(Enumerable.Repeat(value, 1));
} }
public static TResult[] ToArray<TResult, T>(this T[] value, Func<T, TResult> convert)
{
var result = new TResult[value.Length];
for (var i = 0; i < value.Length; i++)
{
result[i] = convert(value[i]);
}
return result;
}
public static TResult[] ToArray<TResult, T>(this IReadOnlyCollection<T> value, Func<T, TResult> convert)
{
var result = new TResult[value.Count];
var i = 0;
foreach (var v in value)
{
result[i] = convert(v);
i++;
}
return result;
}
public static int SequentialHashCode<T>(this IEnumerable<T> collection) public static int SequentialHashCode<T>(this IEnumerable<T> collection)
{ {
return collection.SequentialHashCode(EqualityComparer<T>.Default); return collection.SequentialHashCode(EqualityComparer<T>.Default);

2
src/Squidex.Infrastructure/Language.cs

@ -35,7 +35,7 @@ namespace Squidex.Infrastructure
} }
} }
public static IEnumerable<Language> AllLanguages public static IReadOnlyCollection<Language> AllLanguages
{ {
get { return AllLanguagesField.Values; } get { return AllLanguagesField.Values; }
} }

5
src/Squidex.Infrastructure/Log/IArrayWriter.cs

@ -12,12 +12,17 @@ namespace Squidex.Infrastructure.Log
public interface IArrayWriter public interface IArrayWriter
{ {
IArrayWriter WriteValue(string value); IArrayWriter WriteValue(string value);
IArrayWriter WriteValue(double value); IArrayWriter WriteValue(double value);
IArrayWriter WriteValue(long value); IArrayWriter WriteValue(long value);
IArrayWriter WriteValue(bool value); IArrayWriter WriteValue(bool value);
IArrayWriter WriteValue(TimeSpan value); IArrayWriter WriteValue(TimeSpan value);
IArrayWriter WriteValue(DateTime value); IArrayWriter WriteValue(DateTime value);
IArrayWriter WriteValue(DateTimeOffset value); IArrayWriter WriteValue(DateTimeOffset value);
IArrayWriter WriteObject(Action<IObjectWriter> objectWriter); IArrayWriter WriteObject(Action<IObjectWriter> objectWriter);

6
src/Squidex.Infrastructure/Log/IObjectWriter.cs

@ -12,15 +12,21 @@ namespace Squidex.Infrastructure.Log
public interface IObjectWriter public interface IObjectWriter
{ {
IObjectWriter WriteProperty(string property, string value); IObjectWriter WriteProperty(string property, string value);
IObjectWriter WriteProperty(string property, double value); IObjectWriter WriteProperty(string property, double value);
IObjectWriter WriteProperty(string property, long value); IObjectWriter WriteProperty(string property, long value);
IObjectWriter WriteProperty(string property, bool value); IObjectWriter WriteProperty(string property, bool value);
IObjectWriter WriteProperty(string property, TimeSpan value); IObjectWriter WriteProperty(string property, TimeSpan value);
IObjectWriter WriteProperty(string property, DateTime value); IObjectWriter WriteProperty(string property, DateTime value);
IObjectWriter WriteProperty(string property, DateTimeOffset value); IObjectWriter WriteProperty(string property, DateTimeOffset value);
IObjectWriter WriteObject(string property, Action<IObjectWriter> objectWriter); IObjectWriter WriteObject(string property, Action<IObjectWriter> objectWriter);
IObjectWriter WriteArray(string property, Action<IArrayWriter> arrayWriter); IObjectWriter WriteArray(string property, Action<IArrayWriter> arrayWriter);
} }
} }

16
src/Squidex.Infrastructure/Log/IObjectWriterFactory.cs

@ -0,0 +1,16 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Infrastructure.Log
{
public interface IObjectWriterFactory
{
IObjectWriter Create();
void Release(IObjectWriter writer);
}
}

35
src/Squidex.Infrastructure/Log/JsonLogWriter.cs

@ -14,18 +14,37 @@ namespace Squidex.Infrastructure.Log
{ {
public sealed class JsonLogWriter : IObjectWriter, IArrayWriter public sealed class JsonLogWriter : IObjectWriter, IArrayWriter
{ {
private readonly bool extraLine; private readonly Formatting formatting;
private readonly bool formatLine;
private readonly StringWriter textWriter = new StringWriter(); private readonly StringWriter textWriter = new StringWriter();
private readonly JsonWriter jsonWriter; private JsonWriter jsonWriter;
public JsonLogWriter(Formatting formatting = Formatting.None, bool extraLine = false) public int BufferSize
{ {
this.extraLine = extraLine; get { return textWriter.GetStringBuilder().Capacity; }
}
internal JsonLogWriter(Formatting formatting, bool formatLine)
{
this.formatLine = formatLine;
this.formatting = formatting;
Start();
}
private void Start()
{
jsonWriter = new JsonTextWriter(textWriter) { Formatting = formatting }; jsonWriter = new JsonTextWriter(textWriter) { Formatting = formatting };
jsonWriter.WriteStartObject(); jsonWriter.WriteStartObject();
} }
internal void Reset()
{
textWriter.GetStringBuilder().Clear();
Start();
}
IArrayWriter IArrayWriter.WriteValue(string value) IArrayWriter IArrayWriter.WriteValue(string value)
{ {
jsonWriter.WriteValue(value); jsonWriter.WriteValue(value);
@ -170,14 +189,12 @@ namespace Squidex.Infrastructure.Log
{ {
jsonWriter.WriteEndObject(); jsonWriter.WriteEndObject();
var result = textWriter.ToString(); if (formatLine)
if (extraLine)
{ {
result += Environment.NewLine; textWriter.WriteLine();
} }
return result; return textWriter.ToString();
} }
} }
} }

61
src/Squidex.Infrastructure/Log/JsonLogWriterFactory.cs

@ -0,0 +1,61 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Concurrent;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Log
{
public sealed class JsonLogWriterFactory : IObjectWriterFactory
{
private const int MaxPoolSize = 10;
private const int MaxCapacity = 5000;
private readonly ConcurrentStack<JsonLogWriter> pool = new ConcurrentStack<JsonLogWriter>();
private readonly Formatting formatting;
private readonly bool formatLine;
public JsonLogWriterFactory(Formatting formatting = Formatting.None, bool formatLine = false)
{
this.formatting = formatting;
this.formatLine = formatLine;
}
public static JsonLogWriterFactory Default()
{
return new JsonLogWriterFactory();
}
public static JsonLogWriterFactory Readable()
{
return new JsonLogWriterFactory(Formatting.Indented, true);
}
public IObjectWriter Create()
{
if (pool.TryPop(out var writer))
{
writer.Reset();
}
else
{
writer = new JsonLogWriter(formatting, formatLine);
}
return writer;
}
public void Release(IObjectWriter writer)
{
var jsonWriter = (JsonLogWriter)writer;
if (pool.Count < MaxPoolSize && jsonWriter.BufferSize < MaxCapacity)
{
pool.Push(jsonWriter);
}
}
}
}

38
src/Squidex.Infrastructure/Log/SemanticLog.cs

@ -13,20 +13,21 @@ namespace Squidex.Infrastructure.Log
{ {
public sealed class SemanticLog : ISemanticLog public sealed class SemanticLog : ISemanticLog
{ {
private readonly IEnumerable<ILogChannel> channels; private readonly ILogChannel[] channels;
private readonly IEnumerable<ILogAppender> appenders; private readonly ILogAppender[] appenders;
private readonly Func<IObjectWriter> writerFactory; private readonly IObjectWriterFactory writerFactory;
public SemanticLog( public SemanticLog(
IEnumerable<ILogChannel> channels, IEnumerable<ILogChannel> channels,
IEnumerable<ILogAppender> appenders, IEnumerable<ILogAppender> appenders,
Func<IObjectWriter> writerFactory) IObjectWriterFactory writerFactory)
{ {
Guard.NotNull(channels, nameof(channels)); Guard.NotNull(channels, nameof(channels));
Guard.NotNull(appenders, nameof(appenders)); Guard.NotNull(appenders, nameof(appenders));
Guard.NotNull(writerFactory, nameof(writerFactory));
this.channels = channels; this.channels = channels.ToArray();
this.appenders = appenders; this.appenders = appenders.ToArray();
this.writerFactory = writerFactory; this.writerFactory = writerFactory;
} }
@ -38,11 +39,11 @@ namespace Squidex.Infrastructure.Log
List<Exception> exceptions = null; List<Exception> exceptions = null;
foreach (var channel in channels) for (var i = 0; i < channels.Length; i++)
{ {
try try
{ {
channel.Log(logLevel, formattedText); channels[i].Log(logLevel, formattedText);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -63,18 +64,25 @@ namespace Squidex.Infrastructure.Log
private string FormatText(SemanticLogLevel logLevel, Action<IObjectWriter> objectWriter) private string FormatText(SemanticLogLevel logLevel, Action<IObjectWriter> objectWriter)
{ {
var writer = writerFactory(); var writer = writerFactory.Create();
writer.WriteProperty(nameof(logLevel), logLevel.ToString()); try
{
writer.WriteProperty(nameof(logLevel), logLevel.ToString());
objectWriter(writer);
objectWriter(writer); for (var i = 0; i < appenders.Length; i++)
{
appenders[i].Append(writer);
}
foreach (var appender in appenders) return writer.ToString();
}
finally
{ {
appender.Append(writer); writerFactory.Release(writer);
} }
return writer.ToString();
} }
public ISemanticLog CreateScope(Action<IObjectWriter> objectWriter) public ISemanticLog CreateScope(Action<IObjectWriter> objectWriter)

2
src/Squidex.Infrastructure/Security/PermissionSet.cs

@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.Security
{ {
public sealed class PermissionSet : IReadOnlyCollection<Permission> public sealed class PermissionSet : IReadOnlyCollection<Permission>
{ {
public static readonly PermissionSet Empty = new PermissionSet(new string[0]); public static readonly PermissionSet Empty = new PermissionSet(Array.Empty<string>());
private readonly List<Permission> permissions; private readonly List<Permission> permissions;
private readonly Lazy<string> display; private readonly Lazy<string> display;

3
src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
#pragma warning disable RECS0012 // 'if' statement can be re-written as 'switch' statement #pragma warning disable RECS0012 // 'if' statement can be re-written as 'switch' statement
@ -190,7 +191,7 @@ namespace Squidex.Infrastructure.States
private EventData[] GetEventData(Envelope<IEvent>[] events, Guid commitId) private EventData[] GetEventData(Envelope<IEvent>[] events, Guid commitId)
{ {
return events.Select(x => eventDataFormatter.ToEventData(x, commitId, true)).ToArray(); return events.ToArray(x => eventDataFormatter.ToEventData(x, commitId, true));
} }
private string GetStreamName() private string GetStreamName()

2
src/Squidex.Infrastructure/ValidationError.cs

@ -14,7 +14,7 @@ namespace Squidex.Infrastructure
[Serializable] [Serializable]
public sealed class ValidationError public sealed class ValidationError
{ {
private static readonly string[] FallbackProperties = new string[0]; private static readonly string[] FallbackProperties = Array.Empty<string>();
private readonly string message; private readonly string message;
private readonly string[] propertyNames; private readonly string[] propertyNames;

3
src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Squidex.Areas.Api.Controllers.Apps.Models; using Squidex.Areas.Api.Controllers.Apps.Models;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Shared; using Squidex.Shared;
@ -45,7 +46,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ApiCosts(0)] [ApiCosts(0)]
public IActionResult GetClients(string app) public IActionResult GetClients(string app)
{ {
var response = App.Clients.Select(ClientDto.FromKvp).ToList(); var response = App.Clients.Select(ClientDto.FromKvp).ToArray();
Response.Headers["ETag"] = App.Version.ToString(); Response.Headers["ETag"] = App.Version.ToString();

2
src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs

@ -46,7 +46,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ApiCosts(0)] [ApiCosts(0)]
public IActionResult GetPatterns(string app) public IActionResult GetPatterns(string app)
{ {
var response = App.Patterns.Select(AppPatternDto.FromKvp).OrderBy(x => x.Name).ToList(); var response = App.Patterns.Select(AppPatternDto.FromKvp).OrderBy(x => x.Name).ToArray();
Response.Headers["ETag"] = App.Version.ToString(); Response.Headers["ETag"] = App.Version.ToString();

3
src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs

@ -13,6 +13,7 @@ using Squidex.Areas.Api.Controllers.Apps.Models;
using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Services; using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
using Squidex.Pipeline; using Squidex.Pipeline;
@ -62,7 +63,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
var entities = await appProvider.GetUserApps(userId, userPermissions); var entities = await appProvider.GetUserApps(userId, userPermissions);
var response = entities.Select(a => AppDto.FromApp(a, userId, userPermissions, appPlansProvider)).ToList(); var response = entities.ToArray(a => AppDto.FromApp(a, userId, userPermissions, appPlansProvider));
Response.Headers["ETag"] = response.ToManyEtag(); Response.Headers["ETag"] = response.ToManyEtag();

3
src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs

@ -13,6 +13,7 @@ using Newtonsoft.Json;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services; using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
using Squidex.Pipeline; using Squidex.Pipeline;
@ -80,7 +81,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
var response = SimpleMapper.Map(app, new AppDto()); var response = SimpleMapper.Map(app, new AppDto());
response.Permissions = permissions.Select(x => x.Id).ToArray(); response.Permissions = permissions.ToArray(x => x.Id);
response.PlanName = plans.GetPlanForApp(app)?.Name; response.PlanName = plans.GetPlanForApp(app)?.Name;
response.PlanUpgrade = plans.GetPlanUpgradeForApp(app)?.Name; response.PlanUpgrade = plans.GetPlanUpgradeForApp(app)?.Name;

8
src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs

@ -5,7 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
@ -34,7 +34,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// The fallback languages. /// The fallback languages.
/// </summary> /// </summary>
[Required] [Required]
public List<Language> Fallback { get; set; } public Language[] Fallback { get; set; }
/// <summary> /// <summary>
/// Indicates if the language is the master language. /// Indicates if the language is the master language.
@ -48,7 +48,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
public static AppLanguageDto FromCommand(AddLanguage command) public static AppLanguageDto FromCommand(AddLanguage command)
{ {
return SimpleMapper.Map(command.Language, new AppLanguageDto { Fallback = new List<Language>() }); return SimpleMapper.Map(command.Language, new AppLanguageDto { Fallback = Array.Empty<Language>() });
} }
public static AppLanguageDto[] FromApp(IAppEntity app) public static AppLanguageDto[] FromApp(IAppEntity app)
@ -63,7 +63,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
{ {
IsMaster = x == app.LanguagesConfig.Master, IsMaster = x == app.LanguagesConfig.Master,
IsOptional = x.IsOptional, IsOptional = x.IsOptional,
Fallback = x.LanguageFallbacks.ToList() Fallback = x.LanguageFallbacks.ToArray()
}); });
} }
} }

3
src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs

@ -9,6 +9,7 @@ using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services; using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure;
namespace Squidex.Areas.Api.Controllers.Apps.Models namespace Squidex.Areas.Api.Controllers.Apps.Models
{ {
@ -29,7 +30,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
{ {
var plan = plans.GetPlanForApp(app); var plan = plans.GetPlanForApp(app);
var contributors = app.Contributors.Select(x => new ContributorDto { ContributorId = x.Key, Role = x.Value }).ToArray(); var contributors = app.Contributors.ToArray(x => new ContributorDto { ContributorId = x.Key, Role = x.Value });
return new ContributorsDto { Contributors = contributors, MaxContributors = plan.MaxContributors }; return new ContributorsDto { Contributors = contributors, MaxContributors = plan.MaxContributors };
} }

5
src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs

@ -6,6 +6,7 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
@ -35,7 +36,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// Associated list of permissions. /// Associated list of permissions.
/// </summary> /// </summary>
[Required] [Required]
public string[] Permissions { get; set; } public IEnumerable<string> Permissions { get; set; }
public static RoleDto FromRole(Role role, IAppEntity app) public static RoleDto FromRole(Role role, IAppEntity app)
{ {
@ -46,7 +47,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
Name = role.Name, Name = role.Name,
NumClients = app.Clients.Count(x => string.Equals(x.Value.Role, role.Name, StringComparison.OrdinalIgnoreCase)), NumClients = app.Clients.Count(x => string.Equals(x.Value.Role, role.Name, StringComparison.OrdinalIgnoreCase)),
NumContributors = app.Contributors.Count(x => string.Equals(x.Value, role.Name, StringComparison.OrdinalIgnoreCase)), NumContributors = app.Contributors.Count(x => string.Equals(x.Value, role.Name, StringComparison.OrdinalIgnoreCase)),
Permissions = permissions.ToIds().ToArray() Permissions = permissions.ToIds()
}; };
} }
} }

3
src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateLanguageDto.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
@ -27,7 +26,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary> /// <summary>
/// Optional fallback languages. /// Optional fallback languages.
/// </summary> /// </summary>
public List<Language> Fallback { get; set; } public Language[] Fallback { get; set; }
public UpdateLanguage ToCommand(Language language) public UpdateLanguage ToCommand(Language language)
{ {

3
src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs

@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Mvc;
using Orleans; using Orleans;
using Squidex.Areas.Api.Controllers.Backups.Models; using Squidex.Areas.Api.Controllers.Backups.Models;
using Squidex.Domain.Apps.Entities.Backup; using Squidex.Domain.Apps.Entities.Backup;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Tasks; using Squidex.Infrastructure.Tasks;
using Squidex.Pipeline; using Squidex.Pipeline;
@ -53,7 +54,7 @@ namespace Squidex.Areas.Api.Controllers.Backups
var jobs = await backupGrain.GetStateAsync(); var jobs = await backupGrain.GetStateAsync();
var response = jobs.Value.Select(BackupJobDto.FromBackup).ToList(); var response = jobs.Value.ToArray(BackupJobDto.FromBackup);
return Ok(response); return Ok(response);
} }

8
src/Squidex/Areas/Api/Controllers/Comments/Models/CommentsDto.cs

@ -17,12 +17,12 @@ namespace Squidex.Areas.Api.Controllers.Comments.Models
/// <summary> /// <summary>
/// The created comments including the updates. /// The created comments including the updates.
/// </summary> /// </summary>
public List<CommentDto> CreatedComments { get; set; } public CommentDto[] CreatedComments { get; set; }
/// <summary> /// <summary>
/// The updates comments since the last version. /// The updates comments since the last version.
/// </summary> /// </summary>
public List<CommentDto> UpdatedComments { get; set; } public CommentDto[] UpdatedComments { get; set; }
/// <summary> /// <summary>
/// The deleted comments since the last version. /// The deleted comments since the last version.
@ -38,8 +38,8 @@ namespace Squidex.Areas.Api.Controllers.Comments.Models
{ {
return new CommentsDto return new CommentsDto
{ {
CreatedComments = result.CreatedComments.Select(CommentDto.FromComment).ToList(), CreatedComments = result.CreatedComments.Select(CommentDto.FromComment).ToArray(),
UpdatedComments = result.UpdatedComments.Select(CommentDto.FromComment).ToList(), UpdatedComments = result.UpdatedComments.Select(CommentDto.FromComment).ToArray(),
DeletedComments = result.DeletedComments, DeletedComments = result.DeletedComments,
Version = result.Version Version = result.Version
}; };

1
src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.GraphQL; using Squidex.Domain.Apps.Entities.Contents.GraphQL;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Shared; using Squidex.Shared;

2
src/Squidex/Areas/Api/Controllers/EventConsumers/EventConsumersController.cs

@ -35,7 +35,7 @@ namespace Squidex.Areas.Api.Controllers.EventConsumers
{ {
var entities = await GetGrain().GetConsumersAsync(); var entities = await GetGrain().GetConsumersAsync();
var response = entities.Value.OrderBy(x => x.Name).Select(EventConsumerDto.FromEventConsumerInfo).ToList(); var response = entities.Value.OrderBy(x => x.Name).Select(EventConsumerDto.FromEventConsumerInfo).ToArray();
return Ok(response); return Ok(response);
} }

3
src/Squidex/Areas/Api/Controllers/History/HistoryController.cs

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Squidex.Areas.Api.Controllers.History.Models; using Squidex.Areas.Api.Controllers.History.Models;
using Squidex.Domain.Apps.Entities.History; using Squidex.Domain.Apps.Entities.History;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Shared; using Squidex.Shared;
@ -48,7 +49,7 @@ namespace Squidex.Areas.Api.Controllers.History
{ {
var entities = await historyService.QueryByChannelAsync(AppId, channel, 100); var entities = await historyService.QueryByChannelAsync(AppId, channel, 100);
var response = entities.Select(HistoryEventDto.FromHistoryEvent).ToList(); var response = entities.ToArray(HistoryEventDto.FromHistoryEvent);
return Ok(response); return Ok(response);
} }

2
src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs

@ -39,7 +39,7 @@ namespace Squidex.Areas.Api.Controllers.Languages
[ApiPermission] [ApiPermission]
public IActionResult GetLanguages() public IActionResult GetLanguages()
{ {
var response = Language.AllLanguages.Select(LanguageDto.FromLanguage).ToList(); var response = Language.AllLanguages.Select(LanguageDto.FromLanguage).ToArray();
Response.Headers["Etag"] = "1"; Response.Headers["Etag"] = "1";

5
src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
@ -19,7 +18,7 @@ namespace Squidex.Areas.Api.Controllers.Plans.Models
/// The available plans. /// The available plans.
/// </summary> /// </summary>
[Required] [Required]
public List<PlanDto> Plans { get; set; } public PlanDto[] Plans { get; set; }
/// <summary> /// <summary>
/// The current plan id. /// The current plan id.
@ -43,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.Plans.Models
var response = new AppPlansDto var response = new AppPlansDto
{ {
CurrentPlanId = planId, CurrentPlanId = planId,
Plans = plans.GetAvailablePlans().Select(PlanDto.FromPlan).ToList(), Plans = plans.GetAvailablePlans().Select(PlanDto.FromPlan).ToArray(),
PlanOwner = app.Plan?.Owner.Identifier, PlanOwner = app.Plan?.Owner.Identifier,
HasPortal = hasPortal HasPortal = hasPortal
}; };

2
src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleTriggerDtoFactory.cs

@ -33,7 +33,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Converters
public RuleTriggerDto Visit(ContentChangedTrigger trigger) public RuleTriggerDto Visit(ContentChangedTrigger trigger)
{ {
var schemas = trigger.Schemas.Select(x => SimpleMapper.Map(x, new ContentChangedTriggerSchemaDto())).ToList(); var schemas = trigger.Schemas.Select(x => SimpleMapper.Map(x, new ContentChangedTriggerSchemaDto())).ToArray();
return new ContentChangedTriggerDto { Schemas = schemas, HandleAll = trigger.HandleAll }; return new ContentChangedTriggerDto { Schemas = schemas, HandleAll = trigger.HandleAll };
} }

1
src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Squidex.Domain.Apps.Entities.Rules; using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Infrastructure;
namespace Squidex.Areas.Api.Controllers.Rules.Models namespace Squidex.Areas.Api.Controllers.Rules.Models
{ {

2
src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerDto.cs

@ -23,7 +23,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
/// The schema settings. /// The schema settings.
/// </summary> /// </summary>
[Required] [Required]
public List<ContentChangedTriggerSchemaDto> Schemas { get; set; } public ContentChangedTriggerSchemaDto[] Schemas { get; set; }
/// <summary> /// <summary>
/// Determines whether the trigger should handle all content changes events. /// Determines whether the trigger should handle all content changes events.

2
src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs

@ -101,7 +101,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
{ {
var entities = await appProvider.GetRulesAsync(AppId); var entities = await appProvider.GetRulesAsync(AppId);
var response = entities.Select(RuleDto.FromRule); var response = entities.Select(RuleDto.FromRule).ToArray();
Response.Headers["ETag"] = response.ToManyEtag(0); Response.Headers["ETag"] = response.ToManyEtag(0);

3
src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs

@ -13,6 +13,7 @@ using Squidex.Areas.Api.Controllers.Schemas.Models;
using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Shared; using Squidex.Shared;
@ -50,7 +51,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
{ {
var schemas = await appProvider.GetSchemasAsync(AppId); var schemas = await appProvider.GetSchemasAsync(AppId);
var response = schemas.Select(SchemaDto.FromSchema).ToList(); var response = schemas.ToArray(SchemaDto.FromSchema);
Response.Headers["ETag"] = response.ToManyEtag(); Response.Headers["ETag"] = response.ToManyEtag();

5
src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs

@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Mvc;
using Squidex.Areas.Api.Controllers.Statistics.Models; using Squidex.Areas.Api.Controllers.Statistics.Models;
using Squidex.Domain.Apps.Entities.Apps.Services; using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.UsageTracking; using Squidex.Infrastructure.UsageTracking;
using Squidex.Pipeline; using Squidex.Pipeline;
@ -92,7 +93,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics
var entities = await usageTracker.QueryAsync(AppId.ToString(), fromDate.Date, toDate.Date); var entities = await usageTracker.QueryAsync(AppId.ToString(), fromDate.Date, toDate.Date);
var response = entities.ToDictionary(x => x.Key, x => x.Value.Select(CallsUsageDto.FromUsage).ToList()); var response = entities.ToDictionary(x => x.Key, x => x.Value.Select(CallsUsageDto.FromUsage).ToArray());
return Ok(response); return Ok(response);
} }
@ -146,7 +147,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics
var entities = await assetStatsRepository.QueryAsync(AppId, fromDate.Date, toDate.Date); var entities = await assetStatsRepository.QueryAsync(AppId, fromDate.Date, toDate.Date);
var models = entities.Select(StorageUsageDto.FromStats).ToList(); var models = entities.Select(StorageUsageDto.FromStats).ToArray();
return Ok(models); return Ok(models);
} }

6
src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs

@ -5,8 +5,8 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Shared.Users; using Squidex.Shared.Users;
@ -42,11 +42,11 @@ namespace Squidex.Areas.Api.Controllers.Users.Models
/// Additional permissions for the user. /// Additional permissions for the user.
/// </summary> /// </summary>
[Required] [Required]
public string[] Permissions { get; set; } public IEnumerable<string> Permissions { get; set; }
public static UserDto FromUser(IUser user) public static UserDto FromUser(IUser user)
{ {
var permissions = user.Permissions().ToIds().ToArray(); var permissions = user.Permissions().ToIds();
return SimpleMapper.Map(user, new UserDto { DisplayName = user.DisplayName(), Permissions = permissions }); return SimpleMapper.Map(user, new UserDto { DisplayName = user.DisplayName(), Permissions = permissions });
} }

2
src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs

@ -18,7 +18,7 @@ namespace Squidex.Areas.Frontend.Middlewares
private const string Host = "localhost"; private const string Host = "localhost";
private const string Port = "3000"; private const string Port = "3000";
private static readonly string[] Scripts = { "shims.js", "app.js" }; private static readonly string[] Scripts = { "shims.js", "app.js" };
private static readonly string[] Styles = new string[0]; private static readonly string[] Styles = Array.Empty<string>();
private readonly RequestDelegate next; private readonly RequestDelegate next;
public WebpackMiddleware(RequestDelegate next) public WebpackMiddleware(RequestDelegate next)

7
src/Squidex/Config/Domain/LoggingServices.cs

@ -8,7 +8,6 @@
using System; using System;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using Squidex.Pipeline; using Squidex.Pipeline;
@ -25,11 +24,13 @@ namespace Squidex.Config.Domain
{ {
if (config.GetValue<bool>("logging:human")) if (config.GetValue<bool>("logging:human"))
{ {
services.AddSingletonAs(c => new Func<IObjectWriter>(() => new JsonLogWriter(Formatting.Indented, true))); services.AddSingletonAs(JsonLogWriterFactory.Readable())
.As<IObjectWriterFactory>();
} }
else else
{ {
services.AddSingletonAs(c => new Func<IObjectWriter>(() => new JsonLogWriter())); services.AddSingletonAs(JsonLogWriterFactory.Default())
.As<IObjectWriterFactory>();
} }
var loggingFile = config.GetValue<string>("logging:file"); var loggingFile = config.GetValue<string>("logging:file");

1
src/Squidex/Config/Web/WebExtensions.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using Squidex.Infrastructure.Diagnostics; using Squidex.Infrastructure.Diagnostics;

2
src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs

@ -61,7 +61,7 @@ namespace Squidex.Pipeline
private static IActionResult OnValidationException(ValidationException ex) private static IActionResult OnValidationException(ValidationException ex)
{ {
return ErrorResult(400, new ErrorDto { Message = ex.Summary, Details = ex.Errors?.Select(e => e.Message).ToArray() }); return ErrorResult(400, new ErrorDto { Message = ex.Summary, Details = ex.Errors?.ToArray(e => e.Message) });
} }
private static IActionResult ErrorResult(int statusCode, ErrorDto error) private static IActionResult ErrorResult(int statusCode, ErrorDto error)

53
src/Squidex/Pipeline/ETagExtensions.cs

@ -5,8 +5,9 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Text;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
@ -14,17 +15,59 @@ namespace Squidex.Pipeline
{ {
public static class ETagExtensions public static class ETagExtensions
{ {
public static string ToManyEtag<T>(this IEnumerable<T> items, long total = 0) where T : IGenerateEtag private static readonly int GuidLength = Guid.Empty.ToString().Length;
public static string ToManyEtag<T>(this IReadOnlyList<T> items, long total = 0) where T : IGenerateEtag
{ {
using (Profiler.Trace("CalculateEtag")) using (Profiler.Trace("CalculateEtag"))
{ {
return $"{total}_{string.Join(";", items.Select(x => $"{x.Id}{x.Version}"))}".Sha256Base64(); var unhashed = Unhashed(items, total);
return unhashed.Sha256Base64();
} }
} }
public static string ToSurrogateKeys<T>(this IEnumerable<T> items) where T : IGenerateEtag private static string Unhashed<T>(IReadOnlyList<T> items, long total) where T : IGenerateEtag
{ {
return string.Join(" ", items.Select(x => x.Id)); var sb = new StringBuilder((items.Count * (GuidLength + 4)) + 10);
sb.Append(total);
sb.Append("_");
if (items.Count > 0)
{
sb.Append(items[0].Id.ToString());
sb.Append(items[0].Version);
for (var i = 1; i < items.Count; i++)
{
sb.Append(";");
sb.Append(items[i].Id.ToString());
sb.Append(items[i].Version);
}
}
return sb.ToString();
}
public static string ToSurrogateKeys<T>(this IReadOnlyList<T> items) where T : IGenerateEtag
{
if (items.Count == 0)
{
return string.Empty;
}
var sb = new StringBuilder(items.Count * (GuidLength + 1));
sb.Append(items[0].Id.ToString());
for (var i = 1; i < items.Count; i++)
{
sb.Append(" ");
sb.Append(items[i].Id.ToString());
}
return sb.ToString();
} }
public static string ToEtag<T>(this T item) where T : IGenerateEtag public static string ToEtag<T>(this T item) where T : IGenerateEtag

75
src/Squidex/app/theme/icomoon/icons/control-Tags.svg

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 34.5 31"
style="enable-background:new 0 0 34.5 31;"
xml:space="preserve"
sodipodi:docname="EditTag-icon.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata19"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs17" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1278"
inkscape:window-height="1008"
id="namedview15"
showgrid="false"
inkscape:zoom="10.766271"
inkscape:cx="14.902269"
inkscape:cy="19.022022"
inkscape:window-x="1913"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:#B3BBBF;}
</style>
<g
id="g6"
style="fill:#000000">
<path
class="st0"
d="M 15.1,10 V 8.5 h 3.4 v -2 C 18.5,5 17.3,3.8 15.8,3.8 14.3,3.8 13.1,5 13.1,6.5 v 16.4 l 2.7,4.9 2.7,-4.9 V 10 Z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#000000" />
</g>
<g
id="g12"
style="fill:#000000">
<path
class="st0"
d="m 10.9,21.7 h -2 C 7.1,21.7 5.7,20.3 5.7,18.5 V 10.8 C 5.7,9 7.1,7.6 8.9,7.6 h 2 V 6.3 h -2 c -2.5,0 -4.5,2 -4.5,4.5 v 7.7 c 0,2.5 2,4.5 4.5,4.5 h 2 z"
id="path8"
inkscape:connector-curvature="0"
style="fill:#000000" />
<path
class="st0"
d="M 25.8,7.9 C 25,6.9 23.8,6.3 22.5,6.3 h -1.8 v 1.3 h 1.2 c 1.3,0 2.5,0.6 3.3,1.6 l 4.4,5.5 -4.3,5.4 c -0.8,1 -2.1,1.6 -3.4,1.6 H 20.8 V 23 h 1.7 c 1.3,0 2.6,-0.6 3.4,-1.6 l 5.4,-6.7 z"
id="path10"
inkscape:connector-curvature="0"
style="fill:#000000" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

4
tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldRegistryTests.cs

@ -28,12 +28,12 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
return default(T); return default(T);
} }
public override RootField CreateRootField(long id, string name, Partitioning partitioning) public override RootField CreateRootField(long id, string name, Partitioning partitioning, IFieldSettings settings = null)
{ {
return null; return null;
} }
public override NestedField CreateNestedField(long id, string name) public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null)
{ {
return null; return null;
} }

3
tests/Squidex.Domain.Apps.Entities.Tests/Apps/RoleExtensionsRests.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Linq; using System.Linq;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
using Xunit; using Xunit;
@ -16,7 +17,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
[Fact] [Fact]
public void Should_add_common_permission() public void Should_add_common_permission()
{ {
var source = new string[0]; var source = Array.Empty<string>();
var result = source.Prefix("my-app"); var result = source.Prefix("my-app");
Assert.Equal(new[] { "squidex.apps.my-app.common" }, result); Assert.Equal(new[] { "squidex.apps.my-app.common" }, result);

10
tests/Squidex.Infrastructure.Tests/Log/JsonLogWriterTests.cs

@ -13,7 +13,7 @@ namespace Squidex.Infrastructure.Log
{ {
public class JsonLogWriterTests public class JsonLogWriterTests
{ {
private readonly IObjectWriter sut = new JsonLogWriter(); private readonly IObjectWriter sut = JsonLogWriterFactory.Default().Create();
[Fact] [Fact]
public void Should_write_boolean_property() public void Should_write_boolean_property()
@ -59,6 +59,7 @@ namespace Squidex.Infrastructure.Log
public void Should_write_datetimeoffset_property() public void Should_write_datetimeoffset_property()
{ {
var value = DateTimeOffset.UtcNow; var value = DateTimeOffset.UtcNow;
var result = sut.WriteProperty("property", value).ToString(); var result = sut.WriteProperty("property", value).ToString();
Assert.Equal($"{{\"property\":\"{value:o}\"}}", result); Assert.Equal($"{{\"property\":\"{value:o}\"}}", result);
@ -68,6 +69,7 @@ namespace Squidex.Infrastructure.Log
public void Should_write_date_property() public void Should_write_date_property()
{ {
var value = DateTime.UtcNow; var value = DateTime.UtcNow;
var result = sut.WriteProperty("property", value).ToString(); var result = sut.WriteProperty("property", value).ToString();
Assert.Equal($"{{\"property\":\"{value:o}\"}}", result); Assert.Equal($"{{\"property\":\"{value:o}\"}}", result);
@ -125,6 +127,7 @@ namespace Squidex.Infrastructure.Log
public void Should_write_datetimeoffset_value() public void Should_write_datetimeoffset_value()
{ {
var value = DateTimeOffset.UtcNow; var value = DateTimeOffset.UtcNow;
var result = sut.WriteArray("property", a => a.WriteValue(value)).ToString(); var result = sut.WriteArray("property", a => a.WriteValue(value)).ToString();
Assert.Equal($"{{\"property\":[\"{value:o}\"]}}", result); Assert.Equal($"{{\"property\":[\"{value:o}\"]}}", result);
@ -134,6 +137,7 @@ namespace Squidex.Infrastructure.Log
public void Should_write_date_value() public void Should_write_date_value()
{ {
var value = DateTime.UtcNow; var value = DateTime.UtcNow;
var result = sut.WriteArray("property", a => a.WriteValue(value)).ToString(); var result = sut.WriteArray("property", a => a.WriteValue(value)).ToString();
Assert.Equal($"{{\"property\":[\"{value:yyyy-MM-ddTHH:mm:ssZ}\"]}}", result); Assert.Equal($"{{\"property\":[\"{value:yyyy-MM-ddTHH:mm:ssZ}\"]}}", result);
@ -150,7 +154,7 @@ namespace Squidex.Infrastructure.Log
[Fact] [Fact]
public void Should_write_pretty_json() public void Should_write_pretty_json()
{ {
IObjectWriter prettySut = new JsonLogWriter(Formatting.Indented); var prettySut = new JsonLogWriterFactory(Formatting.Indented).Create();
var result = prettySut.WriteProperty("property", 1.5).ToString(); var result = prettySut.WriteProperty("property", 1.5).ToString();
@ -160,7 +164,7 @@ namespace Squidex.Infrastructure.Log
[Fact] [Fact]
public void Should_write_extra_line_after_object() public void Should_write_extra_line_after_object()
{ {
IObjectWriter prettySut = new JsonLogWriter(Formatting.None, true); var prettySut = new JsonLogWriterFactory(Formatting.None, true).Create();
var result = prettySut.WriteProperty("property", 1.5).ToString(); var result = prettySut.WriteProperty("property", 1.5).ToString();

4
tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs

@ -38,7 +38,7 @@ namespace Squidex.Infrastructure.Log
output = message; output = message;
}); });
log = new Lazy<SemanticLog>(() => new SemanticLog(channels, new List<ILogAppender>(), () => new JsonLogWriter())); log = new Lazy<SemanticLog>(() => new SemanticLog(channels, new List<ILogAppender>(), JsonLogWriterFactory.Default()));
sut = SemanticLogLoggerProvider.ForTesting(log.Value); sut = SemanticLogLoggerProvider.ForTesting(log.Value);
} }
@ -197,7 +197,7 @@ namespace Squidex.Infrastructure.Log
private static string MakeTestCall(Action<IObjectWriter> writer) private static string MakeTestCall(Action<IObjectWriter> writer)
{ {
IObjectWriter sut = new JsonLogWriter(); var sut = JsonLogWriterFactory.Default().Create();
writer(sut); writer(sut);

29
tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs

@ -21,7 +21,7 @@ namespace Squidex.Infrastructure.Log
private readonly List<ILogChannel> channels = new List<ILogChannel>(); private readonly List<ILogChannel> channels = new List<ILogChannel>();
private readonly Lazy<SemanticLog> log; private readonly Lazy<SemanticLog> log;
private readonly ILogChannel channel = A.Fake<ILogChannel>(); private readonly ILogChannel channel = A.Fake<ILogChannel>();
private string output; private string output = string.Empty;
public SemanticLog Log public SemanticLog Log
{ {
@ -35,10 +35,29 @@ namespace Squidex.Infrastructure.Log
A.CallTo(() => channel.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)) A.CallTo(() => channel.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored))
.Invokes((SemanticLogLevel level, string message) => .Invokes((SemanticLogLevel level, string message) =>
{ {
output = message; output += message;
}); });
log = new Lazy<SemanticLog>(() => new SemanticLog(channels, appenders, () => new JsonLogWriter())); log = new Lazy<SemanticLog>(() => new SemanticLog(channels, appenders, JsonLogWriterFactory.Default()));
}
[Fact]
public void Should_log_multiple_lines()
{
Log.Log(SemanticLogLevel.Error, w => w.WriteProperty("logMessage", "Msg1"));
Log.Log(SemanticLogLevel.Error, w => w.WriteProperty("logMessage", "Msg2"));
var expected1 =
LogTest(w => w
.WriteProperty("logLevel", "Error")
.WriteProperty("logMessage", "Msg1"));
var expected2 =
LogTest(w => w
.WriteProperty("logLevel", "Error")
.WriteProperty("logMessage", "Msg2"));
Assert.Equal(expected1 + expected2, output);
} }
[Fact] [Fact]
@ -307,7 +326,7 @@ namespace Squidex.Infrastructure.Log
A.CallTo(() => channel1.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)).Throws(exception1); A.CallTo(() => channel1.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)).Throws(exception1);
A.CallTo(() => channel2.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)).Throws(exception2); A.CallTo(() => channel2.Log(A<SemanticLogLevel>.Ignored, A<string>.Ignored)).Throws(exception2);
var sut = new SemanticLog(new[] { channel1, channel2 }, Enumerable.Empty<ILogAppender>(), () => new JsonLogWriter()); var sut = new SemanticLog(new[] { channel1, channel2 }, Enumerable.Empty<ILogAppender>(), JsonLogWriterFactory.Default());
try try
{ {
@ -324,7 +343,7 @@ namespace Squidex.Infrastructure.Log
private static string LogTest(Action<IObjectWriter> writer) private static string LogTest(Action<IObjectWriter> writer)
{ {
IObjectWriter sut = new JsonLogWriter(); var sut = JsonLogWriterFactory.Default().Create();
writer(sut); writer(sut);

Loading…
Cancel
Save