Browse Source

* Tests for array fields

* Conversions.
pull/297/head
Sebastian 8 years ago
parent
commit
a8a08884e8
  1. 6
      src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs
  2. 4
      src/Squidex.Domain.Apps.Core.Model/Contents/IdContentData.cs
  3. 4
      src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs
  4. 114
      src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs
  5. 124
      src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs
  6. 20
      src/Squidex.Domain.Apps.Core.Operations/ConvertContent/Value.cs
  7. 77
      src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs
  8. 13
      src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/FieldReferencesConverter.cs
  9. 96
      src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs
  10. 69
      src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs
  11. 101
      src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs
  12. 4
      src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentDataObject.cs
  13. 2
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs
  14. 2
      src/Squidex.Infrastructure.Redis/RedisPubSub.cs
  15. 12
      src/Squidex.Infrastructure/CollectionExtensions.cs
  16. 2
      src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs
  17. 2
      src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasSwaggerGenerator.cs
  18. 222
      tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/ArrayFieldTests.cs
  19. 75
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs
  20. 10
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs

6
src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs

@ -25,8 +25,8 @@ namespace Squidex.Domain.Apps.Core.Contents
{
}
protected ContentData(IDictionary<T, ContentFieldData> copy, IEqualityComparer<T> comparer)
: base(copy, comparer)
protected ContentData(int capacity, IEqualityComparer<T> comparer)
: base(capacity, comparer)
{
}
@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Core.Contents
{
foreach (var otherValue in source)
{
var fieldValue = target.GetOrAdd(otherValue.Key, x => new ContentFieldData());
var fieldValue = target.GetOrAddNew(otherValue.Key);
foreach (var value in otherValue.Value)
{

4
src/Squidex.Domain.Apps.Core.Model/Contents/IdContentData.cs

@ -18,8 +18,8 @@ namespace Squidex.Domain.Apps.Core.Contents
{
}
public IdContentData(IdContentData copy)
: base(copy, EqualityComparer<long>.Default)
public IdContentData(int capacity)
: base(capacity, EqualityComparer<long>.Default)
{
}

4
src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs

@ -18,8 +18,8 @@ namespace Squidex.Domain.Apps.Core.Contents
{
}
public NamedContentData(NamedContentData copy)
: base(copy, EqualityComparer<string>.Default)
public NamedContentData(int capacity)
: base(capacity, EqualityComparer<string>.Default)
{
}

114
src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs

@ -5,6 +5,9 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
@ -13,120 +16,83 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
{
public delegate ContentFieldData FieldConverter(ContentFieldData data, IRootField field);
public delegate JToken ValueConverter(JToken value, IRootField field);
public static class ContentConverter
{
public static NamedContentData ToNameModel(this IdContentData source, Schema schema, params FieldConverter[] converters)
public static NamedContentData ToNameModel(this IdContentData content, Schema schema, params FieldConverter[] converters)
{
Guard.NotNull(schema, nameof(schema));
var result = new NamedContentData();
foreach (var fieldValue in source)
{
if (!schema.FieldsById.TryGetValue(fieldValue.Key, out var field))
{
continue;
}
var fieldData = Convert(fieldValue.Value, field, converters);
if (fieldData != null)
{
result[field.Name] = fieldData;
}
}
var result = new NamedContentData(content.Count);
return result;
return ConvertInternal(content, result, schema.FieldsById, x => x.Name, converters);
}
public static IdContentData ToIdModel(this NamedContentData content, Schema schema, params FieldConverter[] converters)
{
Guard.NotNull(schema, nameof(schema));
var result = new IdContentData();
var result = new IdContentData(content.Count);
foreach (var fieldValue in content)
{
if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out var field))
{
continue;
}
var fieldData = Convert(fieldValue.Value, field, converters);
if (fieldData != null)
{
result[field.Id] = fieldData;
}
}
return result;
return ConvertInternal(content, result, schema.FieldsByName, x => x.Id, converters);
}
public static IdContentData Convert(this IdContentData content, Schema schema, params FieldConverter[] converters)
{
Guard.NotNull(schema, nameof(schema));
var result = new IdContentData();
var result = new IdContentData(content.Count);
foreach (var fieldValue in content)
{
if (!schema.FieldsById.TryGetValue(fieldValue.Key, out var field))
{
continue;
}
var fieldData = Convert(fieldValue.Value, field, converters);
if (fieldData != null)
{
result[field.Id] = fieldData;
}
}
return result;
return ConvertInternal(content, result, schema.FieldsById, x => x.Id, converters);
}
public static NamedContentData Convert(this NamedContentData content, Schema schema, params FieldConverter[] converters)
{
Guard.NotNull(schema, nameof(schema));
var result = new NamedContentData();
var result = new NamedContentData(content.Count);
foreach (var fieldValue in content)
return ConvertInternal(content, result, schema.FieldsByName, x => x.Name, converters);
}
private static TDict2 ConvertInternal<TKey1, TKey2, TDict1, TDict2>(
TDict1 source,
TDict2 target,
IReadOnlyDictionary<TKey1, RootField> fields,
Func<IRootField, TKey2> targetKey, params FieldConverter[] converters)
where TDict1 : IDictionary<TKey1, ContentFieldData>
where TDict2 : IDictionary<TKey2, ContentFieldData>
{
foreach (var fieldKvp in source)
{
if (!schema.FieldsByName.TryGetValue(fieldValue.Key, out var field))
if (!fields.TryGetValue(fieldKvp.Key, out var field))
{
continue;
}
var fieldData = Convert(fieldValue.Value, field, converters);
var fieldValue = fieldKvp.Value;
if (fieldData != null)
if (converters != null)
{
result[field.Name] = fieldData;
}
}
foreach (var converter in converters)
{
fieldValue = converter(fieldValue, field);
return result;
}
if (fieldValue == null)
{
break;
}
}
}
private static ContentFieldData Convert(ContentFieldData fieldData, IRootField field, FieldConverter[] converters)
{
if (converters != null)
{
foreach (var converter in converters)
if (fieldValue != null)
{
fieldData = converter(fieldData, field);
if (fieldData == null)
{
break;
}
target.Add(targetKey(field), fieldValue);
}
}
return fieldData;
return target;
}
}
}

