diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs index 9b4374720..05ce64873 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Collections.Generic; using System.Linq; using Squidex.Infrastructure; @@ -13,7 +14,7 @@ namespace Squidex.Domain.Apps.Core.Apps { public sealed class LanguageConfig : IFieldPartitionItem { - private static readonly Language[] DefaultFallback = new Language[0]; + private static readonly Language[] DefaultFallback = Array.Empty(); private readonly Language language; private readonly Language[] languageFallbacks; diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs index 7a74a39f0..1de894b81 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Squidex.Infrastructure; using System; using System.Collections.Generic; using System.Diagnostics.Contracts; @@ -30,11 +31,19 @@ namespace Squidex.Domain.Apps.Core.Schemas 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) { } + 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(fields); + } + [Pure] public ArrayField DeleteField(long fieldId) { diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs index f3f6100d9..8a970c2af 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs @@ -27,12 +27,12 @@ namespace Squidex.Domain.Apps.Core.Schemas 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(); } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs index 62b3be7c1..2e39adc21 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs @@ -47,14 +47,14 @@ namespace Squidex.Domain.Apps.Core.Schemas return visitor.Visit((IField)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); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs index a4a0750a5..435f9c529 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs @@ -28,14 +28,14 @@ namespace Squidex.Domain.Apps.Core.Schemas return visitor.Visit((IField)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); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs index efbcad12b..3e79836f0 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs @@ -33,14 +33,14 @@ namespace Squidex.Domain.Apps.Core.Schemas return visitor.Visit((IField)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); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs index a9c8d0421..59aabc380 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs @@ -21,8 +21,8 @@ namespace Squidex.Domain.Apps.Core.Schemas public abstract T Accept(IFieldVisitor 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); } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs index 2cc7be0de..cd32ac6b7 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs +++ b/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); - 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); - return properties.CreateNestedField(id, name); + return properties.CreateNestedField(id, name, settings); } private void CheckProperties(FieldProperties properties) diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs index de6e49e28..5da91b6a1 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs @@ -13,117 +13,107 @@ namespace Squidex.Domain.Apps.Core.Schemas { public static RootField Array(long id, string name, Partitioning partitioning, params NestedField[] fields) { - var result = new ArrayField(id, name, partitioning, new ArrayFieldProperties()); - - if (fields != null) - { - foreach (var field in fields) - { - result = result.AddField(field); - } - } - - return result; + return new ArrayField(id, name, partitioning, fields: fields); } - 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 Assets(long id, string name, Partitioning partitioning, AssetsFieldProperties properties = null) + public static RootField Assets(long id, string name, Partitioning partitioning, AssetsFieldProperties properties = null, IFieldSettings settings = null) { - return new RootField(id, name, partitioning, properties ?? new AssetsFieldProperties()); + return new RootField(id, name, partitioning, properties, settings); } - public static RootField Boolean(long id, string name, Partitioning partitioning, BooleanFieldProperties properties = null) + public static RootField Boolean(long id, string name, Partitioning partitioning, BooleanFieldProperties properties = null, IFieldSettings settings = null) { - return new RootField(id, name, partitioning, properties ?? new BooleanFieldProperties()); + return new RootField(id, name, partitioning, properties, settings); } - public static RootField DateTime(long id, string name, Partitioning partitioning, DateTimeFieldProperties properties = null) + public static RootField DateTime(long id, string name, Partitioning partitioning, DateTimeFieldProperties properties = null, IFieldSettings settings = null) { - return new RootField(id, name, partitioning, properties ?? new DateTimeFieldProperties()); + return new RootField(id, name, partitioning, properties, settings); } - public static RootField Geolocation(long id, string name, Partitioning partitioning, GeolocationFieldProperties properties = null) + public static RootField Geolocation(long id, string name, Partitioning partitioning, GeolocationFieldProperties properties = null, IFieldSettings settings = null) { - return new RootField(id, name, partitioning, properties ?? new GeolocationFieldProperties()); + return new RootField(id, name, partitioning, properties, settings); } - public static RootField Json(long id, string name, Partitioning partitioning, JsonFieldProperties properties = null) + public static RootField Json(long id, string name, Partitioning partitioning, JsonFieldProperties properties = null, IFieldSettings settings = null) { - return new RootField(id, name, partitioning, properties ?? new JsonFieldProperties()); + return new RootField(id, name, partitioning, properties, settings); } - public static RootField Number(long id, string name, Partitioning partitioning, NumberFieldProperties properties = null) + public static RootField Number(long id, string name, Partitioning partitioning, NumberFieldProperties properties = null, IFieldSettings settings = null) { - return new RootField(id, name, partitioning, properties ?? new NumberFieldProperties()); + return new RootField(id, name, partitioning, properties, settings); } - public static RootField References(long id, string name, Partitioning partitioning, ReferencesFieldProperties properties = null) + public static RootField References(long id, string name, Partitioning partitioning, ReferencesFieldProperties properties = null, IFieldSettings settings = null) { - return new RootField(id, name, partitioning, properties ?? new ReferencesFieldProperties()); + return new RootField(id, name, partitioning, properties, settings); } - public static RootField String(long id, string name, Partitioning partitioning, StringFieldProperties properties = null) + public static RootField String(long id, string name, Partitioning partitioning, StringFieldProperties properties = null, IFieldSettings settings = null) { - return new RootField(id, name, partitioning, properties ?? new StringFieldProperties()); + return new RootField(id, name, partitioning, properties, settings); } - public static RootField Tags(long id, string name, Partitioning partitioning, TagsFieldProperties properties = null) + public static RootField Tags(long id, string name, Partitioning partitioning, TagsFieldProperties properties = null, IFieldSettings settings = null) { - return new RootField(id, name, partitioning, properties ?? new TagsFieldProperties()); + return new RootField(id, name, partitioning, properties, settings); } - public static NestedField Assets(long id, string name, AssetsFieldProperties properties = null) + public static NestedField Assets(long id, string name, AssetsFieldProperties properties = null, IFieldSettings settings = null) { - return new NestedField(id, name, properties ?? new AssetsFieldProperties()); + return new NestedField(id, name, properties, settings); } - public static NestedField Boolean(long id, string name, BooleanFieldProperties properties = null) + public static NestedField Boolean(long id, string name, BooleanFieldProperties properties = null, IFieldSettings settings = null) { - return new NestedField(id, name, properties ?? new BooleanFieldProperties()); + return new NestedField(id, name, properties, settings); } - public static NestedField DateTime(long id, string name, DateTimeFieldProperties properties = null) + public static NestedField DateTime(long id, string name, DateTimeFieldProperties properties = null, IFieldSettings settings = null) { - return new NestedField(id, name, properties ?? new DateTimeFieldProperties()); + return new NestedField(id, name, properties, settings); } - public static NestedField Geolocation(long id, string name, GeolocationFieldProperties properties = null) + public static NestedField Geolocation(long id, string name, GeolocationFieldProperties properties = null, IFieldSettings settings = null) { - return new NestedField(id, name, properties ?? new GeolocationFieldProperties()); + return new NestedField(id, name, properties, settings); } - public static NestedField Json(long id, string name, JsonFieldProperties properties = null) + public static NestedField Json(long id, string name, JsonFieldProperties properties = null, IFieldSettings settings = null) { - return new NestedField(id, name, properties ?? new JsonFieldProperties()); + return new NestedField(id, name, properties, settings); } - public static NestedField Number(long id, string name, NumberFieldProperties properties = null) + public static NestedField Number(long id, string name, NumberFieldProperties properties = null, IFieldSettings settings = null) { - return new NestedField(id, name, properties ?? new NumberFieldProperties()); + return new NestedField(id, name, properties, settings); } - public static NestedField References(long id, string name, ReferencesFieldProperties properties = null) + public static NestedField References(long id, string name, ReferencesFieldProperties properties = null, IFieldSettings settings = null) { - return new NestedField(id, name, properties ?? new ReferencesFieldProperties()); + return new NestedField(id, name, properties, settings); } - public static NestedField String(long id, string name, StringFieldProperties properties = null) + public static NestedField String(long id, string name, StringFieldProperties properties = null, IFieldSettings settings = null) { - return new NestedField(id, name, properties ?? new StringFieldProperties()); + return new NestedField(id, name, properties, settings); } - public static NestedField Tags(long id, string name, TagsFieldProperties properties = null) + public static NestedField Tags(long id, string name, TagsFieldProperties properties = null, IFieldSettings settings = null) { - return new NestedField(id, name, properties ?? new TagsFieldProperties()); + return new NestedField(id, name, properties, settings); } - public static Schema AddArray(this Schema schema, long id, string name, Partitioning partitioning, Func handler, ArrayFieldProperties properties = null) + public static Schema AddArray(this Schema schema, long id, string name, Partitioning partitioning, Func 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) { @@ -133,94 +123,94 @@ namespace Squidex.Domain.Apps.Core.Schemas 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)); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs index 9136b723c..fadf558fa 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs @@ -24,14 +24,14 @@ namespace Squidex.Domain.Apps.Core.Schemas return visitor.Visit((IField)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); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/IField.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/IField.cs index 6cc86239d..52b1b0bd2 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/IField.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/IField.cs @@ -7,18 +7,12 @@ namespace Squidex.Domain.Apps.Core.Schemas { - public interface IField + public interface IField : IFieldSettings { long Id { get; } string Name { get; } - bool IsLocked { get; } - - bool IsDisabled { get; } - - bool IsHidden { get; } - FieldProperties RawProperties { get; } T Accept(IFieldVisitor visitor); diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/IFieldSettings.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/IFieldSettings.cs new file mode 100644 index 000000000..4caeb7827 --- /dev/null +++ b/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; } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonFieldModel.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonFieldModel.cs index 952ef87f1..ebd6f722f 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonFieldModel.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonFieldModel.cs @@ -5,12 +5,11 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; using Newtonsoft.Json; namespace Squidex.Domain.Apps.Core.Schemas.Json { - public sealed class JsonFieldModel + public sealed class JsonFieldModel : IFieldSettings { [JsonProperty] public long Id { get; set; } @@ -34,6 +33,6 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json public FieldProperties Properties { get; set; } [JsonProperty] - public List Children { get; set; } + public JsonNestedFieldModel[] Children { get; set; } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs index 59b2035e7..50240e695 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs @@ -9,7 +9,7 @@ using Newtonsoft.Json; namespace Squidex.Domain.Apps.Core.Schemas.Json { - public sealed class JsonNestedFieldModel + public sealed class JsonNestedFieldModel : IFieldSettings { [JsonProperty] public long Id { get; set; } @@ -25,5 +25,10 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json [JsonProperty] public FieldProperties Properties { get; set; } + + public bool IsLocked + { + get { return false; } + } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs index 37af1b9e6..7e27ef756 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs @@ -5,15 +5,16 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; +using System; using System.Linq; using Newtonsoft.Json; +using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Schemas.Json { public sealed class JsonSchemaModel { - private static readonly RootField[] Empty = new RootField[0]; + private static readonly RootField[] Empty = Array.Empty(); [JsonProperty] public string Name { get; set; } @@ -25,7 +26,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json public SchemaProperties Properties { get; set; } [JsonProperty] - public List Fields { get; set; } + public JsonFieldModel[] Fields { get; set; } public JsonSchemaModel() { @@ -38,7 +39,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json Properties = schema.Properties; Fields = - schema.Fields.Select(x => + schema.Fields.ToArray(x => new JsonFieldModel { Id = x.Id, @@ -49,16 +50,16 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json IsDisabled = x.IsDisabled, Partitioning = x.Partitioning.Key, Properties = x.RawProperties - }).ToList(); + }); IsPublished = schema.IsPublished; } - private static List CreateChildren(IField field) + private static JsonNestedFieldModel[] CreateChildren(IField field) { if (field is ArrayField arrayField) { - return arrayField.Fields.Select(x => + return arrayField.Fields.ToArray(x => new JsonNestedFieldModel { Id = x.Id, @@ -66,7 +67,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json IsHidden = x.IsHidden, IsDisabled = x.IsDisabled, Properties = x.RawProperties - }).ToList(); + }); } return null; @@ -78,55 +79,28 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json if (Fields != null) { - fields = new RootField[Fields.Count]; - - for (var i = 0; i < fields.Length; i++) + fields = Fields.ToArray(fieldModel => { - var fieldModel = Fields[i]; - 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); - - if (nestedFieldModel.IsHidden) - { - nestedField = nestedField.Hide(); - } - - if (nestedFieldModel.IsDisabled) - { - nestedField = nestedField.Disable(); - } - - arrayField = arrayField.AddField(nestedField); - } - - field = arrayField; - } - - if (fieldModel.IsDisabled) - { - field = field.Disable(); - } + return registry.CreateNestedField(nestedFieldModel.Id, nestedFieldModel.Name, nestedFieldModel.Properties, nestedFieldModel); + }); - if (fieldModel.IsLocked) - { - field = field.Lock(); + field = new ArrayField(fieldModel.Id, fieldModel.Name, parititonKey, nestedFields, arrayProperties, fieldModel); } - - if (fieldModel.IsHidden) + else { - 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); diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs index 6edb4f80b..87a5adea6 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs @@ -22,14 +22,14 @@ namespace Squidex.Domain.Apps.Core.Schemas return visitor.Visit((IField)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); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs index c958951ad..9643c2ea4 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs @@ -45,13 +45,20 @@ namespace Squidex.Domain.Apps.Core.Schemas 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.GreaterThan(id, 0, nameof(id)); fieldId = id; fieldName = name; + + if (settings != null) + { + isLocked = settings.IsLocked; + isHidden = settings.IsHidden; + isDisabled = settings.IsDisabled; + } } [Pure] diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs index 7de914a4b..808de167b 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs @@ -25,12 +25,10 @@ namespace Squidex.Domain.Apps.Core.Schemas get { return properties; } } - public NestedField(long id, string name, T properties) - : base(id, name) + public NestedField(long id, string name, T properties = null, IFieldSettings settings = null) + : base(id, name, settings) { - Guard.NotNull(properties, nameof(properties)); - - SetProperties(properties); + SetProperties(properties ?? new T()); } [Pure] diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs index 3238aff25..e16a47f28 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs @@ -35,14 +35,14 @@ namespace Squidex.Domain.Apps.Core.Schemas return visitor.Visit((IField)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); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs index 98e4bb5ec..71cfe0cff 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs @@ -29,14 +29,14 @@ namespace Squidex.Domain.Apps.Core.Schemas return visitor.Visit((IField)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); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs index 461f60365..6c21a1054 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs @@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Core.Schemas 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.GreaterThan(id, 0, nameof(id)); @@ -61,6 +61,13 @@ namespace Squidex.Domain.Apps.Core.Schemas fieldName = name; this.partitioning = partitioning; + + if (settings != null) + { + isLocked = settings.IsLocked; + isHidden = settings.IsHidden; + isDisabled = settings.IsDisabled; + } } [Pure] diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs index 90165643b..cbe6716d0 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs @@ -25,12 +25,10 @@ namespace Squidex.Domain.Apps.Core.Schemas get { return properties; } } - public RootField(long id, string name, Partitioning partitioning, T properties) - : base(id, name, partitioning) + public RootField(long id, string name, Partitioning partitioning, T properties = null, IFieldSettings settings = null) + : base(id, name, partitioning, settings) { - Guard.NotNull(properties, nameof(properties)); - - SetProperties(properties); + SetProperties(properties ?? new T()); } [Pure] diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs index e9731480d..eaee1f7a5 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs @@ -39,14 +39,14 @@ namespace Squidex.Domain.Apps.Core.Schemas return visitor.Visit((IField)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); } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs index a50cc4bd9..a1c1f822e 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs @@ -33,14 +33,14 @@ namespace Squidex.Domain.Apps.Core.Schemas return visitor.Visit((IField)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); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs index dce94972e..bdb223f40 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs +++ b/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))); - 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) diff --git a/src/Squidex.Infrastructure/CollectionExtensions.cs b/src/Squidex.Infrastructure/CollectionExtensions.cs index 61513594d..1d26ce8b4 100644 --- a/src/Squidex.Infrastructure/CollectionExtensions.cs +++ b/src/Squidex.Infrastructure/CollectionExtensions.cs @@ -62,6 +62,32 @@ namespace Squidex.Infrastructure return source.Concat(Enumerable.Repeat(value, 1)); } + public static TResult[] ToArray(this T[] value, Func 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(this IReadOnlyCollection value, Func 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(this IEnumerable collection) { return collection.SequentialHashCode(EqualityComparer.Default); diff --git a/src/Squidex.Infrastructure/Language.cs b/src/Squidex.Infrastructure/Language.cs index c3c6afb71..ba5c546b6 100644 --- a/src/Squidex.Infrastructure/Language.cs +++ b/src/Squidex.Infrastructure/Language.cs @@ -35,7 +35,7 @@ namespace Squidex.Infrastructure } } - public static IEnumerable AllLanguages + public static IReadOnlyCollection AllLanguages { get { return AllLanguagesField.Values; } } diff --git a/src/Squidex.Infrastructure/Log/IArrayWriter.cs b/src/Squidex.Infrastructure/Log/IArrayWriter.cs index 7d5c96a48..8ec8b803a 100644 --- a/src/Squidex.Infrastructure/Log/IArrayWriter.cs +++ b/src/Squidex.Infrastructure/Log/IArrayWriter.cs @@ -12,12 +12,17 @@ namespace Squidex.Infrastructure.Log public interface IArrayWriter { IArrayWriter WriteValue(string value); + IArrayWriter WriteValue(double value); + IArrayWriter WriteValue(long value); + IArrayWriter WriteValue(bool value); IArrayWriter WriteValue(TimeSpan value); + IArrayWriter WriteValue(DateTime value); + IArrayWriter WriteValue(DateTimeOffset value); IArrayWriter WriteObject(Action objectWriter); diff --git a/src/Squidex.Infrastructure/Log/IObjectWriter.cs b/src/Squidex.Infrastructure/Log/IObjectWriter.cs index 1c8ce1f95..fc133875e 100644 --- a/src/Squidex.Infrastructure/Log/IObjectWriter.cs +++ b/src/Squidex.Infrastructure/Log/IObjectWriter.cs @@ -12,15 +12,21 @@ namespace Squidex.Infrastructure.Log public interface IObjectWriter { IObjectWriter WriteProperty(string property, string value); + IObjectWriter WriteProperty(string property, double value); + IObjectWriter WriteProperty(string property, long value); + IObjectWriter WriteProperty(string property, bool value); IObjectWriter WriteProperty(string property, TimeSpan value); + IObjectWriter WriteProperty(string property, DateTime value); + IObjectWriter WriteProperty(string property, DateTimeOffset value); IObjectWriter WriteObject(string property, Action objectWriter); + IObjectWriter WriteArray(string property, Action arrayWriter); } } diff --git a/src/Squidex.Infrastructure/Log/IObjectWriterFactory.cs b/src/Squidex.Infrastructure/Log/IObjectWriterFactory.cs new file mode 100644 index 000000000..65b992ee0 --- /dev/null +++ b/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); + } +} diff --git a/src/Squidex.Infrastructure/Log/JsonLogWriter.cs b/src/Squidex.Infrastructure/Log/JsonLogWriter.cs index 7d9bdf21f..4d8012f8f 100644 --- a/src/Squidex.Infrastructure/Log/JsonLogWriter.cs +++ b/src/Squidex.Infrastructure/Log/JsonLogWriter.cs @@ -14,18 +14,37 @@ namespace Squidex.Infrastructure.Log { 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 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.WriteStartObject(); } + internal void Reset() + { + textWriter.GetStringBuilder().Clear(); + + Start(); + } + IArrayWriter IArrayWriter.WriteValue(string value) { jsonWriter.WriteValue(value); @@ -170,14 +189,12 @@ namespace Squidex.Infrastructure.Log { jsonWriter.WriteEndObject(); - var result = textWriter.ToString(); - - if (extraLine) + if (formatLine) { - result += Environment.NewLine; + textWriter.WriteLine(); } - return result; + return textWriter.ToString(); } } } diff --git a/src/Squidex.Infrastructure/Log/JsonLogWriterFactory.cs b/src/Squidex.Infrastructure/Log/JsonLogWriterFactory.cs new file mode 100644 index 000000000..7ff85b6ea --- /dev/null +++ b/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 pool = new ConcurrentStack(); + 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); + } + } + } +} diff --git a/src/Squidex.Infrastructure/Log/SemanticLog.cs b/src/Squidex.Infrastructure/Log/SemanticLog.cs index 7816db42f..09293315d 100644 --- a/src/Squidex.Infrastructure/Log/SemanticLog.cs +++ b/src/Squidex.Infrastructure/Log/SemanticLog.cs @@ -13,20 +13,21 @@ namespace Squidex.Infrastructure.Log { public sealed class SemanticLog : ISemanticLog { - private readonly IEnumerable channels; - private readonly IEnumerable appenders; - private readonly Func writerFactory; + private readonly ILogChannel[] channels; + private readonly ILogAppender[] appenders; + private readonly IObjectWriterFactory writerFactory; public SemanticLog( IEnumerable channels, IEnumerable appenders, - Func writerFactory) + IObjectWriterFactory writerFactory) { Guard.NotNull(channels, nameof(channels)); Guard.NotNull(appenders, nameof(appenders)); + Guard.NotNull(writerFactory, nameof(writerFactory)); - this.channels = channels; - this.appenders = appenders; + this.channels = channels.ToArray(); + this.appenders = appenders.ToArray(); this.writerFactory = writerFactory; } @@ -38,11 +39,11 @@ namespace Squidex.Infrastructure.Log List exceptions = null; - foreach (var channel in channels) + for (var i = 0; i < channels.Length; i++) { try { - channel.Log(logLevel, formattedText); + channels[i].Log(logLevel, formattedText); } catch (Exception ex) { @@ -63,18 +64,25 @@ namespace Squidex.Infrastructure.Log private string FormatText(SemanticLogLevel logLevel, Action 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 objectWriter) diff --git a/src/Squidex.Infrastructure/Security/PermissionSet.cs b/src/Squidex.Infrastructure/Security/PermissionSet.cs index a7b138927..08f702b19 100644 --- a/src/Squidex.Infrastructure/Security/PermissionSet.cs +++ b/src/Squidex.Infrastructure/Security/PermissionSet.cs @@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.Security { public sealed class PermissionSet : IReadOnlyCollection { - public static readonly PermissionSet Empty = new PermissionSet(new string[0]); + public static readonly PermissionSet Empty = new PermissionSet(Array.Empty()); private readonly List permissions; private readonly Lazy display; diff --git a/src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs b/src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs index d07e2d7a4..3aa0e09a3 100644 --- a/src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs +++ b/src/Squidex.Infrastructure/States/Persistence{TSnapshot,TKey}.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; #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[] 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() diff --git a/src/Squidex.Infrastructure/ValidationError.cs b/src/Squidex.Infrastructure/ValidationError.cs index 06e294aaf..2ae3c5de4 100644 --- a/src/Squidex.Infrastructure/ValidationError.cs +++ b/src/Squidex.Infrastructure/ValidationError.cs @@ -14,7 +14,7 @@ namespace Squidex.Infrastructure [Serializable] public sealed class ValidationError { - private static readonly string[] FallbackProperties = new string[0]; + private static readonly string[] FallbackProperties = Array.Empty(); private readonly string message; private readonly string[] propertyNames; diff --git a/src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs b/src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs index 7a6f1c579..dbff58573 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Squidex.Areas.Api.Controllers.Apps.Models; using Squidex.Domain.Apps.Entities.Apps.Commands; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Pipeline; using Squidex.Shared; @@ -45,7 +46,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [ApiCosts(0)] 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(); diff --git a/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs b/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs index 8ed7a7685..cf6bee081 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs @@ -46,7 +46,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [ApiCosts(0)] 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(); diff --git a/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs b/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs index 74fc6bfb4..0f3092fb4 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs +++ b/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.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Services; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Security; using Squidex.Pipeline; @@ -62,7 +63,7 @@ namespace Squidex.Areas.Api.Controllers.Apps 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(); diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs index dfbeec425..f2774e4b4 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs @@ -13,6 +13,7 @@ using Newtonsoft.Json; using NodaTime; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps.Services; +using Squidex.Infrastructure; using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Security; using Squidex.Pipeline; @@ -80,7 +81,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models 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.PlanUpgrade = plans.GetPlanUpgradeForApp(app)?.Name; diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs index 0a064ebac..62501e4c2 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs @@ -5,7 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; +using System; using System.ComponentModel.DataAnnotations; using System.Linq; using Squidex.Domain.Apps.Core.Apps; @@ -34,7 +34,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models /// The fallback languages. /// [Required] - public List Fallback { get; set; } + public Language[] Fallback { get; set; } /// /// 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) { - return SimpleMapper.Map(command.Language, new AppLanguageDto { Fallback = new List() }); + return SimpleMapper.Map(command.Language, new AppLanguageDto { Fallback = Array.Empty() }); } public static AppLanguageDto[] FromApp(IAppEntity app) @@ -63,7 +63,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models { IsMaster = x == app.LanguagesConfig.Master, IsOptional = x.IsOptional, - Fallback = x.LanguageFallbacks.ToList() + Fallback = x.LanguageFallbacks.ToArray() }); } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs index a3352dd48..8292c7f0a 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs @@ -9,6 +9,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps.Services; +using Squidex.Infrastructure; namespace Squidex.Areas.Api.Controllers.Apps.Models { @@ -29,7 +30,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models { 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 }; } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs index 4ee092e69..2c833707b 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using Squidex.Domain.Apps.Core.Apps; @@ -35,7 +36,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models /// Associated list of permissions. /// [Required] - public string[] Permissions { get; set; } + public IEnumerable Permissions { get; set; } public static RoleDto FromRole(Role role, IAppEntity app) { @@ -46,7 +47,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models Name = role.Name, 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)), - Permissions = permissions.ToIds().ToArray() + Permissions = permissions.ToIds() }; } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateLanguageDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateLanguageDto.cs index 8c6d2e707..0a470ff6c 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateLanguageDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateLanguageDto.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure; using Squidex.Infrastructure.Reflection; @@ -27,7 +26,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models /// /// Optional fallback languages. /// - public List Fallback { get; set; } + public Language[] Fallback { get; set; } public UpdateLanguage ToCommand(Language language) { diff --git a/src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs b/src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs index 2f88f75f3..d69893681 100644 --- a/src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs @@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Mvc; using Orleans; using Squidex.Areas.Api.Controllers.Backups.Models; using Squidex.Domain.Apps.Entities.Backup; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Tasks; using Squidex.Pipeline; @@ -53,7 +54,7 @@ namespace Squidex.Areas.Api.Controllers.Backups var jobs = await backupGrain.GetStateAsync(); - var response = jobs.Value.Select(BackupJobDto.FromBackup).ToList(); + var response = jobs.Value.ToArray(BackupJobDto.FromBackup); return Ok(response); } diff --git a/src/Squidex/Areas/Api/Controllers/Comments/Models/CommentsDto.cs b/src/Squidex/Areas/Api/Controllers/Comments/Models/CommentsDto.cs index dceed44de..063e76234 100644 --- a/src/Squidex/Areas/Api/Controllers/Comments/Models/CommentsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Comments/Models/CommentsDto.cs @@ -17,12 +17,12 @@ namespace Squidex.Areas.Api.Controllers.Comments.Models /// /// The created comments including the updates. /// - public List CreatedComments { get; set; } + public CommentDto[] CreatedComments { get; set; } /// /// The updates comments since the last version. /// - public List UpdatedComments { get; set; } + public CommentDto[] UpdatedComments { get; set; } /// /// The deleted comments since the last version. @@ -38,8 +38,8 @@ namespace Squidex.Areas.Api.Controllers.Comments.Models { return new CommentsDto { - CreatedComments = result.CreatedComments.Select(CommentDto.FromComment).ToList(), - UpdatedComments = result.UpdatedComments.Select(CommentDto.FromComment).ToList(), + CreatedComments = result.CreatedComments.Select(CommentDto.FromComment).ToArray(), + UpdatedComments = result.UpdatedComments.Select(CommentDto.FromComment).ToArray(), DeletedComments = result.DeletedComments, Version = result.Version }; diff --git a/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs b/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs index e53ea81b5..5a5479cbc 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs +++ b/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.Commands; using Squidex.Domain.Apps.Entities.Contents.GraphQL; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Pipeline; using Squidex.Shared; diff --git a/src/Squidex/Areas/Api/Controllers/EventConsumers/EventConsumersController.cs b/src/Squidex/Areas/Api/Controllers/EventConsumers/EventConsumersController.cs index 0f770adff..f8a2c9d0d 100644 --- a/src/Squidex/Areas/Api/Controllers/EventConsumers/EventConsumersController.cs +++ b/src/Squidex/Areas/Api/Controllers/EventConsumers/EventConsumersController.cs @@ -35,7 +35,7 @@ namespace Squidex.Areas.Api.Controllers.EventConsumers { 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); } diff --git a/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs b/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs index 6a7565235..53e2fa8aa 100644 --- a/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs +++ b/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Squidex.Areas.Api.Controllers.History.Models; using Squidex.Domain.Apps.Entities.History; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Pipeline; using Squidex.Shared; @@ -48,7 +49,7 @@ namespace Squidex.Areas.Api.Controllers.History { var entities = await historyService.QueryByChannelAsync(AppId, channel, 100); - var response = entities.Select(HistoryEventDto.FromHistoryEvent).ToList(); + var response = entities.ToArray(HistoryEventDto.FromHistoryEvent); return Ok(response); } diff --git a/src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs b/src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs index d0750f020..8c4509e3f 100644 --- a/src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs +++ b/src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs @@ -39,7 +39,7 @@ namespace Squidex.Areas.Api.Controllers.Languages [ApiPermission] public IActionResult GetLanguages() { - var response = Language.AllLanguages.Select(LanguageDto.FromLanguage).ToList(); + var response = Language.AllLanguages.Select(LanguageDto.FromLanguage).ToArray(); Response.Headers["Etag"] = "1"; diff --git a/src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs b/src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs index f6130ceef..95345863e 100644 --- a/src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using Squidex.Domain.Apps.Entities.Apps; @@ -19,7 +18,7 @@ namespace Squidex.Areas.Api.Controllers.Plans.Models /// The available plans. /// [Required] - public List Plans { get; set; } + public PlanDto[] Plans { get; set; } /// /// The current plan id. @@ -43,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.Plans.Models var response = new AppPlansDto { CurrentPlanId = planId, - Plans = plans.GetAvailablePlans().Select(PlanDto.FromPlan).ToList(), + Plans = plans.GetAvailablePlans().Select(PlanDto.FromPlan).ToArray(), PlanOwner = app.Plan?.Owner.Identifier, HasPortal = hasPortal }; diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleTriggerDtoFactory.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleTriggerDtoFactory.cs index 0c58dc1e9..cd75ac32b 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleTriggerDtoFactory.cs +++ b/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) { - 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 }; } diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs index ef320b78d..482ac0caa 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using Squidex.Domain.Apps.Entities.Rules; +using Squidex.Infrastructure; namespace Squidex.Areas.Api.Controllers.Rules.Models { diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerDto.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerDto.cs index aa2b70386..9aa436d5f 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerDto.cs +++ b/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. /// [Required] - public List Schemas { get; set; } + public ContentChangedTriggerSchemaDto[] Schemas { get; set; } /// /// Determines whether the trigger should handle all content changes events. diff --git a/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs b/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs index dee4249f5..e3e0da2eb 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs +++ b/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 response = entities.Select(RuleDto.FromRule); + var response = entities.Select(RuleDto.FromRule).ToArray(); Response.Headers["ETag"] = response.ToManyEtag(0); diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs b/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs index f696a628f..86e5bdd96 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs +++ b/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.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Pipeline; using Squidex.Shared; @@ -50,7 +51,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas { var schemas = await appProvider.GetSchemasAsync(AppId); - var response = schemas.Select(SchemaDto.FromSchema).ToList(); + var response = schemas.ToArray(SchemaDto.FromSchema); Response.Headers["ETag"] = response.ToManyEtag(); diff --git a/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs b/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs index 67d0295df..99065d372 100644 --- a/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs +++ b/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.Domain.Apps.Entities.Apps.Services; using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.UsageTracking; 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 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); } @@ -146,7 +147,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics 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); } diff --git a/src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs b/src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs index bf048c0ba..8a2a5a2d4 100644 --- a/src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Users/Models/UserDto.cs @@ -5,8 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; using Squidex.Infrastructure.Reflection; using Squidex.Shared.Users; @@ -42,11 +42,11 @@ namespace Squidex.Areas.Api.Controllers.Users.Models /// Additional permissions for the user. /// [Required] - public string[] Permissions { get; set; } + public IEnumerable Permissions { get; set; } 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 }); } diff --git a/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs b/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs index 83c57d810..85d2b032f 100644 --- a/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs +++ b/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs @@ -18,7 +18,7 @@ namespace Squidex.Areas.Frontend.Middlewares private const string Host = "localhost"; private const string Port = "3000"; private static readonly string[] Scripts = { "shims.js", "app.js" }; - private static readonly string[] Styles = new string[0]; + private static readonly string[] Styles = Array.Empty(); private readonly RequestDelegate next; public WebpackMiddleware(RequestDelegate next) diff --git a/src/Squidex/Config/Domain/LoggingServices.cs b/src/Squidex/Config/Domain/LoggingServices.cs index a00ad2db2..0d1631ded 100644 --- a/src/Squidex/Config/Domain/LoggingServices.cs +++ b/src/Squidex/Config/Domain/LoggingServices.cs @@ -8,7 +8,6 @@ using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using Squidex.Infrastructure.Log; using Squidex.Pipeline; @@ -25,11 +24,13 @@ namespace Squidex.Config.Domain { if (config.GetValue("logging:human")) { - services.AddSingletonAs(c => new Func(() => new JsonLogWriter(Formatting.Indented, true))); + services.AddSingletonAs(JsonLogWriterFactory.Readable()) + .As(); } else { - services.AddSingletonAs(c => new Func(() => new JsonLogWriter())); + services.AddSingletonAs(JsonLogWriterFactory.Default()) + .As(); } var loggingFile = config.GetValue("logging:file"); diff --git a/src/Squidex/Config/Web/WebExtensions.cs b/src/Squidex/Config/Web/WebExtensions.cs index 9886fbc73..82a71d289 100644 --- a/src/Squidex/Config/Web/WebExtensions.cs +++ b/src/Squidex/Config/Web/WebExtensions.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.HttpOverrides; using Squidex.Infrastructure.Diagnostics; diff --git a/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs b/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs index 62aa39efe..6d0294fca 100644 --- a/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs +++ b/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs @@ -61,7 +61,7 @@ namespace Squidex.Pipeline 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) diff --git a/src/Squidex/Pipeline/ETagExtensions.cs b/src/Squidex/Pipeline/ETagExtensions.cs index 4c30e2411..074c952a3 100644 --- a/src/Squidex/Pipeline/ETagExtensions.cs +++ b/src/Squidex/Pipeline/ETagExtensions.cs @@ -5,8 +5,9 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Collections.Generic; -using System.Linq; +using System.Text; using Squidex.Infrastructure; using Squidex.Infrastructure.Log; @@ -14,17 +15,59 @@ namespace Squidex.Pipeline { public static class ETagExtensions { - public static string ToManyEtag(this IEnumerable items, long total = 0) where T : IGenerateEtag + private static readonly int GuidLength = Guid.Empty.ToString().Length; + + public static string ToManyEtag(this IReadOnlyList items, long total = 0) where T : IGenerateEtag { 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(this IEnumerable items) where T : IGenerateEtag + private static string Unhashed(IReadOnlyList 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(this IReadOnlyList 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(this T item) where T : IGenerateEtag diff --git a/src/Squidex/app/theme/icomoon/icons/control-Tags.svg b/src/Squidex/app/theme/icomoon/icons/control-Tags.svg new file mode 100644 index 000000000..b77cc0f7d --- /dev/null +++ b/src/Squidex/app/theme/icomoon/icons/control-Tags.svg @@ -0,0 +1,75 @@ + + + +image/svg+xml + + + + + + + + + \ No newline at end of file diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldRegistryTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldRegistryTests.cs index 597b54f8d..614ee6a65 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldRegistryTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldRegistryTests.cs @@ -28,12 +28,12 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas 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; } - public override NestedField CreateNestedField(long id, string name) + public override NestedField CreateNestedField(long id, string name, IFieldSettings settings = null) { return null; } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/RoleExtensionsRests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/RoleExtensionsRests.cs index 3e530d6d9..5d4b8764d 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/RoleExtensionsRests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/RoleExtensionsRests.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Linq; using Squidex.Infrastructure.Security; using Xunit; @@ -16,7 +17,7 @@ namespace Squidex.Domain.Apps.Entities.Apps [Fact] public void Should_add_common_permission() { - var source = new string[0]; + var source = Array.Empty(); var result = source.Prefix("my-app"); Assert.Equal(new[] { "squidex.apps.my-app.common" }, result); diff --git a/tests/Squidex.Infrastructure.Tests/Log/JsonLogWriterTests.cs b/tests/Squidex.Infrastructure.Tests/Log/JsonLogWriterTests.cs index a0ff04d7a..069be90c7 100644 --- a/tests/Squidex.Infrastructure.Tests/Log/JsonLogWriterTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Log/JsonLogWriterTests.cs @@ -13,7 +13,7 @@ namespace Squidex.Infrastructure.Log { public class JsonLogWriterTests { - private readonly IObjectWriter sut = new JsonLogWriter(); + private readonly IObjectWriter sut = JsonLogWriterFactory.Default().Create(); [Fact] public void Should_write_boolean_property() @@ -59,6 +59,7 @@ namespace Squidex.Infrastructure.Log public void Should_write_datetimeoffset_property() { var value = DateTimeOffset.UtcNow; + var result = sut.WriteProperty("property", value).ToString(); Assert.Equal($"{{\"property\":\"{value:o}\"}}", result); @@ -68,6 +69,7 @@ namespace Squidex.Infrastructure.Log public void Should_write_date_property() { var value = DateTime.UtcNow; + var result = sut.WriteProperty("property", value).ToString(); Assert.Equal($"{{\"property\":\"{value:o}\"}}", result); @@ -125,6 +127,7 @@ namespace Squidex.Infrastructure.Log public void Should_write_datetimeoffset_value() { var value = DateTimeOffset.UtcNow; + var result = sut.WriteArray("property", a => a.WriteValue(value)).ToString(); Assert.Equal($"{{\"property\":[\"{value:o}\"]}}", result); @@ -134,6 +137,7 @@ namespace Squidex.Infrastructure.Log public void Should_write_date_value() { var value = DateTime.UtcNow; + var result = sut.WriteArray("property", a => a.WriteValue(value)).ToString(); Assert.Equal($"{{\"property\":[\"{value:yyyy-MM-ddTHH:mm:ssZ}\"]}}", result); @@ -150,7 +154,7 @@ namespace Squidex.Infrastructure.Log [Fact] 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(); @@ -160,7 +164,7 @@ namespace Squidex.Infrastructure.Log [Fact] 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(); diff --git a/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs b/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs index b08b3262d..ec9958df8 100644 --- a/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Log/SemanticLogAdapterTests.cs @@ -38,7 +38,7 @@ namespace Squidex.Infrastructure.Log output = message; }); - log = new Lazy(() => new SemanticLog(channels, new List(), () => new JsonLogWriter())); + log = new Lazy(() => new SemanticLog(channels, new List(), JsonLogWriterFactory.Default())); sut = SemanticLogLoggerProvider.ForTesting(log.Value); } @@ -197,7 +197,7 @@ namespace Squidex.Infrastructure.Log private static string MakeTestCall(Action writer) { - IObjectWriter sut = new JsonLogWriter(); + var sut = JsonLogWriterFactory.Default().Create(); writer(sut); diff --git a/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs b/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs index 24593806c..7f82046ff 100644 --- a/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Log/SemanticLogTests.cs @@ -21,7 +21,7 @@ namespace Squidex.Infrastructure.Log private readonly List channels = new List(); private readonly Lazy log; private readonly ILogChannel channel = A.Fake(); - private string output; + private string output = string.Empty; public SemanticLog Log { @@ -35,10 +35,29 @@ namespace Squidex.Infrastructure.Log A.CallTo(() => channel.Log(A.Ignored, A.Ignored)) .Invokes((SemanticLogLevel level, string message) => { - output = message; + output += message; }); - log = new Lazy(() => new SemanticLog(channels, appenders, () => new JsonLogWriter())); + log = new Lazy(() => 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] @@ -307,7 +326,7 @@ namespace Squidex.Infrastructure.Log A.CallTo(() => channel1.Log(A.Ignored, A.Ignored)).Throws(exception1); A.CallTo(() => channel2.Log(A.Ignored, A.Ignored)).Throws(exception2); - var sut = new SemanticLog(new[] { channel1, channel2 }, Enumerable.Empty(), () => new JsonLogWriter()); + var sut = new SemanticLog(new[] { channel1, channel2 }, Enumerable.Empty(), JsonLogWriterFactory.Default()); try { @@ -324,7 +343,7 @@ namespace Squidex.Infrastructure.Log private static string LogTest(Action writer) { - IObjectWriter sut = new JsonLogWriter(); + var sut = JsonLogWriterFactory.Default().Create(); writer(sut);