Browse Source

Tag field

pull/141/head
Sebastian Stehle 8 years ago
parent
commit
181729fe6f
  1. 5
      src/Squidex.Domain.Apps.Core/Schemas/Edm/EdmTypeVisitor.cs
  2. 4
      src/Squidex.Domain.Apps.Core/Schemas/FieldRegistry.cs
  3. 2
      src/Squidex.Domain.Apps.Core/Schemas/IFieldPropertiesVisitor.cs
  4. 2
      src/Squidex.Domain.Apps.Core/Schemas/IFieldVisitor.cs
  5. 11
      src/Squidex.Domain.Apps.Core/Schemas/JsonSchema/JsonTypeVisitor.cs
  6. 50
      src/Squidex.Domain.Apps.Core/Schemas/TagsField.cs
  7. 58
      src/Squidex.Domain.Apps.Core/Schemas/TagsFieldProperties.cs
  8. 2
      tests/Squidex.Domain.Apps.Core.Tests/Schemas/Json/JsonSerializerTests.cs
  9. 4
      tests/Squidex.Domain.Apps.Core.Tests/Schemas/SchemaTests.cs
  10. 121
      tests/Squidex.Domain.Apps.Core.Tests/Schemas/TagsFieldTests.cs

5
src/Squidex.Domain.Apps.Core/Schemas/Edm/EdmTypeVisitor.cs

@ -63,6 +63,11 @@ namespace Squidex.Domain.Apps.Core.Schemas.Edm
return CreatePrimitive(EdmPrimitiveTypeKind.String, field);
}
public IEdmTypeReference Visit(TagsField field)
{
return CreatePrimitive(EdmPrimitiveTypeKind.String, field);
}
private static IEdmTypeReference CreatePrimitive(EdmPrimitiveTypeKind kind, Field field)
{
return EdmCoreModel.Instance.GetPrimitive(kind, !field.RawProperties.IsRequired);

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

@ -79,6 +79,10 @@ namespace Squidex.Domain.Apps.Core.Schemas
(id, name, partitioning, properties) =>
new DateTimeField(id, name, partitioning, (DateTimeFieldProperties)properties));
Add<TagsFieldProperties>(
(id, name, partitioning, properties) =>
new TagsField(id, name, partitioning, (TagsFieldProperties)properties));
typeNameRegistry.MapObsolete(typeof(ReferencesFieldProperties), "DateTime");
typeNameRegistry.MapObsolete(typeof(DateTimeFieldProperties), "References");

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

@ -25,5 +25,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
T Visit(ReferencesFieldProperties properties);
T Visit(StringFieldProperties properties);
T Visit(TagsFieldProperties properties);
}
}

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

@ -25,5 +25,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
T Visit(ReferencesField field);
T Visit(StringField field);
T Visit(TagsField field);
}
}

11
src/Squidex.Domain.Apps.Core/Schemas/JsonSchema/JsonTypeVisitor.cs

@ -139,6 +139,17 @@ namespace Squidex.Domain.Apps.Core.Schemas.JsonSchema
});
}
public JsonProperty Visit(TagsField field)
{
return CreateProperty(field, jsonProperty =>
{
var itemSchema = schemaResolver("TagsItem", new JsonSchema4 { Type = JsonObjectType.String });
jsonProperty.Type = JsonObjectType.Array;
jsonProperty.Item = itemSchema;
});
}
private static JsonProperty CreateProperty(Field field, Action<JsonProperty> updater)
{
var property = new JsonProperty { IsRequired = field.RawProperties.IsRequired };

50
src/Squidex.Domain.Apps.Core/Schemas/TagsField.cs

@ -0,0 +1,50 @@
// ==========================================================================
// TagsField.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using System.Collections.Immutable;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas.Validators;
namespace Squidex.Domain.Apps.Core.Schemas
{
public sealed class TagsField : Field<TagsFieldProperties>
{
private static readonly ImmutableList<string> EmptyTags = ImmutableList<string>.Empty;
public TagsField(long id, string name, Partitioning partitioning)
: this(id, name, partitioning, new TagsFieldProperties())
{
}
public TagsField(long id, string name, Partitioning partitioning, TagsFieldProperties properties)
: base(id, name, partitioning, properties)
{
}
protected override IEnumerable<IValidator> CreateValidators()
{
if (Properties.IsRequired || Properties.MinItems.HasValue || Properties.MaxItems.HasValue)
{
yield return new CollectionValidator<string>(Properties.IsRequired, Properties.MinItems, Properties.MaxItems);
}
yield return new CollectionItemValidator<string>(new RequiredStringValidator());
}
public override object ConvertValue(JToken value)
{
return value.ToObject<List<string>>();
}
public override T Accept<T>(IFieldVisitor<T> visitor)
{
return visitor.Visit(this);
}
}
}

58
src/Squidex.Domain.Apps.Core/Schemas/TagsFieldProperties.cs

@ -0,0 +1,58 @@
// ==========================================================================
// TagsField.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas
{
[TypeName(nameof(TagsField))]
public sealed class TagsFieldProperties : FieldProperties
{
private int? minItems;
private int? maxItems;
public int? MinItems
{
get
{
return minItems;
}
set
{
ThrowIfFrozen();
minItems = value;
}
}
public int? MaxItems
{
get
{
return maxItems;
}
set
{
ThrowIfFrozen();
maxItems = value;
}
}
public override JToken GetDefaultValue()
{
return new JArray();
}
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{
return visitor.Visit(this);
}
}
}

2
tests/Squidex.Domain.Apps.Core.Tests/Schemas/Json/JsonSerializerTests.cs

@ -68,6 +68,8 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
new ReferencesFieldProperties { SchemaId = Guid.NewGuid() }))
.AddField(new GeolocationField(10, "my-geolocation", Partitioning.Invariant,
new GeolocationFieldProperties()))
.AddField(new TagsField(11, "my-tags", Partitioning.Invariant,
new TagsFieldProperties()))
.Publish();
var deserialized = JToken.FromObject(schema, serializer).ToObject<Schema>(serializer);