124
src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs

@ -8,7 +8,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
@ -35,29 +34,23 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
{
return (data, field) =>
{
var isValid = true;
foreach (var value in data.Values)
{
if (value.IsNull())
{
continue;
}
try
{
if (!value.IsNull())
{
JsonValueConverter.ConvertValue(field, value);
}
JsonValueConverter.ConvertValue(field, value);
}
catch
{
isValid = false;
break;
return null;
}
}
if (!isValid)
{
return null;
}
return data;
};
}
@ -173,12 +166,10 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
return (data, field) => data;
}
var languageCodes =
new HashSet<string>(
languages.Select(x => x.Iso2Code).Where(x => languagesConfig.Contains(x)),
StringComparer.OrdinalIgnoreCase);
var languageCodes = languages.Select(x => x.Iso2Code).Where(x => languagesConfig.Contains(x));
var languageSet = new HashSet<string>(languageCodes, StringComparer.OrdinalIgnoreCase);
if (languageCodes.Count == 0)
if (languageSet.Count == 0)
{
return (data, field) => data;
}
@ -189,7 +180,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
{
var result = new ContentFieldData();
foreach (var languageCode in languageCodes)
foreach (var languageCode in languageSet)
{
if (data.TryGetValue(languageCode, out var value))
{
@ -204,58 +195,101 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
};
}
public static FieldConverter DecodeJson()
private static FieldConverter ForNested(params ValueConverter[] converters)
{
return (data, field) =>
{
if (field is IField<JsonFieldProperties>)
if (field is IArrayField arrayField)
{
var result = new ContentFieldData();
foreach (var partitionValue in data)
foreach (var partition in data)
{
if (partitionValue.Value.IsNull())
{
result[partitionValue.Key] = null;
}
else
if (partition.Value is JArray jArray)
{
var value = Encoding.UTF8.GetString(Convert.FromBase64String(partitionValue.Value.ToString()));
result[partitionValue.Key] = JToken.Parse(value);
for (var i = 0; i < jArray.Count; i++)
{
if (jArray[i] is JObject item)
{
var result = new JObject();
foreach (var kvp in item)
{
if (!arrayField.FieldsByName.TryGetValue(kvp.Key, out var nestedField))
{
continue;
}
var newValue = kvp.Value;
if (converters != null)
{
foreach (var converter in converters)
{
newValue = converter(newValue, field);
if (ReferenceEquals(newValue, Value.Unset))
{
break;
}
}
}
if (!ReferenceEquals(newValue, Value.Unset))
{
result.Add(field.Id.ToString(), newValue);
}
}
jArray[i] = item;
}
}
}
}
return result;
}
return data;
};
}
public static FieldConverter EncodeJson()
public static FieldConverter ForValues(params ValueConverter[] converters)
{
return (data, field) =>
{
if (field is IField<JsonFieldProperties>)
if (!(field is IArrayField))
{
var result = new ContentFieldData();
ContentFieldData result = null;
foreach (var partitionValue in data)
foreach (var partition in data)
{
if (partitionValue.Value.IsNull())
var newValue = partition.Value;
if (converters != null)
{
result[partitionValue.Key] = null;
foreach (var converter in converters)
{
newValue = converter(newValue, field);
if (ReferenceEquals(newValue, Value.Unset))
{
break;
}
}
}
else
if (result != null || !ReferenceEquals(newValue, partition.Value))
{
var value = Convert.ToBase64String(Encoding.UTF8.GetBytes(partitionValue.Value.ToString()));
if (result == null)
{
result = new ContentFieldData();
}
result[partitionValue.Key] = value;
if (!ReferenceEquals(newValue, Value.Unset))
{
result.Add(partition.Key, newValue);
}
}
}
return result;
return result ?? data;
}
return data;

20
src/Squidex.Domain.Apps.Core.Operations/ConvertContent/Value.cs

@ -0,0 +1,20 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json.Linq;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public sealed class Value
{
public static readonly JToken Unset = JValue.CreateUndefined();
private Value()
{
}
}
}

77
src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs

@ -0,0 +1,77 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Text;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public static class ValueConverters
{
public static ValueConverter DecodeJson()
{
return (value, field) =>
{
if (!value.IsNull() && field is IField<JsonFieldProperties>)
{
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(value.ToString()));
return JToken.Parse(decoded);
}
return value;
};
}
public static ValueConverter EncodeJson()
{
return (value, field) =>
{
if (!value.IsNull() && field is IField<JsonFieldProperties>)
{
var encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(value.ToString()));
return encoded;
}
return value;
};
}
public static ValueConverter ExcludeHidden()
{
return (value, field) =>
{
return field.IsHidden ? null : value;
};
}
public static ValueConverter ExcludeChangedTypes()
{
return (value, field) =>
{
try
{
if (!value.IsNull())
{
JsonValueConverter.ConvertValue(field, value);
}
}
catch
{
return null;
}
return value;
};
}
}
}

