mirror of https://github.com/Squidex/squidex.git
59 changed files with 755 additions and 189 deletions
@ -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<DateTimeFieldProperties> |
|||
{ |
|||
public DateTimeField(long id, string name, DateTimeFieldProperties properties) |
|||
: base(id, name, properties) |
|||
{ |
|||
} |
|||
|
|||
protected override IEnumerable<IValidator> CreateValidators() |
|||
{ |
|||
if (Properties.IsRequired) |
|||
{ |
|||
yield return new RequiredValidator(); |
|||
} |
|||
|
|||
if (Properties.MinValue.HasValue || Properties.MaxValue.HasValue) |
|||
{ |
|||
yield return new RangeValidator<DateTimeOffset>(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); |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -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<ValidationError> 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)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// The default value for the field value.
|
|||
/// </summary>
|
|||
public string DefaultValue { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The maximum allowed value for the field value.
|
|||
/// </summary>
|
|||
public string MaxValue { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The minimum allowed value for the field value.
|
|||
/// </summary>
|
|||
public string MinValue { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The editor that is used to manage this field.
|
|||
/// </summary>
|
|||
[JsonConverter(typeof(StringEnumConverter))] |
|||
public DateTimeFieldEditor Editor { get; set; } |
|||
|
|||
public override FieldProperties ToProperties() |
|||
{ |
|||
var result = SimpleMapper.Map(this, new DateTimeFieldProperties()); |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -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<ValidationError> errors = new List<ValidationError>(); |
|||
|
|||
[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<ValidationError> |
|||
{ |
|||
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<ValidationError> |
|||
{ |
|||
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<ValidationError> |
|||
{ |
|||
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<ValidationError> |
|||
{ |
|||
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<InvalidOperationException>(() => |
|||
{ |
|||
try |
|||
{ |
|||
property.SetValue(sut, value); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
throw ex.InnerException; |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
private static DateTimeOffset FutureDays(int days) |
|||
{ |
|||
return DateTimeOffset.UtcNow.AddDays(days); |
|||
} |
|||
} |
|||
} |
|||
@ -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<string> errors = new List<string>(); |
|||
|
|||
[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); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue