diff --git a/build.sh b/build.sh index 0ad9dd304..bd61f351d 100644 --- a/build.sh +++ b/build.sh @@ -9,9 +9,9 @@ npm run build:copy npm run build cd ./../.. dotnet restore -dotnet test tests/Squidex.Core.Tests/project.json -dotnet test tests/Squidex.Infrastructure.Tests/project.json -dotnet test tests/Squidex.Read.Tests/project.json -dotnet test tests/Squidex.Write.Tests/project.json +dotnet test tests/Squidex.Core.Tests/Squidex.Core.Tests.csproj +dotnet test tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj +dotnet test tests/Squidex.Read.Tests/Squidex.Read.Tests.csproj +dotnet test tests/Squidex.Write.Tests/Squidex.Write.Tests.csproj rm -rf $(pwd)/publish/web -dotnet publish src/Squidex/project.json -c release -o $(pwd)/publish/web \ No newline at end of file +dotnet publish src/Squidex/Squidex.csproj -c release -o $(pwd)/publish/web \ No newline at end of file diff --git a/src/Squidex.Core/Contents/ContentData.cs b/src/Squidex.Core/Contents/ContentData.cs index 9544a12e2..2766b6251 100644 --- a/src/Squidex.Core/Contents/ContentData.cs +++ b/src/Squidex.Core/Contents/ContentData.cs @@ -70,9 +70,7 @@ namespace Squidex.Core.Contents foreach (var fieldValue in this) { - Field field; - - if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out field)) + if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out Field field)) { continue; } @@ -91,11 +89,7 @@ namespace Squidex.Core.Contents foreach (var fieldValue in this) { - long fieldId; - - Field field; - - if (!long.TryParse(fieldValue.Key, out fieldId) || !schema.Fields.TryGetValue(fieldId, out field)) + if (!long.TryParse(fieldValue.Key, out long fieldId) || !schema.Fields.TryGetValue(fieldId, out Field field)) { continue; } @@ -118,9 +112,7 @@ namespace Squidex.Core.Contents foreach (var fieldValue in this) { - Field field; - - if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out field) || (excludeHidden && field.IsHidden)) + if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out Field field) || (excludeHidden && field.IsHidden)) { continue; } @@ -134,9 +126,7 @@ namespace Squidex.Core.Contents { var languageCode = language.Iso2Code; - JToken value; - - if (fieldValues.TryGetValue(languageCode, out value)) + if (fieldValues.TryGetValue(languageCode, out JToken value)) { fieldResult.Add(languageCode, value); } @@ -144,9 +134,7 @@ namespace Squidex.Core.Contents } else { - JToken value; - - if (fieldValues.TryGetValue(invariantCode, out value)) + if (fieldValues.TryGetValue(invariantCode, out JToken value)) { fieldResult.Add(invariantCode, value); } @@ -183,9 +171,7 @@ namespace Squidex.Core.Contents { var languageCode = language.Iso2Code; - JToken value; - - if (fieldValues.TryGetValue(languageCode, out value) && value != null) + if (fieldValues.TryGetValue(languageCode, out JToken value) && value != null) { result[fieldValue.Key] = value; diff --git a/src/Squidex.Core/Schemas/BooleanFieldProperties.cs b/src/Squidex.Core/Schemas/BooleanFieldProperties.cs index 531524824..b125e0602 100644 --- a/src/Squidex.Core/Schemas/BooleanFieldProperties.cs +++ b/src/Squidex.Core/Schemas/BooleanFieldProperties.cs @@ -7,6 +7,7 @@ // ========================================================================== using System.Collections.Generic; +using Newtonsoft.Json.Linq; using Squidex.Infrastructure; namespace Squidex.Core.Schemas @@ -39,6 +40,11 @@ namespace Squidex.Core.Schemas } } + public override JToken GetDefaultValue() + { + return DefaultValue; + } + protected override IEnumerable ValidateCore() { if (!Editor.IsEnumValue()) diff --git a/src/Squidex.Core/Schemas/DateTimeField.cs b/src/Squidex.Core/Schemas/DateTimeField.cs new file mode 100644 index 000000000..56812c388 --- /dev/null +++ b/src/Squidex.Core/Schemas/DateTimeField.cs @@ -0,0 +1,57 @@ +// ========================================================================== +// DateTimeField.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; +using Microsoft.OData.Edm; +using Microsoft.OData.Edm.Library; +using Newtonsoft.Json.Linq; +using NJsonSchema; +using Squidex.Core.Schemas.Validators; +using Squidex.Infrastructure; + +namespace Squidex.Core.Schemas +{ + [TypeName("DateTimeField")] + public sealed class DateTimeField : Field + { + public DateTimeField(long id, string name, DateTimeFieldProperties properties) + : base(id, name, properties) + { + } + + protected override IEnumerable CreateValidators() + { + if (Properties.IsRequired) + { + yield return new RequiredValidator(); + } + + if (Properties.MinValue.HasValue || Properties.MaxValue.HasValue) + { + yield return new RangeValidator(Properties.MinValue, Properties.MaxValue); + } + } + + protected override object ConvertValue(JToken value) + { + return (DateTimeOffset?)value; + } + + protected override void PrepareJsonSchema(JsonProperty jsonProperty) + { + jsonProperty.Type = JsonObjectType.String; + jsonProperty.Format = "date-time"; + } + + protected override IEdmTypeReference CreateEdmType() + { + return EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.DateTimeOffset, !Properties.IsRequired); + } + } +} diff --git a/src/Squidex.Core/Schemas/DateTimeFieldEditor.cs b/src/Squidex.Core/Schemas/DateTimeFieldEditor.cs new file mode 100644 index 000000000..b894d5c9b --- /dev/null +++ b/src/Squidex.Core/Schemas/DateTimeFieldEditor.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// DateTimeFieldEditor.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +namespace Squidex.Core.Schemas +{ + public enum DateTimeFieldEditor + { + Date, + DateWithTimezone, + DateTime, + DateTimeWithTimezone + } +} diff --git a/src/Squidex.Core/Schemas/DateTimeFieldProperties.cs b/src/Squidex.Core/Schemas/DateTimeFieldProperties.cs new file mode 100644 index 000000000..2867eb639 --- /dev/null +++ b/src/Squidex.Core/Schemas/DateTimeFieldProperties.cs @@ -0,0 +1,96 @@ +// ========================================================================== +// DateTimeFieldProperties.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; +using Newtonsoft.Json.Linq; +using Squidex.Infrastructure; + +namespace Squidex.Core.Schemas +{ + [TypeName("DateTime")] + public sealed class DateTimeFieldProperties : FieldProperties + { + private DateTimeFieldEditor editor; + private DateTimeOffset? maxValue; + private DateTimeOffset? minValue; + private DateTimeOffset? defaultValue; + + public DateTimeOffset? MaxValue + { + get { return maxValue; } + set + { + ThrowIfFrozen(); + + maxValue = value; + } + } + + public DateTimeOffset? MinValue + { + get { return minValue; } + set + { + ThrowIfFrozen(); + + minValue = value; + } + } + + public DateTimeOffset? DefaultValue + { + get { return defaultValue; } + set + { + ThrowIfFrozen(); + + defaultValue = value; + } + } + + public DateTimeFieldEditor Editor + { + get { return editor; } + set + { + ThrowIfFrozen(); + + editor = value; + } + } + + public override JToken GetDefaultValue() + { + return DefaultValue; + } + + protected override IEnumerable ValidateCore() + { + if (!Editor.IsEnumValue()) + { + yield return new ValidationError("Editor ist not a valid value", nameof(Editor)); + } + + if (MaxValue.HasValue && MinValue.HasValue && MinValue.Value >= MaxValue.Value) + { + yield return new ValidationError("Max value must be greater than min value", nameof(MinValue), nameof(MaxValue)); + } + + if (DefaultValue.HasValue && MinValue.HasValue && DefaultValue.Value < MinValue.Value) + { + yield return new ValidationError("Default value must be greater than min value", nameof(DefaultValue)); + } + + if (DefaultValue.HasValue && MaxValue.HasValue && DefaultValue.Value > MaxValue.Value) + { + yield return new ValidationError("Default value must be less than max value", nameof(DefaultValue)); + } + } + } +} diff --git a/src/Squidex.Core/Schemas/Field.cs b/src/Squidex.Core/Schemas/Field.cs index dbbfca2ad..fc1eed634 100644 --- a/src/Squidex.Core/Schemas/Field.cs +++ b/src/Squidex.Core/Schemas/Field.cs @@ -13,6 +13,7 @@ using Microsoft.OData.Edm; using Microsoft.OData.Edm.Library; using Newtonsoft.Json.Linq; using NJsonSchema; +using Squidex.Core.Contents; using Squidex.Infrastructure; // ReSharper disable InvertIf @@ -65,6 +66,19 @@ namespace Squidex.Core.Schemas public abstract Field Update(FieldProperties newProperties); + public void Enrich(ContentFieldData fieldData, Language language) + { + Guard.NotNull(fieldData, nameof(fieldData)); + Guard.NotNull(language, nameof(language)); + + var defaultValue = RawProperties.GetDefaultValue(); + + if (!RawProperties.IsRequired && defaultValue != null && fieldData.GetOrDefault(language.Iso2Code) == null) + { + fieldData.AddValue(language.Iso2Code, defaultValue); + } + } + public async Task ValidateAsync(JToken value, ICollection errors, Language language = null) { Guard.NotNull(value, nameof(value)); diff --git a/src/Squidex.Core/Schemas/FieldProperties.cs b/src/Squidex.Core/Schemas/FieldProperties.cs index f82590468..0fbbea7f1 100644 --- a/src/Squidex.Core/Schemas/FieldProperties.cs +++ b/src/Squidex.Core/Schemas/FieldProperties.cs @@ -7,6 +7,7 @@ // ========================================================================== using System.Collections.Generic; +using Newtonsoft.Json.Linq; using Squidex.Infrastructure; namespace Squidex.Core.Schemas @@ -62,6 +63,8 @@ namespace Squidex.Core.Schemas } } + public abstract JToken GetDefaultValue(); + public void Validate(IList errors) { foreach (var error in ValidateCore()) diff --git a/src/Squidex.Core/Schemas/FieldRegistry.cs b/src/Squidex.Core/Schemas/FieldRegistry.cs index ff22069d9..0ffc8c7ba 100644 --- a/src/Squidex.Core/Schemas/FieldRegistry.cs +++ b/src/Squidex.Core/Schemas/FieldRegistry.cs @@ -64,6 +64,9 @@ namespace Squidex.Core.Schemas Add( (id, name, p) => new StringField(id, name, (StringFieldProperties)p)); + + Add( + (id, name, p) => new DateTimeField(id, name, (DateTimeFieldProperties)p)); } public void Add(FactoryFunction fieldFactory) diff --git a/src/Squidex.Core/Schemas/NumberFieldProperties.cs b/src/Squidex.Core/Schemas/NumberFieldProperties.cs index 4d64f53e5..9f2625c34 100644 --- a/src/Squidex.Core/Schemas/NumberFieldProperties.cs +++ b/src/Squidex.Core/Schemas/NumberFieldProperties.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using Newtonsoft.Json.Linq; using Squidex.Infrastructure; namespace Squidex.Core.Schemas @@ -76,6 +77,11 @@ namespace Squidex.Core.Schemas } } + public override JToken GetDefaultValue() + { + return DefaultValue; + } + protected override IEnumerable ValidateCore() { if (!Editor.IsEnumValue()) diff --git a/src/Squidex.Core/Schemas/Schema.cs b/src/Squidex.Core/Schemas/Schema.cs index 2cbb4dd6c..f36cdf52e 100644 --- a/src/Squidex.Core/Schemas/Schema.cs +++ b/src/Squidex.Core/Schemas/Schema.cs @@ -162,9 +162,7 @@ namespace Squidex.Core.Schemas { Guard.NotNull(updater, nameof(updater)); - Field field; - - if (!fieldsById.TryGetValue(fieldId, out field)) + if (!fieldsById.TryGetValue(fieldId, out Field field)) { throw new DomainObjectNotFoundException(fieldId.ToString(), "Fields", typeof(Field)); } @@ -211,9 +209,7 @@ namespace Squidex.Core.Schemas foreach (var fieldData in data) { - Field field; - - if (!fieldsByName.TryGetValue(fieldData.Key, out field)) + if (!fieldsByName.TryGetValue(fieldData.Key, out Field field)) { errors.Add(new ValidationError($"{fieldData.Key} is not a known field", fieldData.Key)); } @@ -225,9 +221,7 @@ namespace Squidex.Core.Schemas { foreach (var languageValue in fieldData.Value) { - Language language; - - if (!Language.TryGetLanguage(languageValue.Key, out language)) + if (!Language.TryGetLanguage(languageValue.Key, out Language language)) { fieldErrors.Add($"{field.Name} has an invalid language '{languageValue.Key}'"); } @@ -248,9 +242,7 @@ namespace Squidex.Core.Schemas fieldErrors.Add($"{field.Name} can only contain a single entry for invariant language ({Language.Invariant.Iso2Code})"); } - JToken value; - - if (fieldData.Value.TryGetValue(Language.Invariant.Iso2Code, out value)) + if (fieldData.Value.TryGetValue(Language.Invariant.Iso2Code, out JToken value)) { await field.ValidateAsync(value, fieldErrors); } @@ -281,16 +273,13 @@ namespace Squidex.Core.Schemas foreach (var field in fieldsByName.Values) { var fieldErrors = new List(); + var fieldData = data.GetOrCreate(field.Name, k => new ContentFieldData()); - var fieldData = data.GetOrDefault(field.Name) ?? new ContentFieldData(); - if (field.RawProperties.IsLocalizable) { foreach (var valueLanguage in fieldData.Keys) { - Language language; - - if (!Language.TryGetLanguage(valueLanguage, out language)) + if (!Language.TryGetLanguage(valueLanguage, out Language language)) { fieldErrors.Add($"{field.Name} has an invalid language '{valueLanguage}'"); } @@ -325,5 +314,33 @@ namespace Squidex.Core.Schemas } } } + + public void Enrich(ContentData data, HashSet languages) + { + Guard.NotNull(data, nameof(data)); + Guard.NotEmpty(languages, nameof(languages)); + + foreach (var field in fieldsByName.Values) + { + var fieldData = data.GetOrCreate(field.Name, k => new ContentFieldData()); + + if (field.RawProperties.IsLocalizable) + { + foreach (var language in languages) + { + field.Enrich(fieldData, language); + } + } + else + { + field.Enrich(fieldData, Language.Invariant); + } + + if (fieldData.Count > 0) + { + data.AddField(field.Name, fieldData); + } + } + } } } \ No newline at end of file diff --git a/src/Squidex.Core/Schemas/SchemaProperties.cs b/src/Squidex.Core/Schemas/SchemaProperties.cs index fa76ffbe6..25ba80c67 100644 --- a/src/Squidex.Core/Schemas/SchemaProperties.cs +++ b/src/Squidex.Core/Schemas/SchemaProperties.cs @@ -5,6 +5,7 @@ // Copyright (c) Squidex Group // All rights reserved. // ========================================================================== + namespace Squidex.Core.Schemas { public sealed class SchemaProperties : NamedElementProperties diff --git a/src/Squidex.Core/Schemas/StringFieldProperties.cs b/src/Squidex.Core/Schemas/StringFieldProperties.cs index 072114db2..41f80867a 100644 --- a/src/Squidex.Core/Schemas/StringFieldProperties.cs +++ b/src/Squidex.Core/Schemas/StringFieldProperties.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using Squidex.Infrastructure; using System.Collections.Immutable; +using Newtonsoft.Json.Linq; // ReSharper disable ObjectCreationAsStatement @@ -21,6 +22,7 @@ namespace Squidex.Core.Schemas private int? maxLength; private string pattern; private string patternMessage; + private string defaultValue; private ImmutableList allowedValues; private StringFieldEditor editor; @@ -46,6 +48,17 @@ namespace Squidex.Core.Schemas } } + public string DefaultValue + { + get { return defaultValue; } + set + { + ThrowIfFrozen(); + + defaultValue = value; + } + } + public string Pattern { get { return pattern; } @@ -90,6 +103,11 @@ namespace Squidex.Core.Schemas } } + public override JToken GetDefaultValue() + { + return DefaultValue; + } + protected override IEnumerable ValidateCore() { if (!Editor.IsEnumValue()) diff --git a/src/Squidex.Core/Squidex.Core.csproj b/src/Squidex.Core/Squidex.Core.csproj index def4259e5..8080fb0ce 100644 --- a/src/Squidex.Core/Squidex.Core.csproj +++ b/src/Squidex.Core/Squidex.Core.csproj @@ -3,6 +3,10 @@ netstandard1.6 $(PackageTargetFallback);dnxcore50 + + full + True + diff --git a/src/Squidex.Events/Squidex.Events.csproj b/src/Squidex.Events/Squidex.Events.csproj index e7319ec16..c5dd52a47 100644 --- a/src/Squidex.Events/Squidex.Events.csproj +++ b/src/Squidex.Events/Squidex.Events.csproj @@ -3,6 +3,10 @@ netstandard1.6 $(PackageTargetFallback);dnxcore50 + + full + True + diff --git a/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj b/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj index 1f4fc04e4..2d95e66be 100644 --- a/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj +++ b/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj @@ -2,6 +2,10 @@ netstandard1.6 + + full + True + diff --git a/src/Squidex.Infrastructure.Redis/RedisSubscription.cs b/src/Squidex.Infrastructure.Redis/RedisSubscription.cs index 9d37539d1..b5db338b1 100644 --- a/src/Squidex.Infrastructure.Redis/RedisSubscription.cs +++ b/src/Squidex.Infrastructure.Redis/RedisSubscription.cs @@ -64,9 +64,7 @@ namespace Squidex.Infrastructure.Redis return; } - Guid sender; - - if (!Guid.TryParse(parts[0], out sender)) + if (!Guid.TryParse(parts[0], out Guid sender)) { return; } diff --git a/src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj b/src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj index aeb6f358d..68d07fc7b 100644 --- a/src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj +++ b/src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj @@ -2,6 +2,10 @@ netstandard1.6 + + full + True + diff --git a/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs b/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs index 2b6ec3f08..d693f040f 100644 --- a/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs +++ b/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs @@ -136,7 +136,6 @@ namespace Squidex.Infrastructure.CQRS.Events { try { - logger.LogDebug("[{0}]: Reset started", eventConsumer); await eventConsumer.ClearAsync(); diff --git a/src/Squidex.Infrastructure/CollectionExtensions.cs b/src/Squidex.Infrastructure/CollectionExtensions.cs index 223a42227..1050e4a68 100644 --- a/src/Squidex.Infrastructure/CollectionExtensions.cs +++ b/src/Squidex.Infrastructure/CollectionExtensions.cs @@ -109,9 +109,7 @@ namespace Squidex.Infrastructure public static TValue GetOrCreate(this IReadOnlyDictionary dictionary, TKey key, Func creator) { - TValue result; - - if (!dictionary.TryGetValue(key, out result)) + if (!dictionary.TryGetValue(key, out TValue result)) { result = creator(key); } @@ -121,9 +119,7 @@ namespace Squidex.Infrastructure public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func creator) { - TValue result; - - if (!dictionary.TryGetValue(key, out result)) + if (!dictionary.TryGetValue(key, out TValue result)) { result = creator(key); diff --git a/src/Squidex.Infrastructure/Dispatching/ActionContextDispatcher.cs b/src/Squidex.Infrastructure/Dispatching/ActionContextDispatcher.cs index b14d6896a..d859af728 100644 --- a/src/Squidex.Infrastructure/Dispatching/ActionContextDispatcher.cs +++ b/src/Squidex.Infrastructure/Dispatching/ActionContextDispatcher.cs @@ -30,9 +30,7 @@ namespace Squidex.Infrastructure.Dispatching public static bool Dispatch(TTarget target, TIn input, TContext context) { - Action handler; - - if (!Handlers.TryGetValue(input.GetType(), out handler)) + if (!Handlers.TryGetValue(input.GetType(), out Action handler)) { return false; } diff --git a/src/Squidex.Infrastructure/Dispatching/ActionDispatcher.cs b/src/Squidex.Infrastructure/Dispatching/ActionDispatcher.cs index 0949b8542..7dbcfae8b 100644 --- a/src/Squidex.Infrastructure/Dispatching/ActionDispatcher.cs +++ b/src/Squidex.Infrastructure/Dispatching/ActionDispatcher.cs @@ -30,9 +30,7 @@ namespace Squidex.Infrastructure.Dispatching public static bool Dispatch(TTarget target, TIn item) { - Action handler; - - if (!Handlers.TryGetValue(item.GetType(), out handler)) + if (!Handlers.TryGetValue(item.GetType(), out Action handler)) { return false; } diff --git a/src/Squidex.Infrastructure/Dispatching/FuncContextDispatcher.cs b/src/Squidex.Infrastructure/Dispatching/FuncContextDispatcher.cs index a4153e259..53477114f 100644 --- a/src/Squidex.Infrastructure/Dispatching/FuncContextDispatcher.cs +++ b/src/Squidex.Infrastructure/Dispatching/FuncContextDispatcher.cs @@ -31,9 +31,7 @@ namespace Squidex.Infrastructure.Dispatching public static TOut Dispatch(TTarget target, TIn item, TContext context) { - Func handler; - - return Handlers.TryGetValue(item.GetType(), out handler) ? handler(target, item, context) : default(TOut); + return Handlers.TryGetValue(item.GetType(), out Func handler) ? handler(target, item, context) : default(TOut); } } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure/Dispatching/FuncDispatcher.cs b/src/Squidex.Infrastructure/Dispatching/FuncDispatcher.cs index eb427fffd..ce18f9085 100644 --- a/src/Squidex.Infrastructure/Dispatching/FuncDispatcher.cs +++ b/src/Squidex.Infrastructure/Dispatching/FuncDispatcher.cs @@ -31,9 +31,7 @@ namespace Squidex.Infrastructure.Dispatching public static TOut Dispatch(TTarget target, TIn item) { - Func handler; - - return Handlers.TryGetValue(item.GetType(), out handler) ? handler(target, item) : default(TOut); + return Handlers.TryGetValue(item.GetType(), out Func handler) ? handler(target, item) : default(TOut); } } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs b/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs index 781a030fc..003d4e54e 100644 --- a/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs @@ -35,9 +35,7 @@ namespace Squidex.Infrastructure.Json throw new JsonException("Named id must have more than 2 parts divided by commata"); } - Guid id; - - if (!Guid.TryParse(parts[0], out id)) + if (!Guid.TryParse(parts[0], out Guid id)) { throw new JsonException("Named id must be a valid guid"); } diff --git a/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs b/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs index 0d86c732b..1bf9d7ead 100644 --- a/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs @@ -35,9 +35,7 @@ namespace Squidex.Infrastructure.Json throw new JsonException("Named id must have more than 2 parts divided by commata"); } - long id; - - if (!long.TryParse(parts[0], out id)) + if (!long.TryParse(parts[0], out long id)) { throw new JsonException("Named id must be a valid long"); } diff --git a/src/Squidex.Infrastructure/Language.cs b/src/Squidex.Infrastructure/Language.cs index b0ad82847..641d9fd82 100644 --- a/src/Squidex.Infrastructure/Language.cs +++ b/src/Squidex.Infrastructure/Language.cs @@ -103,9 +103,7 @@ namespace Squidex.Infrastructure input = match.Groups[0].Value; } - Language result; - - if (TryGetLanguage(input.ToLowerInvariant(), out result)) + if (TryGetLanguage(input.ToLowerInvariant(), out Language result)) { return result; } diff --git a/src/Squidex.Infrastructure/PropertiesBag.cs b/src/Squidex.Infrastructure/PropertiesBag.cs index ee1fad584..959fbba84 100644 --- a/src/Squidex.Infrastructure/PropertiesBag.cs +++ b/src/Squidex.Infrastructure/PropertiesBag.cs @@ -99,9 +99,7 @@ namespace Squidex.Infrastructure throw new ArgumentException($"The property names '{newPropertyName}' are equal.", newPropertyName); } - PropertyValue property; - - if (!internalDictionary.TryGetValue(oldPropertyName, out property)) + if (!internalDictionary.TryGetValue(oldPropertyName, out PropertyValue property)) { return false; } diff --git a/src/Squidex.Infrastructure/PropertyValue.cs b/src/Squidex.Infrastructure/PropertyValue.cs index fa985db39..dbfabcfea 100644 --- a/src/Squidex.Infrastructure/PropertyValue.cs +++ b/src/Squidex.Infrastructure/PropertyValue.cs @@ -58,9 +58,7 @@ namespace Squidex.Infrastructure { result = null; - Func parser; - - if (!Parsers.TryGetValue(binder.Type, out parser)) + if (!Parsers.TryGetValue(binder.Type, out Func parser)) { return false; } @@ -147,16 +145,12 @@ namespace Squidex.Infrastructure private T? ToNullableOrParseValue(IFormatProvider culture, Func parser) where T : struct { - T result; - - return TryParse(culture, parser, out result) ? result : (T?)null; + return TryParse(culture, parser, out T result) ? result : (T?)null; } private T ToOrParseValue(IFormatProvider culture, Func parser) { - T result; - - return TryParse(culture, parser, out result) ? result : default(T); + return TryParse(culture, parser, out T result) ? result : default(T); } private bool TryParse(IFormatProvider culture, Func parser, out T result) diff --git a/src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs b/src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs index e3c0db2eb..2dbd0be53 100644 --- a/src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs +++ b/src/Squidex.Infrastructure/Reflection/PropertiesTypeAccessor.cs @@ -68,9 +68,7 @@ namespace Squidex.Infrastructure.Reflection { Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); - IPropertyAccessor accessor; - - if (!accessors.TryGetValue(propertyName, out accessor)) + if (!accessors.TryGetValue(propertyName, out IPropertyAccessor accessor)) { throw new ArgumentException("Property does not exist.", nameof(propertyName)); } diff --git a/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index d7ce08816..64fe379bd 100644 --- a/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -2,6 +2,10 @@ netstandard1.6 + + full + True + diff --git a/src/Squidex.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs b/src/Squidex.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs index 0387aa29f..e972b1b3f 100644 --- a/src/Squidex.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs +++ b/src/Squidex.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs @@ -29,9 +29,7 @@ namespace Squidex.Read.MongoDb.Contents.Visitors if (propertyNames.Length == 3) { - Field field; - - if (!schema.FieldsByName.TryGetValue(propertyNames[1], out field)) + if (!schema.FieldsByName.TryGetValue(propertyNames[1], out Field field)) { throw new NotSupportedException(); } diff --git a/src/Squidex.Read.MongoDb/Squidex.Read.MongoDb.csproj b/src/Squidex.Read.MongoDb/Squidex.Read.MongoDb.csproj index a5c3703d7..ab4e5369b 100644 --- a/src/Squidex.Read.MongoDb/Squidex.Read.MongoDb.csproj +++ b/src/Squidex.Read.MongoDb/Squidex.Read.MongoDb.csproj @@ -3,6 +3,10 @@ netstandard1.6 $(PackageTargetFallback);dnxcore50 + + full + True + diff --git a/src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs b/src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs index 644289010..69c3f1493 100644 --- a/src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs +++ b/src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs @@ -35,9 +35,7 @@ namespace Squidex.Read.Apps.Services.Implementations { var cacheKey = BuildIdCacheKey(appId); - IAppEntity result; - - if (!Cache.TryGetValue(cacheKey, out result)) + if (!Cache.TryGetValue(cacheKey, out IAppEntity result)) { result = await repository.FindAppAsync(appId); @@ -58,9 +56,7 @@ namespace Squidex.Read.Apps.Services.Implementations var cacheKey = BuildNameCacheKey(name); - IAppEntity result; - - if (!Cache.TryGetValue(cacheKey, out result)) + if (!Cache.TryGetValue(cacheKey, out IAppEntity result)) { result = await repository.FindAppAsync(name); diff --git a/src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs b/src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs index 6899bc874..85cd97588 100644 --- a/src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs +++ b/src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs @@ -36,9 +36,7 @@ namespace Squidex.Read.Schemas.Services.Implementations { var cacheKey = BuildIdCacheKey(id); - ISchemaEntityWithSchema result; - - if (!Cache.TryGetValue(cacheKey, out result)) + if (!Cache.TryGetValue(cacheKey, out ISchemaEntityWithSchema result)) { result = await repository.FindSchemaAsync(id); @@ -59,9 +57,7 @@ namespace Squidex.Read.Schemas.Services.Implementations var cacheKey = BuildNameCacheKey(appId, name); - ISchemaEntityWithSchema result; - - if (!Cache.TryGetValue(cacheKey, out result)) + if (!Cache.TryGetValue(cacheKey, out ISchemaEntityWithSchema result)) { result = await repository.FindSchemaAsync(appId, name); diff --git a/src/Squidex.Read/Squidex.Read.csproj b/src/Squidex.Read/Squidex.Read.csproj index 700fa1891..911f23a17 100644 --- a/src/Squidex.Read/Squidex.Read.csproj +++ b/src/Squidex.Read/Squidex.Read.csproj @@ -3,6 +3,10 @@ netstandard1.6 $(PackageTargetFallback);dnxcore50 + + full + True + diff --git a/src/Squidex.Write/Apps/AppContributors.cs b/src/Squidex.Write/Apps/AppContributors.cs index b7627bc5f..06ea7a56c 100644 --- a/src/Squidex.Write/Apps/AppContributors.cs +++ b/src/Squidex.Write/Apps/AppContributors.cs @@ -50,9 +50,7 @@ namespace Squidex.Write.Apps private void ThrowIfFound(string contributorId, PermissionLevel permission, Func message) { - PermissionLevel currentPermission; - - if (contributors.TryGetValue(contributorId, out currentPermission) && currentPermission == permission) + if (contributors.TryGetValue(contributorId, out PermissionLevel currentPermission) && currentPermission == permission) { var error = new ValidationError("Contributor is already part of the app with same permissions", "ContributorId"); diff --git a/src/Squidex.Write/Contents/ContentCommandHandler.cs b/src/Squidex.Write/Contents/ContentCommandHandler.cs index f6423740b..e63785ca8 100644 --- a/src/Squidex.Write/Contents/ContentCommandHandler.cs +++ b/src/Squidex.Write/Contents/ContentCommandHandler.cs @@ -96,13 +96,18 @@ namespace Squidex.Write.Contents await Task.WhenAll(taskForApp, taskForSchema); - var errors = new List(); + var languages = new HashSet(taskForApp.Result.Languages); - await taskForSchema.Result.Schema.ValidateAsync(command.Data, errors, new HashSet(taskForApp.Result.Languages)); + var schemaObject = taskForSchema.Result.Schema; + var schemaErrors = new List(); - if (errors.Count > 0) + await schemaObject.ValidateAsync(command.Data, schemaErrors, languages); + + schemaObject.Enrich(command.Data, languages); + + if (schemaErrors.Count > 0) { - throw new ValidationException(message(), errors); + throw new ValidationException(message(), schemaErrors); } } } diff --git a/src/Squidex.Write/Schemas/SchemaDomainObject.cs b/src/Squidex.Write/Schemas/SchemaDomainObject.cs index 44fb38f83..edaf8d9f3 100644 --- a/src/Squidex.Write/Schemas/SchemaDomainObject.cs +++ b/src/Squidex.Write/Schemas/SchemaDomainObject.cs @@ -240,9 +240,7 @@ namespace Squidex.Write.Schemas { SimpleMapper.Map(fieldCommand, @event); - Field field; - - if (schema.Fields.TryGetValue(fieldCommand.FieldId, out field)) + if (schema.Fields.TryGetValue(fieldCommand.FieldId, out Field field)) { @event.FieldId = new NamedId(field.Id, field.Name); } diff --git a/src/Squidex.Write/Squidex.Write.csproj b/src/Squidex.Write/Squidex.Write.csproj index afc5ca9a8..719c4a55d 100644 --- a/src/Squidex.Write/Squidex.Write.csproj +++ b/src/Squidex.Write/Squidex.Write.csproj @@ -3,6 +3,10 @@ netstandard1.6 $(PackageTargetFallback);dnxcore50 + + full + True + diff --git a/src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs b/src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs index 7a9cd9865..b928fea69 100644 --- a/src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs +++ b/src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs @@ -37,9 +37,7 @@ namespace Squidex.Config.Swagger { var statusCode = match.Groups["Code"].Value; - SwaggerResponse response; - - if (!operation.Responses.TryGetValue(statusCode, out response)) + if (!operation.Responses.TryGetValue(statusCode, out SwaggerResponse response)) { response = new SwaggerResponse(); @@ -83,9 +81,7 @@ namespace Squidex.Config.Swagger private static void RemoveOkResponse(SwaggerOperation operation) { - SwaggerResponse response; - - if (operation.Responses.TryGetValue("200", out response) && + if (operation.Responses.TryGetValue("200", out SwaggerResponse response) && response.Description != null && response.Description.Contains("=>")) { diff --git a/src/Squidex/Controllers/Api/Apps/Models/ClientDto.cs b/src/Squidex/Controllers/Api/Apps/Models/ClientDto.cs index 50bc49ea9..63ac305e0 100644 --- a/src/Squidex/Controllers/Api/Apps/Models/ClientDto.cs +++ b/src/Squidex/Controllers/Api/Apps/Models/ClientDto.cs @@ -6,7 +6,6 @@ // All rights reserved. // ========================================================================== -using System; using System.ComponentModel.DataAnnotations; namespace Squidex.Controllers.Api.Apps.Models diff --git a/src/Squidex/Controllers/Api/Schemas/Models/Converters/SchemaConverter.cs b/src/Squidex/Controllers/Api/Schemas/Models/Converters/SchemaConverter.cs index 4a21552a6..11d8d71a2 100644 --- a/src/Squidex/Controllers/Api/Schemas/Models/Converters/SchemaConverter.cs +++ b/src/Squidex/Controllers/Api/Schemas/Models/Converters/SchemaConverter.cs @@ -23,6 +23,10 @@ namespace Squidex.Controllers.Api.Schemas.Models.Converters typeof(NumberFieldProperties), p => Convert((NumberFieldProperties)p) }, + { + typeof(DateTimeFieldProperties), + p => Convert((DateTimeFieldProperties)p) + }, { typeof(StringFieldProperties), p => Convert((StringFieldProperties)p) @@ -63,6 +67,13 @@ namespace Squidex.Controllers.Api.Schemas.Models.Converters return result; } + private static FieldPropertiesDto Convert(DateTimeFieldProperties source) + { + var result = SimpleMapper.Map(source, new DateTimeFieldPropertiesDto()); + + return result; + } + private static FieldPropertiesDto Convert(StringFieldProperties source) { var result = SimpleMapper.Map(source, new StringFieldPropertiesDto()); diff --git a/src/Squidex/Controllers/Api/Schemas/Models/DateTimeFieldPropertiesDto.cs b/src/Squidex/Controllers/Api/Schemas/Models/DateTimeFieldPropertiesDto.cs new file mode 100644 index 000000000..d25a79fdf --- /dev/null +++ b/src/Squidex/Controllers/Api/Schemas/Models/DateTimeFieldPropertiesDto.cs @@ -0,0 +1,49 @@ +// ========================================================================== +// DateTimeFieldPropertiesDto.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Immutable; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using NJsonSchema.Annotations; +using Squidex.Core.Schemas; +using Squidex.Infrastructure.Reflection; + +namespace Squidex.Controllers.Api.Schemas.Models +{ + [JsonSchema("dateTime")] + public sealed class DateTimeFieldPropertiesDto : FieldPropertiesDto + { + /// + /// The default value for the field value. + /// + public string DefaultValue { get; set; } + + /// + /// The maximum allowed value for the field value. + /// + public string MaxValue { get; set; } + + /// + /// The minimum allowed value for the field value. + /// + public string MinValue { get; set; } + + /// + /// The editor that is used to manage this field. + /// + [JsonConverter(typeof(StringEnumConverter))] + public DateTimeFieldEditor Editor { get; set; } + + public override FieldProperties ToProperties() + { + var result = SimpleMapper.Map(this, new DateTimeFieldProperties()); + + return result; + } + } +} diff --git a/src/Squidex/Controllers/Api/Schemas/Models/FieldPropertiesDto.cs b/src/Squidex/Controllers/Api/Schemas/Models/FieldPropertiesDto.cs index 2f03cabf3..1d27e260f 100644 --- a/src/Squidex/Controllers/Api/Schemas/Models/FieldPropertiesDto.cs +++ b/src/Squidex/Controllers/Api/Schemas/Models/FieldPropertiesDto.cs @@ -16,6 +16,7 @@ namespace Squidex.Controllers.Api.Schemas.Models { [JsonConverter(typeof(JsonInheritanceConverter), "fieldType")] [KnownType(typeof(BooleanFieldPropertiesDto))] + [KnownType(typeof(DateTimeFieldPropertiesDto))] [KnownType(typeof(NumberFieldPropertiesDto))] [KnownType(typeof(StringFieldPropertiesDto))] public abstract class FieldPropertiesDto diff --git a/tests/RunCoverage.bat b/tests/RunCoverage.bat index 72c6e8605..7a3ff7c7c 100644 --- a/tests/RunCoverage.bat +++ b/tests/RunCoverage.bat @@ -19,7 +19,7 @@ exit /b %errorlevel% "%UserProfile%\.nuget\packages\OpenCover\4.6.519\tools\OpenCover.Console.exe" ^ -register:user ^ -target:"C:\Program Files\dotnet\dotnet.exe" ^ --targetargs:"test %~dp0\Squidex.Infrastructure.Tests" ^ +-targetargs:"test %~dp0\Squidex.Infrastructure.Tests\Squidex.Infrastructure.Tests.csproj" ^ -filter:"+[Squidex*]*" ^ -skipautoprops ^ -output:"%~dp0\GeneratedReports\Infrastructure.xml" ^ @@ -28,7 +28,7 @@ exit /b %errorlevel% "%UserProfile%\.nuget\packages\OpenCover\4.6.519\tools\OpenCover.Console.exe" ^ -register:user ^ -target:"C:\Program Files\dotnet\dotnet.exe" ^ --targetargs:"test %~dp0\Squidex.Core.Tests" ^ +-targetargs:"test %~dp0\Squidex.Core.Tests\Squidex.Core.Tests.csproj" ^ -filter:"+[Squidex*]*" ^ -skipautoprops ^ -output:"%~dp0\GeneratedReports\Core.xml" ^ @@ -37,7 +37,7 @@ exit /b %errorlevel% "%UserProfile%\.nuget\packages\OpenCover\4.6.519\tools\OpenCover.Console.exe" ^ -register:user ^ -target:"C:\Program Files\dotnet\dotnet.exe" ^ --targetargs:"test %~dp0\Squidex.Write.Tests" ^ +-targetargs:"test %~dp0\Squidex.Write.Tests\Squidex.Write.Tests.csproj" ^ -filter:"+[Squidex*]*" ^ -skipautoprops ^ -output:"%~dp0\GeneratedReports\Write.xml" ^ @@ -46,7 +46,7 @@ exit /b %errorlevel% "%UserProfile%\.nuget\packages\OpenCover\4.6.519\tools\OpenCover.Console.exe" ^ -register:user ^ -target:"C:\Program Files\dotnet\dotnet.exe" ^ --targetargs:"test %~dp0\Squidex.Read.Tests" ^ +-targetargs:"test %~dp0\Squidex.Read.Tests\Squidex.Read.Tests.csproj" ^ -filter:"+[Squidex*]*" ^ -skipautoprops ^ -output:"%~dp0\GeneratedReports\Read.xml" ^ diff --git a/tests/Squidex.Core.Tests/Schemas/BooleanFieldTests.cs b/tests/Squidex.Core.Tests/Schemas/BooleanFieldTests.cs index 207a6a831..ce168fba0 100644 --- a/tests/Squidex.Core.Tests/Schemas/BooleanFieldTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/BooleanFieldTests.cs @@ -21,9 +21,9 @@ namespace Squidex.Core.Schemas [Fact] public void Should_instantiate_field() { - var sut = new BooleanField(1, "name", new BooleanFieldProperties()); + var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties()); - Assert.Equal("name", sut.Name); + Assert.Equal("my-bolean", sut.Name); } [Fact] @@ -37,7 +37,7 @@ namespace Squidex.Core.Schemas [Fact] public async Task Should_not_add_error_if_valid_null() { - var sut = new BooleanField(1, "name", new BooleanFieldProperties { Label = "Name" }); + var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties { Label = "My-Boolean" }); await sut.ValidateAsync(CreateValue(null), errors); @@ -47,7 +47,7 @@ namespace Squidex.Core.Schemas [Fact] public async Task Should_not_add_error_if_valid() { - var sut = new BooleanField(1, "name", new BooleanFieldProperties { Label = "Name" }); + var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties { Label = "My-Boolean" }); await sut.ValidateAsync(CreateValue(true), errors); @@ -57,23 +57,23 @@ namespace Squidex.Core.Schemas [Fact] public async Task Should_add_errors_if_boolean_is_required() { - var sut = new BooleanField(1, "name", new BooleanFieldProperties { Label = "Name", IsRequired = true }); + var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties { Label = "My-Boolean", IsRequired = true }); await sut.ValidateAsync(CreateValue(null), errors); errors.ShouldBeEquivalentTo( - new[] { "Name is required" }); + new[] { "My-Boolean is required" }); } [Fact] public async Task Should_add_errors_if_value_is_not_valid() { - var sut = new BooleanField(1, "name", new BooleanFieldProperties { Label = "Name" }); + var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties { Label = "My-Boolean" }); await sut.ValidateAsync(CreateValue("Invalid"), errors); errors.ShouldBeEquivalentTo( - new[] { "Name is not a valid value" }); + new[] { "My-Boolean is not a valid value" }); } private static JValue CreateValue(object v) diff --git a/tests/Squidex.Core.Tests/Schemas/DateTimeFieldPropertiesTests.cs b/tests/Squidex.Core.Tests/Schemas/DateTimeFieldPropertiesTests.cs new file mode 100644 index 000000000..31ff30be0 --- /dev/null +++ b/tests/Squidex.Core.Tests/Schemas/DateTimeFieldPropertiesTests.cs @@ -0,0 +1,141 @@ +// ========================================================================== +// DateTimeFieldPropertiesTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using FluentAssertions; +using Squidex.Infrastructure; +using Xunit; + +namespace Squidex.Core.Schemas +{ + public class DateTimeFieldPropertiesTests + { + private readonly List errors = new List(); + + [Fact] + public void Should_not_add_error_if_sut_is_valid() + { + var sut = new DateTimeFieldProperties + { + MinValue = FutureDays(10), + MaxValue = FutureDays(20), + DefaultValue = FutureDays(15) + }; + + sut.Validate(errors); + + Assert.Equal(0, errors.Count); + } + + [Fact] + public void Should_add_error_if_default_value_is_less_than_min() + { + var sut = new DateTimeFieldProperties { MinValue = FutureDays(10), DefaultValue = FutureDays(5) }; + + sut.Validate(errors); + + errors.ShouldBeEquivalentTo( + new List + { + new ValidationError("Default value must be greater than min value", "DefaultValue") + }); + } + + [Fact] + public void Should_add_error_if_default_value_is_greater_than_min() + { + var sut = new DateTimeFieldProperties { MaxValue = FutureDays(10), DefaultValue = FutureDays(15) }; + + sut.Validate(errors); + + errors.ShouldBeEquivalentTo( + new List + { + new ValidationError("Default value must be less than max value", "DefaultValue") + }); + } + + [Fact] + public void Should_add_error_if_min_greater_than_max() + { + var sut = new DateTimeFieldProperties { MinValue = FutureDays(10), MaxValue = FutureDays(5) }; + + sut.Validate(errors); + + errors.ShouldBeEquivalentTo( + new List + { + new ValidationError("Max value must be greater than min value", "MinValue", "MaxValue") + }); + } + + [Fact] + public void Should_add_error_if_editor_is_not_valid() + { + var sut = new DateTimeFieldProperties { Editor = (DateTimeFieldEditor)123 }; + + sut.Validate(errors); + + errors.ShouldBeEquivalentTo( + new List + { + new ValidationError("Editor ist not a valid value", "Editor") + }); + } + + [Fact] + public void Should_set_or_freeze_sut() + { + var sut = new DateTimeFieldProperties(); + + foreach (var property in sut.GetType().GetRuntimeProperties().Where(x => x.Name != "IsFrozen")) + { + var value = + property.PropertyType.GetTypeInfo().IsValueType ? + Activator.CreateInstance(property.PropertyType) : + null; + + property.SetValue(sut, value); + + var result = property.GetValue(sut); + + Assert.Equal(value, result); + } + + sut.Freeze(); + + foreach (var property in sut.GetType().GetRuntimeProperties().Where(x => x.Name != "IsFrozen")) + { + var value = + property.PropertyType.GetTypeInfo().IsValueType ? + Activator.CreateInstance(property.PropertyType) : + null; + + Assert.Throws(() => + { + try + { + property.SetValue(sut, value); + } + catch (Exception ex) + { + throw ex.InnerException; + } + }); + } + } + + private static DateTimeOffset FutureDays(int days) + { + return DateTimeOffset.UtcNow.AddDays(days); + } + } +} \ No newline at end of file diff --git a/tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs b/tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs new file mode 100644 index 000000000..03a0aad2b --- /dev/null +++ b/tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs @@ -0,0 +1,102 @@ +// ========================================================================== +// DateTimeFieldTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Squidex.Core.Schemas +{ + public class DateTimeFieldTests + { + private readonly List errors = new List(); + + [Fact] + public void Should_instantiate_field() + { + var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties()); + + Assert.Equal("my-datetime", sut.Name); + } + + [Fact] + public void Should_clone_object() + { + var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties()); + + Assert.NotEqual(sut, sut.Enable()); + } + + [Fact] + public async Task Should_not_add_error_if_valid() + { + var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties { Label = "My-DateTime" }); + + await sut.ValidateAsync(CreateValue(null), errors); + + Assert.Empty(errors); + } + + [Fact] + public async Task Should_add_errors_if_datetime_is_required() + { + var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties { Label = "My-DateTime", IsRequired = true }); + + await sut.ValidateAsync(CreateValue(null), errors); + + errors.ShouldBeEquivalentTo( + new[] { "My-DateTime is required" }); + } + + [Fact] + public async Task Should_add_errors_if_datetime_is_less_than_min() + { + var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties { Label = "My-DateTime", MinValue = FutureDays(10) }); + + await sut.ValidateAsync(CreateValue(FutureDays(0)), errors); + + errors.ShouldBeEquivalentTo( + new[] { $"My-DateTime must be greater than '{DateTimeOffset.UtcNow.AddDays(10)}'" }); + } + + [Fact] + public async Task Should_add_errors_if_datetime_is_greater_than_max() + { + var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties { Label = "My-DateTime", MaxValue = FutureDays(10) }); + + await sut.ValidateAsync(CreateValue(FutureDays(20)), errors); + + errors.ShouldBeEquivalentTo( + new[] { $"My-DateTime must be less than '{FutureDays(10)}'" }); + } + + [Fact] + public async Task Should_add_errors_if_value_is_not_valid() + { + var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties { Label = "My-DateTime" }); + + await sut.ValidateAsync(CreateValue("Invalid"), errors); + + errors.ShouldBeEquivalentTo( + new[] { "My-DateTime is not a valid value" }); + } + + private static DateTimeOffset FutureDays(int days) + { + return DateTimeOffset.UtcNow.AddDays(days); + } + + private static JValue CreateValue(object v) + { + return new JValue(v); + } + } +} diff --git a/tests/Squidex.Core.Tests/Schemas/FieldRegistryTests.cs b/tests/Squidex.Core.Tests/Schemas/FieldRegistryTests.cs index c06b8591e..7f5d439e1 100644 --- a/tests/Squidex.Core.Tests/Schemas/FieldRegistryTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/FieldRegistryTests.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json.Linq; using Squidex.Infrastructure; using Xunit; @@ -19,6 +20,11 @@ namespace Squidex.Core.Schemas private sealed class InvalidProperties : FieldProperties { + public override JToken GetDefaultValue() + { + return null; + } + protected override IEnumerable ValidateCore() { yield break; diff --git a/tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs b/tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs index 75e46e901..41069bcac 100644 --- a/tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs @@ -33,12 +33,14 @@ namespace Squidex.Core.Schemas.Json { var schema = Schema.Create("my-schema", new SchemaProperties()) - .AddOrUpdateField(new StringField(1, "field1", - new StringFieldProperties { Label = "Field1", Pattern = "[0-9]{3}" })).DisableField(1) - .AddOrUpdateField(new NumberField(2, "field2", - new NumberFieldProperties { Hints = "Hints" })) - .AddOrUpdateField(new BooleanField(3, "field3", + .AddOrUpdateField(new StringField(1, "my-string", + new StringFieldProperties { Label = "My-String", Pattern = "[0-9]{3}" })).DisableField(1) + .AddOrUpdateField(new NumberField(2, "my-number", + new NumberFieldProperties { Hints = "My-Number" })) + .AddOrUpdateField(new BooleanField(3, "my-boolean", new BooleanFieldProperties())).HideField(2) + .AddOrUpdateField(new DateTimeField(4, "my-datetime", + new DateTimeFieldProperties())).HideField(2) .Publish(); var deserialized = sut.Deserialize(sut.Serialize(schema)); diff --git a/tests/Squidex.Core.Tests/Schemas/NumberFieldPropertiesTests.cs b/tests/Squidex.Core.Tests/Schemas/NumberFieldPropertiesTests.cs index 017e38c57..e9199d4a3 100644 --- a/tests/Squidex.Core.Tests/Schemas/NumberFieldPropertiesTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/NumberFieldPropertiesTests.cs @@ -22,7 +22,7 @@ namespace Squidex.Core.Schemas private readonly List errors = new List(); [Fact] - public void Should_not_add_error_if_sut_are_valid() + public void Should_not_add_error_if_sut_is_valid() { var sut = new NumberFieldProperties { diff --git a/tests/Squidex.Core.Tests/Schemas/NumberFieldTests.cs b/tests/Squidex.Core.Tests/Schemas/NumberFieldTests.cs index 7d69308cb..e47f0d223 100644 --- a/tests/Squidex.Core.Tests/Schemas/NumberFieldTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/NumberFieldTests.cs @@ -22,15 +22,15 @@ namespace Squidex.Core.Schemas [Fact] public void Should_instantiate_field() { - var sut = new NumberField(1, "name", new NumberFieldProperties()); + var sut = new NumberField(1, "my-number", new NumberFieldProperties()); - Assert.Equal("name", sut.Name); + Assert.Equal("my-number", sut.Name); } [Fact] public void Should_clone_object() { - var sut = new NumberField(1, "name", new NumberFieldProperties()); + var sut = new NumberField(1, "my-number", new NumberFieldProperties()); Assert.NotEqual(sut, sut.Enable()); } @@ -38,7 +38,7 @@ namespace Squidex.Core.Schemas [Fact] public async Task Should_not_add_error_if_valid() { - var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name" }); + var sut = new NumberField(1, "my-number", new NumberFieldProperties { Label = "My-Number" }); await sut.ValidateAsync(CreateValue(null), errors); @@ -48,56 +48,56 @@ namespace Squidex.Core.Schemas [Fact] public async Task Should_add_errors_if_number_is_required() { - var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name", IsRequired = true }); + var sut = new NumberField(1, "my-number", new NumberFieldProperties { Label = "My-Number", IsRequired = true }); await sut.ValidateAsync(CreateValue(null), errors); errors.ShouldBeEquivalentTo( - new [] { "Name is required" }); + new [] { "My-Number is required" }); } [Fact] public async Task Should_add_errors_if_number_is_less_than_min() { - var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name", MinValue = 10 }); + var sut = new NumberField(1, "my-number", new NumberFieldProperties { Label = "My-Number", MinValue = 10 }); await sut.ValidateAsync(CreateValue(5), errors); errors.ShouldBeEquivalentTo( - new[] { "Name must be greater than '10'" }); + new[] { "My-Number must be greater than '10'" }); } [Fact] public async Task Should_add_errors_if_number_is_greater_than_max() { - var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name", MaxValue = 10 }); + var sut = new NumberField(1, "my-number", new NumberFieldProperties { Label = "My-Number", MaxValue = 10 }); await sut.ValidateAsync(CreateValue(20), errors); errors.ShouldBeEquivalentTo( - new[] { "Name must be less than '10'" }); + new[] { "My-Number must be less than '10'" }); } [Fact] public async Task Should_add_errors_if_number_is_not_allowed() { - var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name", AllowedValues = ImmutableList.Create(10d) }); + var sut = new NumberField(1, "my-number", new NumberFieldProperties { Label = "My-Number", AllowedValues = ImmutableList.Create(10d) }); await sut.ValidateAsync(CreateValue(20), errors); errors.ShouldBeEquivalentTo( - new[] { "Name is not an allowed value" }); + new[] { "My-Number is not an allowed value" }); } [Fact] public async Task Should_add_errors_if_value_is_not_valid() { - var sut = new NumberField(1, "name", new NumberFieldProperties { Label = "Name" }); + var sut = new NumberField(1, "my-number", new NumberFieldProperties { Label = "My-Number" }); await sut.ValidateAsync(CreateValue("Invalid"), errors); errors.ShouldBeEquivalentTo( - new[] { "Name is not a valid value" }); + new[] { "My-Number is not a valid value" }); } private static JValue CreateValue(object v) diff --git a/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs b/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs index 6eb45c718..9433c02ba 100644 --- a/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using Newtonsoft.Json.Linq; using Squidex.Infrastructure; using Xunit; @@ -20,6 +21,11 @@ namespace Squidex.Core.Schemas private sealed class InvalidProperties : FieldProperties { + public override JToken GetDefaultValue() + { + return null; + } + protected override IEnumerable ValidateCore() { yield break; @@ -247,26 +253,24 @@ namespace Squidex.Core.Schemas [Fact] public void Should_build_schema() { - var schema = - Schema.Create("user", new SchemaProperties { Hints = "The User" }) - .AddOrUpdateField(new StringField(1, "firstName", - new StringFieldProperties { Label = "FirstName", IsLocalizable = true, IsRequired = true, AllowedValues = new [] { "1", "2" }.ToImmutableList() })) - .AddOrUpdateField(new StringField(2, "lastName", - new StringFieldProperties { Hints = "Last Name" })) - .AddOrUpdateField(new BooleanField(3, "admin", - new BooleanFieldProperties())) - .AddOrUpdateField(new NumberField(4, "age", - new NumberFieldProperties { MinValue = 1, MaxValue = 10 })); - var languages = new HashSet(new[] { Language.DE, Language.EN }); - var json = schema.BuildSchema(languages, (n, s) => s).ToJson(); + var json = BuildMixedSchema().BuildSchema(languages, (n, s) => s).ToJson(); Assert.NotNull(json); } [Fact] public void Should_build_edm_model() + { + var languages = new HashSet(new[] { Language.DE, Language.EN }); + + var edmModel = BuildMixedSchema().BuildEdmType(languages, x => x); + + Assert.NotNull(edmModel); + } + + private Schema BuildMixedSchema() { var schema = Schema.Create("user", new SchemaProperties { Hints = "The User" }) @@ -276,14 +280,12 @@ namespace Squidex.Core.Schemas new StringFieldProperties { Hints = "Last Name" })) .AddOrUpdateField(new BooleanField(3, "admin", new BooleanFieldProperties())) - .AddOrUpdateField(new NumberField(4, "age", + .AddOrUpdateField(new DateTimeField(4, "birtday", + new DateTimeFieldProperties())) + .AddOrUpdateField(new NumberField(5, "age", new NumberFieldProperties { MinValue = 1, MaxValue = 10 })); - var languages = new HashSet(new[] { Language.DE, Language.EN }); - - var edmModel = schema.BuildEdmType(languages, x => x); - - Assert.NotNull(edmModel); + return schema; } private NumberField AddField() diff --git a/tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs b/tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs index 305e08793..cf85bf50a 100644 --- a/tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs @@ -6,6 +6,7 @@ // All rights reserved. // ========================================================================== +using System; using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; @@ -283,5 +284,40 @@ namespace Squidex.Core.Schemas new ValidationError("my-field has an unsupported language 'it'", "my-field") }); } + + [Fact] + private void Should_enrich_with_default_values() + { + var schema = + Schema.Create("my-schema", new SchemaProperties()) + .AddOrUpdateField( + new StringField(1, "my-string", new StringFieldProperties { DefaultValue = "EN-String", IsLocalizable = true })) + .AddOrUpdateField( + new BooleanField(2, "my-boolean", new BooleanFieldProperties { DefaultValue = true })) + .AddOrUpdateField( + new NumberField(3, "my-number", new NumberFieldProperties { DefaultValue = 123 })) + .AddOrUpdateField( + new DateTimeField(4, "my-datetime", new DateTimeFieldProperties { DefaultValue = DateTime.Today })); + + var data = + new ContentData() + .AddField("my-string", + new ContentFieldData() + .AddValue("de", "DE-String")) + .AddField("my-number", + new ContentFieldData() + .AddValue("iv", 456)); + + schema.Enrich(data, languages); + + Assert.Equal(456, (int)data["my-number"]["iv"]); + + Assert.Equal("DE-String", (string)data["my-string"]["de"]); + Assert.Equal("EN-String", (string)data["my-string"]["en"]); + + Assert.Equal(DateTime.Today, (DateTime)data["my-datetime"]["iv"]); + + Assert.Equal(true, (bool)data["my-boolean"]["iv"]); + } } } diff --git a/tests/Squidex.Core.Tests/Schemas/StringFieldTests.cs b/tests/Squidex.Core.Tests/Schemas/StringFieldTests.cs index e768903ef..0dc3aa169 100644 --- a/tests/Squidex.Core.Tests/Schemas/StringFieldTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/StringFieldTests.cs @@ -22,15 +22,15 @@ namespace Squidex.Core.Schemas [Fact] public void Should_instantiate_field() { - var sut = new StringField(1, "name", new StringFieldProperties()); + var sut = new StringField(1, "my-string", new StringFieldProperties()); - Assert.Equal("name", sut.Name); + Assert.Equal("my-string", sut.Name); } [Fact] public void Should_clone_object() { - var sut = new StringField(1, "name", new StringFieldProperties()); + var sut = new StringField(1, "my-string", new StringFieldProperties()); Assert.NotEqual(sut, sut.Enable()); } @@ -38,7 +38,7 @@ namespace Squidex.Core.Schemas [Fact] public async Task Should_not_add_error_if_valid() { - var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name" }); + var sut = new StringField(1, "my-string", new StringFieldProperties { Label = "My-String" }); await sut.ValidateAsync(CreateValue(null), errors); @@ -48,62 +48,62 @@ namespace Squidex.Core.Schemas [Fact] public async Task Should_add_errors_if_string_is_required() { - var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", IsRequired = true }); + var sut = new StringField(1, "my-string", new StringFieldProperties { Label = "My-String", IsRequired = true }); await sut.ValidateAsync(CreateValue(null), errors); errors.ShouldBeEquivalentTo( - new[] { "Name is required" }); + new[] { "My-String is required" }); } [Fact] public async Task Should_add_errors_if_string_is_shorter_than_min_length() { - var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", MinLength = 10 }); + var sut = new StringField(1, "my-string", new StringFieldProperties { Label = "My-String", MinLength = 10 }); await sut.ValidateAsync(CreateValue("123"), errors); errors.ShouldBeEquivalentTo( - new[] { "Name must have more than '10' characters" }); + new[] { "My-String must have more than '10' characters" }); } [Fact] public async Task Should_add_errors_if_string_is_longer_than_max_length() { - var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", MaxLength = 5 }); + var sut = new StringField(1, "my-string", new StringFieldProperties { Label = "My-String", MaxLength = 5 }); await sut.ValidateAsync(CreateValue("12345678"), errors); errors.ShouldBeEquivalentTo( - new[] { "Name must have less than '5' characters" }); + new[] { "My-String must have less than '5' characters" }); } [Fact] public async Task Should_add_errors_if_string_not_allowed() { - var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", AllowedValues = ImmutableList.Create("Foo") }); + var sut = new StringField(1, "my-string", new StringFieldProperties { Label = "My-String", AllowedValues = ImmutableList.Create("Foo") }); await sut.ValidateAsync(CreateValue("Bar"), errors); errors.ShouldBeEquivalentTo( - new[] { "Name is not an allowed value" }); + new[] { "My-String is not an allowed value" }); } [Fact] public async Task Should_add_errors_if_number_is_not_valid_pattern() { - var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", Pattern = "[0-9]{3}" }); + var sut = new StringField(1, "my-string", new StringFieldProperties { Label = "My-String", Pattern = "[0-9]{3}" }); await sut.ValidateAsync(CreateValue("abc"), errors); errors.ShouldBeEquivalentTo( - new[] { "Name is not valid" }); + new[] { "My-String is not valid" }); } [Fact] public async Task Should_add_errors_if_number_is_not_valid_pattern_with_message() { - var sut = new StringField(1, "name", new StringFieldProperties { Label = "Name", Pattern = "[0-9]{3}", PatternMessage = "Custom Error Message" }); + var sut = new StringField(1, "my-string", new StringFieldProperties { Label = "My-String", Pattern = "[0-9]{3}", PatternMessage = "Custom Error Message" }); await sut.ValidateAsync(CreateValue("abc"), errors); diff --git a/tests/Squidex.Read.Tests/Apps/CachingAppProviderTests.cs b/tests/Squidex.Read.Tests/Apps/CachingAppProviderTests.cs index 58bd97232..5f4429110 100644 --- a/tests/Squidex.Read.Tests/Apps/CachingAppProviderTests.cs +++ b/tests/Squidex.Read.Tests/Apps/CachingAppProviderTests.cs @@ -63,7 +63,7 @@ namespace Squidex.Read.Apps repository.Verify(x => x.FindAppAsync(appId.Id), Times.Never()); } - [Theory] + [Fact] public async Task Should_clear_cache_for_id_after_update_event() { var apps = ProviderResults(appV1, appV2); @@ -79,7 +79,7 @@ namespace Squidex.Read.Apps repository.Verify(x => x.FindAppAsync(appId.Id), Times.Exactly(2)); } - [Theory] + [Fact] public async Task Should_clear_cache_for_name_after_update_event() { var apps = ProviderResults(appV1, appV2); diff --git a/tests/Squidex.Read.Tests/Schemas/CachingSchemaProviderTests.cs b/tests/Squidex.Read.Tests/Schemas/CachingSchemaProviderTests.cs index 706625b7d..8c1ddb489 100644 --- a/tests/Squidex.Read.Tests/Schemas/CachingSchemaProviderTests.cs +++ b/tests/Squidex.Read.Tests/Schemas/CachingSchemaProviderTests.cs @@ -64,7 +64,7 @@ namespace Squidex.Read.Schemas repository.Verify(x => x.FindSchemaAsync(schemaId.Id), Times.Never()); } - [Theory] + [Fact] public async Task Should_clear_cache_for_id_after_update_event() { var schemas = ProviderResults(schemaV1, schemaV2); @@ -80,7 +80,7 @@ namespace Squidex.Read.Schemas repository.Verify(x => x.FindSchemaAsync(schemaId.Id), Times.Exactly(2)); } - [Theory] + [Fact] public async Task Should_clear_cache_for_name_after_update_event() { var schemas = ProviderResults(schemaV1, schemaV2); diff --git a/tests/Squidex.Write.Tests/Squidex.Write.Tests.csproj b/tests/Squidex.Write.Tests/Squidex.Write.Tests.csproj index acad1fcf1..b9ef1380c 100644 --- a/tests/Squidex.Write.Tests/Squidex.Write.Tests.csproj +++ b/tests/Squidex.Write.Tests/Squidex.Write.Tests.csproj @@ -5,6 +5,10 @@ $(PackageTargetFallback);dnxcore50 Squidex.Write + + full + True +