13
src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/FieldReferencesConverter.cs

@ -7,7 +7,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Infrastructure.Json;
@ -15,20 +14,18 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class FieldReferencesConverter
{
public static FieldConverter CleanReferences(IEnumerable<Guid> deletedReferencedIds)
public static ValueConverter CleanReferences(IEnumerable<Guid> deletedReferencedIds)
{
var ids = new HashSet<Guid>(deletedReferencedIds);
return (data, field) =>
return (value, field) =>
{
foreach (var partitionValue in data.Where(x => !x.Value.IsNull()).ToList())
if (value.IsNull())
{
var newValue = field.CleanReferences(partitionValue.Value, ids);
data[partitionValue.Key] = newValue;
return value;
}
return data;
return field.CleanReferences(value, ids);
};
}
}

96
src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs

@ -7,65 +7,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ReferencesCleaner
public sealed class ReferencesCleaner : IFieldVisitor<JToken>
{
private static readonly List<Guid> EmptyIds = new List<Guid>();
private readonly JToken value;
private readonly ICollection<Guid> oldReferences;
public static JToken CleanReferences(this IField field, JToken value, ISet<Guid> oldReferences)
private ReferencesCleaner(JToken value, ICollection<Guid> oldReferences)
{
if ((field is IField<AssetsFieldProperties> || field is IField<ReferencesFieldProperties>) && !value.IsNull())
{
switch (field)
{
case IField<AssetsFieldProperties> assetsField:
return Visit(assetsField, value, oldReferences);
case IField<ReferencesFieldProperties> referencesField:
return Visit(referencesField, value, oldReferences);
}
}
this.value = value;
return value;
this.oldReferences = oldReferences;
}
private static JToken Visit(IField<AssetsFieldProperties> field, JToken value, IEnumerable<Guid> oldReferences)
public static JToken CleanReferences(IField field, JToken value, ICollection<Guid> oldReferences)
{
var oldIds = GetIds(value);
var newIds = oldIds.Except(oldReferences).ToList();
return field.Accept(new ReferencesCleaner(value, oldReferences));
}
return oldIds.Count != newIds.Count ? JToken.FromObject(newIds) : value;
public JToken Visit(IArrayField field)
{
return value;
}
private static JToken Visit(IField<ReferencesFieldProperties> field, JToken value, ICollection<Guid> oldReferences)
public JToken Visit(IField<AssetsFieldProperties> field)
{
return CleanIds();
}
public JToken Visit(IField<ReferencesFieldProperties> field)
{
if (oldReferences.Contains(field.Properties.SchemaId))
{
return new JArray();
}
var oldIds = GetIds(value);
var newIds = oldIds.Except(oldReferences).ToList();
return oldIds.Count != newIds.Count ? JToken.FromObject(newIds) : value;
return CleanIds();
}
private static List<Guid> GetIds(JToken value)
private JToken CleanIds()
{
try
{
return value?.ToObject<List<Guid>>() ?? EmptyIds;
}
catch
var ids = value.ToGuidSet();
var isRemoved = false;
foreach (var oldReference in oldReferences)
{
return EmptyIds;
isRemoved |= ids.Remove(oldReference);
}
return isRemoved ? ids.ToJToken() : value;
}
public JToken Visit(IField<BooleanFieldProperties> field)
{
return value;
}
public JToken Visit(IField<DateTimeFieldProperties> field)
{
return value;
}
public JToken Visit(IField<GeolocationFieldProperties> field)
{
return value;
}
public JToken Visit(IField<JsonFieldProperties> field)
{
return value;
}
public JToken Visit(IField<NumberFieldProperties> field)
{
return value;
}
public JToken Visit(IField<StringFieldProperties> field)
{
return value;
}
public JToken Visit(IField<TagsFieldProperties> field)
{
return value;
}
}
}