4
tests/Squidex.Domain.Apps.Core.Tests/Schemas/SchemaTests.cs

@ -262,7 +262,9 @@ namespace Squidex.Domain.Apps.Core.Schemas
.AddField(new GeolocationField(9, "my-geolocation", Partitioning.Invariant,
new GeolocationFieldProperties()))
.AddField(new ReferencesField(10, "my-references", Partitioning.Invariant,
new ReferencesFieldProperties()));
new ReferencesFieldProperties()))
.AddField(new TagsField(11, "my-tags", Partitioning.Invariant,
new TagsFieldProperties()));
return schema;
}

121
tests/Squidex.Domain.Apps.Core.Tests/Schemas/TagsFieldTests.cs

@ -0,0 +1,121 @@
// ==========================================================================
// TagsFieldTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using Xunit;
namespace Squidex.Domain.Apps.Core.Schemas
{
public class TagsFieldTests
{
private readonly List<string> errors = new List<string>();
[Fact]
public void Should_instantiate_field()
{
var sut = new TagsField(1, "my-tags", Partitioning.Invariant);
Assert.Equal("my-tags", sut.Name);
}
[Fact]
public void Should_clone_object()
{
var sut = new TagsField(1, "my-tags", Partitioning.Invariant);
Assert.NotEqual(sut, sut.Enable());
}
[Fact]
public async Task Should_not_add_error_if_tags_are_valid()
{
var referenceId = Guid.NewGuid();
var sut = new TagsField(1, "my-tags", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(referenceId), errors, ValidationTestExtensions.ValidContext);
Assert.Empty(errors);
}
[Fact]
public async Task Should_not_add_error_if_tags_are_null_and_valid()
{
var sut = new TagsField(1, "my-tags", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(null), errors);
Assert.Empty(errors);
}
[Fact]
public async Task Should_add_errors_if_tags_are_required_and_null()
{
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), errors);
errors.ShouldBeEquivalentTo(
new[] { "<FIELD> is required." });
}
[Fact]
public async Task Should_add_errors_if_tags_are_required_and_empty()
{
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(), errors);
errors.ShouldBeEquivalentTo(
new[] { "<FIELD> is required." });
}
[Fact]
public async Task Should_add_errors_if_value_is_not_valid()
{
var sut = new TagsField(1, "my-tags", Partitioning.Invariant);
await sut.ValidateAsync("invalid", errors);
errors.ShouldBeEquivalentTo(
new[] { "<FIELD> is not a valid value." });
}
[Fact]
public async Task Should_add_errors_if_value_has_not_enough_items()
{
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { MinItems = 3 });
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors);
errors.ShouldBeEquivalentTo(
new[] { "<FIELD> must have at least 3 item(s)." });
}
[Fact]
public async Task Should_add_errors_if_value_has_too_much_items()
{
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { MaxItems = 1 });
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors);
errors.ShouldBeEquivalentTo(
new[] { "<FIELD> must have not more than 1 item(s)." });
}
private static JToken CreateValue(params Guid[] ids)
{
return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType<object>().ToArray());
}
}
}
Loading…
Cancel
Save