69
src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs

@ -0,0 +1,69 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ReferencesExtensions
{
public static IEnumerable<Guid> ExtractReferences(this IField field, JToken value)
{
return ReferencesExtractor.ExtractReferences(field, value);
}
public static JToken CleanReferences(this IField field, JToken value, ICollection<Guid> oldReferences)
{
if (value.IsNull())
{
return value;
}
return ReferencesCleaner.CleanReferences(field, value, oldReferences);
}
public static JToken ToJToken(this HashSet<Guid> ids)
{
var result = new JArray();
foreach (var id in ids)
{
result.Add(new JValue(id));
}
return result;
}
public static HashSet<Guid> ToGuidSet(this JToken value)
{
if (value is JArray ids)
{
var result = new HashSet<Guid>();
foreach (var id in ids)
{
if (id.Type == JTokenType.Guid)
{
result.Add((Guid)id);
}
else if (id.Type == JTokenType.String && Guid.TryParse((string)id, out var guid))
{
result.Add(guid);
}
}
return result;
}
return new HashSet<Guid>();
}
}
}

101
src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs

@ -13,50 +13,93 @@ using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ReferencesExtractor
public sealed class ReferencesExtractor : IFieldVisitor<IEnumerable<Guid>>
{
public static IEnumerable<Guid> ExtractReferences(this IField field, JToken value)
{
switch (field)
{
case IField<AssetsFieldProperties> assetsField:
return Visit(assetsField, value);
private readonly JToken value;
case IField<ReferencesFieldProperties> referencesField:
return Visit(referencesField, value);
}
private ReferencesExtractor(JToken value)
{
this.value = value;
}
return Enumerable.Empty<Guid>();
public static IEnumerable<Guid> ExtractReferences(IField field, JToken value)
{
return field.Accept(new ReferencesExtractor(value));
}
public static IEnumerable<Guid> Visit(IField<AssetsFieldProperties> field, JToken value)
public IEnumerable<Guid> Visit(IArrayField field)
{
IEnumerable<Guid> result;
try
{
result = value?.ToObject<List<Guid>>();
}
catch
var result = new List<Guid>();
if (value is JArray items)
{
result = null;
foreach (JObject item in value)
{
foreach (var nestedField in field.Fields)
{
if (item.TryGetValue(field.Name, out var value))
{
result.AddRange(nestedField.Accept(new ReferencesExtractor(value)));
}
}
}
}
return result ?? Enumerable.Empty<Guid>();
return result;
}
private static IEnumerable<Guid> Visit(IField<ReferencesFieldProperties> field, JToken value)
public IEnumerable<Guid> Visit(IField<AssetsFieldProperties> field)
{
IEnumerable<Guid> result;
try
{
result = value?.ToObject<List<Guid>>() ?? Enumerable.Empty<Guid>();
}
catch
var ids = value.ToGuidSet();
return ids;
}
public IEnumerable<Guid> Visit(IField<ReferencesFieldProperties> field)
{
var ids = value.ToGuidSet();
if (field.Properties.SchemaId != Guid.Empty)
{
result = Enumerable.Empty<Guid>();
ids.Add(field.Properties.SchemaId);
}
return result.Union(new[] { field.Properties.SchemaId });
return ids;
}
public IEnumerable<Guid> Visit(IField<BooleanFieldProperties> field)
{
return Enumerable.Empty<Guid>();
}
public IEnumerable<Guid> Visit(IField<DateTimeFieldProperties> field)
{
return Enumerable.Empty<Guid>();
}
public IEnumerable<Guid> Visit(IField<GeolocationFieldProperties> field)
{
return Enumerable.Empty<Guid>();
}
public IEnumerable<Guid> Visit(IField<JsonFieldProperties> field)
{
return Enumerable.Empty<Guid>();
}
public IEnumerable<Guid> Visit(IField<NumberFieldProperties> field)
{
return Enumerable.Empty<Guid>();
}
public IEnumerable<Guid> Visit(IField<StringFieldProperties> field)
{
return Enumerable.Empty<Guid>();
}
public IEnumerable<Guid> Visit(IField<TagsFieldProperties> field)
{
return Enumerable.Empty<Guid>();
}
}
}

4
src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentDataObject.cs

@ -95,14 +95,14 @@ namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper
{
EnsurePropertiesInitialized();
fieldProperties.GetOrAdd(propertyName, x => new ContentDataProperty(this)).Value = value;
fieldProperties.GetOrAdd(propertyName, this, (k, c) => new ContentDataProperty(c)).Value = value;
}
public override PropertyDescriptor GetOwnProperty(string propertyName)
{
EnsurePropertiesInitialized();
return fieldProperties.GetOrAdd(propertyName, x => new ContentDataProperty(this, new ContentFieldObject(this, new ContentFieldData(), false)));
return fieldProperties.GetOrAdd(propertyName, this, (k, c) => new ContentDataProperty(c, new ContentFieldObject(c, new ContentFieldData(), false)));
}
public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()

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

@ -160,7 +160,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return null;
}
return schema != null ? contentDataTypes.GetOrAdd(schema, s => new ContentDataGraphType()) : null;
return schema != null ? contentDataTypes.GetOrAddNew(schema) : null;
}
public IGraphType GetContentType(Guid schemaId)

2
src/Squidex.Infrastructure.Redis/RedisPubSub.cs

@ -56,7 +56,7 @@ namespace Squidex.Infrastructure
{
var typeName = typeof(T).FullName;
return (RedisSubscription<T>)subscriptions.GetOrAdd(typeName, c => new RedisSubscription<T>(redisSubscriber.Value, c, log));
return (RedisSubscription<T>)subscriptions.GetOrAdd(typeName, this, (k, c) => new RedisSubscription<T>(c.redisSubscriber.Value, k, c.log));
}
}
}

12
src/Squidex.Infrastructure/CollectionExtensions.cs

@ -172,6 +172,18 @@ namespace Squidex.Infrastructure
return result;
}
public static TValue GetOrAdd<TKey, TContext, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TContext context, Func<TKey, TContext, TValue> creator)
{
if (!dictionary.TryGetValue(key, out var result))
{
result = creator(key, context);
dictionary.Add(key, result);
}
return result;
}
public static void Foreach<T>(this IEnumerable<T> collection, Action<T> action)
{
foreach (var item in collection)

2
src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs

@ -242,7 +242,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator
private SwaggerOperations AddOperation(SwaggerOperationMethod method, string entityName, string path, Action<SwaggerOperation> updater)
{
var operations = document.Paths.GetOrAdd(path, k => new SwaggerOperations());
var operations = document.Paths.GetOrAddNew(path);
var operation = new SwaggerOperation();
updater(operation);

2
src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasSwaggerGenerator.cs

@ -78,7 +78,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator
{
name = char.ToUpperInvariant(name[0]) + name.Substring(1);
return new JsonSchema4 { Reference = document.Definitions.GetOrAdd(name, x => schema) };
return new JsonSchema4 { Reference = document.Definitions.GetOrAdd(name, schema, (k, c) => c) };
}
}
}

222
tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/ArrayFieldTests.cs

@ -0,0 +1,222 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Schemas;
using Xunit;
#pragma warning disable SA1310 // Field names must not contain underscore
namespace Squidex.Domain.Apps.Core.Model.Schemas
{
public class ArrayfieldTests
{
private readonly JsonSerializer serializer = TestData.DefaultSerializer();
private readonly ArrayField parent_0 = Fields.Array(100, "root", Partitioning.Invariant);
[Fact]
public void Should_add_field()
{
var field = CreateField(1);
var parent_1 = parent_0.AddField(field);
Assert.Empty(parent_0.Fields);
Assert.Equal(field, parent_1.FieldsById[1]);
}
[Fact]
public void Should_throw_exception_if_adding_field_with_name_that_already_exists()
{
var parent_1 = parent_0.AddField(CreateField(1));
Assert.Throws<ArgumentException>(() => parent_1.AddNumber(2, "my-field-1"));
}
[Fact]
public void Should_throw_exception_if_adding_field_with_id_that_already_exists()
{
var parent_1 = parent_0.AddField(CreateField(1));
Assert.Throws<ArgumentException>(() => parent_1.AddNumber(1, "my-field-2"));
}
[Fact]
public void Should_hide_field()
{
var parent_1 = parent_0.AddField(CreateField(1));
var parent_2 = parent_1.UpdateField(1, f => f.Hide());
var parent_3 = parent_2.UpdateField(1, f => f.Hide());
Assert.False(parent_1.FieldsById[1].IsHidden);
Assert.True(parent_3.FieldsById[1].IsHidden);
}
[Fact]
public void Should_return_same_parent_if_field_to_hide_does_not_exist()
{
var parent_1 = parent_0.UpdateField(1, f => f.Hide()); ;
Assert.Same(parent_0, parent_1);
}
[Fact]
public void Should_show_field()
{
var parent_1 = parent_0.AddField(CreateField(1));
var parent_2 = parent_1.UpdateField(1, f => f.Hide());
var parent_3 = parent_2.UpdateField(1, f => f.Show());
var parent_4 = parent_3.UpdateField(1, f => f.Show());
Assert.True(parent_2.FieldsById[1].IsHidden);
Assert.False(parent_4.FieldsById[1].IsHidden);
}
[Fact]
public void Should_return_same_parent_if_field_to_show_does_not_exist()
{
var parent_1 = parent_0.UpdateField(1, f => f.Show());
Assert.Same(parent_0, parent_1);
}
[Fact]
public void Should_disable_field()
{
var parent_1 = parent_0.AddField(CreateField(1));
var parent_2 = parent_1.UpdateField(1, f => f.Disable());
var parent_3 = parent_2.UpdateField(1, f => f.Disable());
Assert.False(parent_1.FieldsById[1].IsDisabled);
Assert.True(parent_3.FieldsById[1].IsDisabled);
}
[Fact]
public void Should_return_same_parent_if_field_to_disable_does_not_exist()
{
var parent_1 = parent_0.UpdateField(1, f => f.Disable());
Assert.Same(parent_0, parent_1);
}
[Fact]
public void Should_enable_field()
{
var parent_1 = parent_0.AddField(CreateField(1));
var parent_2 = parent_1.UpdateField(1, f => f.Disable());
var parent_3 = parent_2.UpdateField(1, f => f.Enable());
var parent_4 = parent_3.UpdateField(1, f => f.Enable());
Assert.True(parent_2.FieldsById[1].IsDisabled);
Assert.False(parent_4.FieldsById[1].IsDisabled);
}
[Fact]
public void Should_return_same_parent_if_field_to_enable_does_not_exist()
{
var parent_1 = parent_0.UpdateField(1, f => f.Enable());
Assert.Same(parent_0, parent_1);
}
[Fact]
public void Should_update_field()
{
var properties = new NumberFieldProperties();
var parent_1 = parent_0.AddField(CreateField(1));
var parent_2 = parent_1.UpdateField(1, f => f.Update(properties));
Assert.NotSame(properties, parent_1.FieldsById[1].RawProperties);
Assert.Same(properties, parent_2.FieldsById[1].RawProperties);
}
[Fact]
public void Should_throw_exception_if_updating_with_invalid_properties_type()
{
var parent_1 = parent_0.AddField(CreateField(1));
Assert.Throws<ArgumentException>(() => parent_1.UpdateField(1, f => f.Update(new StringFieldProperties())));
}
[Fact]
public void Should_return_same_parent_if_field_to_update_does_not_exist()
{
var parent_1 = parent_0.UpdateField(1, f => f.Update(new StringFieldProperties()));
Assert.Same(parent_0, parent_1);
}
[Fact]
public void Should_delete_field()
{
var parent_1 = parent_0.AddField(CreateField(1));
var parent_2 = parent_1.DeleteField(1);
Assert.Empty(parent_2.FieldsById);
}
[Fact]
public void Should_return_same_parent_if_field_to_delete_does_not_exist()
{
var parent_1 = parent_0.DeleteField(1);
Assert.Same(parent_0, parent_1);
}
[Fact]
public void Should_reorder_fields()
{
var field1 = CreateField(1);
var field2 = CreateField(2);
var field3 = CreateField(3);
var parent_1 = parent_0.AddField(field1);
var parent_2 = parent_1.AddField(field2);
var parent_3 = parent_2.AddField(field3);
var parent_4 = parent_3.ReorderFields(new List<long> { 3, 2, 1 });
Assert.Equal(new List<NestedField> { field3, field2, field1 }, parent_4.Fields.ToList());
}
[Fact]
public void Should_throw_exception_if_not_all_fields_are_covered_for_reordering()
{
var field1 = CreateField(1);
var field2 = CreateField(2);
var parent_1 = parent_0.AddField(field1);
var parent_2 = parent_1.AddField(field2);
Assert.Throws<ArgumentException>(() => parent_2.ReorderFields(new List<long> { 1 }));
}
[Fact]
public void Should_throw_exception_if_field_to_reorder_does_not_exist()
{
var field1 = CreateField(1);
var field2 = CreateField(2);
var parent_1 = parent_0.AddField(field1);
var parent_2 = parent_1.AddField(field2);
Assert.Throws<ArgumentException>(() => parent_2.ReorderFields(new List<long> { 1, 4 }));
}
private static NestedField<NumberFieldProperties> CreateField(int id)
{
return Fields.Number(id, $"my-field-{id}");
}
}
}

75
tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs

@ -21,30 +21,35 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.EN, Language.DE);
private readonly RootField<StringFieldProperties> stringLanguageField = Fields.String(1, "1", Partitioning.Language);
private readonly RootField<StringFieldProperties> stringInvariantField = Fields.String(1, "1", Partitioning.Invariant);
private readonly RootField<JsonFieldProperties> jsonField = Fields.Json(1, "1", Partitioning.Invariant);
private readonly RootField<NumberFieldProperties> numberField = Fields.Number(1, "1", Partitioning.Invariant);
[Fact]
public void Should_encode_json_values()
public void Should_encode_json_value()
{
var source =
new ContentFieldData()
.AddValue("en", null)
.AddValue("de", JToken.FromObject(new { Value = 1 }));
var source = JToken.FromObject(new { Value = 1 });
var result = FieldConverters.EncodeJson()(source, Fields.Json(1, "1", Partitioning.Invariant));
var result = ValueConverters.EncodeJson()(source, jsonField);
Assert.Null(result["en"]);
Assert.True(result["de"].Type == JTokenType.String);
Assert.True(result.Type == JTokenType.String);
}
[Fact]
public void Should_return_same_values_if_encoding_non_json_field()
public void Should_return_same_value_if_encoding_null_value()
{
var source =
new ContentFieldData()
.AddValue("en", null);
var source = JValue.CreateNull();
var result = FieldConverters.EncodeJson()(source, stringLanguageField);
var result = ValueConverters.EncodeJson()(source, jsonField);
Assert.Same(source, result);
}
[Fact]
public void Should_return_same_value_if_encoding_non_json_field()
{
var source = (JToken)"NO-JSON";
var result = ValueConverters.EncodeJson()(source, stringLanguageField);
Assert.Same(source, result);
}
@ -52,15 +57,31 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
[Fact]
public void Should_decode_json_values()
{
var source =
new ContentFieldData()
.AddValue("en", null)
.AddValue("de", "e30=");
var source = "e30=";
var result = ValueConverters.DecodeJson()(source, jsonField);
Assert.True(result is JObject);
}
[Fact]
public void Should_return_same_value_if_decoding_null_value()
{
var source = JValue.CreateNull();
var result = ValueConverters.DecodeJson()(source, jsonField);
Assert.Same(source, result);
}
var result = FieldConverters.DecodeJson()(source, Fields.Json(1, "1", Partitioning.Invariant));
[Fact]
public void Should_return_same_value_if_decoding_non_json_field()
{
var source = JValue.CreateNull();
Assert.Null(result["en"]);
Assert.True(result["de"] is JObject);
var result = ValueConverters.EncodeJson()(source, stringLanguageField);
Assert.Same(source, result);
}
[Fact]
@ -77,7 +98,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
}
[Fact]
public void Should_return_null_values_if_all_value_is_invalid()
public void Should_return_null_values_any_value_is_invalid()
{
var source =
new ContentFieldData()
@ -89,18 +110,6 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
Assert.Null(result);
}
[Fact]
public void Should_return_same_values_if_decoding_non_json_field()
{
var source =
new ContentFieldData()
.AddValue("en", null);
var result = FieldConverters.DecodeJson()(source, stringLanguageField);
Assert.Same(source, result);
}
[Fact]
public void Should_return_same_values_if_field_not_hidden()
{

10
tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs

@ -69,7 +69,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
new ContentFieldData()
.AddValue("iv", new JArray(id1.ToString(), id2.ToString())));
var actual = input.Convert(schema, FieldReferencesConverter.CleanReferences(new[] { id2 }));
var converter = FieldConverters.ForValues(FieldReferencesConverter.CleanReferences(new[] { id2 }));
var actual = input.Convert(schema, converter);
var cleanedValue = (JArray)actual[5]["iv"];
@ -91,7 +93,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
}
[Fact]
public void Should_empty_list_from_assets_field_for_referenced_ids_when_null()
public void Should_return_empty_list_from_assets_field_for_referenced_ids_when_null()
{
var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant);
@ -101,7 +103,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
}
[Fact]
public void Should_empty_list_from_assets_field_for_referenced_ids_when_other_type()
public void Should_return_empty_list_from_assets_field_for_referenced_ids_when_other_type()
{
var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant);
@ -111,7 +113,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
}
[Fact]
public void Should_empty_list_from_non_references_field()
public void Should_return_empty_list_from_non_references_field()
{
var sut = Fields.String(1, "my-string", Partitioning.Invariant);

Loading…
Cancel
Save