Browse Source

Item converters. (#892)

pull/895/head
Sebastian Stehle 4 years ago
committed by GitHub
parent
commit
804b6b1869
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 51
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/AddSchemaNames.cs
  2. 262
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs
  3. 66
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeChangedTypes.cs
  4. 32
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeHidden.cs
  5. 403
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs
  6. 39
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/IConverter.cs
  7. 78
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveAssetUrls.cs
  8. 58
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveInvariant.cs
  9. 104
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveLanguages.cs
  10. 111
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs
  11. 21
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs
  12. 59
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs
  13. 12
      backend/tests/RunCoverage.ps1
  14. 129
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs
  15. 458
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs
  16. 188
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs
  17. 15
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs

51
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/AddSchemaNames.cs

@ -0,0 +1,51 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public sealed class AddSchemaNames : IContentItemConverter
{
private readonly ResolvedComponents components;
public AddSchemaNames(ResolvedComponents components)
{
this.components = components;
}
public JsonObject ConvertItem(IField field, JsonObject source)
{
if (field is IArrayField)
{
return source;
}
if (source.ContainsKey("schemaName"))
{
return source;
}
if (!Component.IsValid(source, out var discriminator))
{
return source;
}
var id = DomainId.Create(discriminator);
if (components.TryGetValue(id, out var schema))
{
source["schemaName"] = schema.Name;
}
return source;
}
}
}

262
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs

@ -8,22 +8,60 @@
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
#pragma warning disable RECS0033 // Convert 'if' to '||' expression
namespace Squidex.Domain.Apps.Core.ConvertContent namespace Squidex.Domain.Apps.Core.ConvertContent
{ {
public static class ContentConverter public sealed class ContentConverter
{ {
public static ContentData Convert(this ContentData content, Schema schema, params FieldConverter[] converters) private readonly List<IContentItemConverter> itemConverters = new List<IContentItemConverter>();
private readonly List<IContentFieldAfterConverter> fieldAfterConverters = new List<IContentFieldAfterConverter>();
private readonly List<IContentFieldConverter> fieldConverters = new List<IContentFieldConverter>();
private readonly List<IContentValueConverter> valueConverters = new List<IContentValueConverter>();
private readonly ResolvedComponents components;
private readonly Schema schema;
public ContentConverter(ResolvedComponents components, Schema schema)
{ {
Guard.NotNull(schema); this.components = components;
this.schema = schema;
}
var result = new ContentData(content.Count); public ContentConverter Add(IConverter converter)
{
if (converter is IContentItemConverter itemConverter)
{
itemConverters.Add(itemConverter);
}
if (converters == null || converters.Length == 0) if (converter is IContentFieldConverter fieldConverter)
{ {
return result; fieldConverters.Add(fieldConverter);
}
if (converter is IContentFieldAfterConverter fieldAfterConverter)
{
fieldAfterConverters.Add(fieldAfterConverter);
}
if (converter is IContentValueConverter valueConverter)
{
valueConverters.Add(valueConverter);
}
return this;
} }
public ContentData Convert(ContentData content)
{
Guard.NotNull(schema);
// The conversion process assumes that we have ownership of the data and can manipulate it.
// Clones are only created to save allocations.
var result = new ContentData(content.Count);
foreach (var (fieldName, fieldData) in content) foreach (var (fieldName, fieldData) in content)
{ {
if (fieldData == null || !schema.FieldsByName.TryGetValue(fieldName, out var field)) if (fieldData == null || !schema.FieldsByName.TryGetValue(fieldName, out var field))
@ -31,13 +69,19 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
continue; continue;
} }
ContentFieldData? newData = fieldData; // Some conversions are faster to do upfront, e.g. to remove hidden fields.
var newData = ConvertField(field, fieldData);
if (newData != null) if (newData == null)
{ {
newData = ConvertData(field, newData, converters); continue;
} }
newData = ConvertValues(field, newData);
// Some conversions are faster to do later, e.g. fallback handling for languages.
newData = ConvertFieldAfter(field, newData);
if (newData != null) if (newData != null)
{ {
result.Add(field.Name, newData); result.Add(field.Name, newData);
@ -47,16 +91,26 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
return result; return result;
} }
private static ContentFieldData? ConvertData(IRootField field, ContentFieldData data, FieldConverter[] converters) private ContentFieldData? ConvertField(IRootField field, ContentFieldData? data)
{
foreach (var converter in fieldConverters)
{ {
if (converters == null || converters.Length == 0) data = converter.ConvertField(field, data!);
if (data == null)
{ {
break;
}
}
return data; return data;
} }
foreach (var converter in converters) private ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData? data)
{ {
data = converter(data!, field)!; foreach (var converter in fieldAfterConverters)
{
data = converter.ConvertFieldAfter(field, data!);
if (data == null) if (data == null)
{ {
@ -66,5 +120,187 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
return data; return data;
} }
private (bool Remove, JsonValue) ConvertByType<T>(T field, JsonValue value, IField? parent) where T : IField
{
switch (field)
{
case IArrayField arrayField:
return ConvertArray(arrayField, value);
case IField<ComponentFieldProperties>:
return ConvertComponent(value, field);
case IField<ComponentsFieldProperties>:
return ConvertComponents(value, field);
default:
return ConvertValue(field, value, parent);
}
}
private (bool Remove, JsonValue) ConvertArray(IArrayField field, JsonValue value)
{
if (value.Value is not JsonArray array)
{
return (true, default);
}
for (int i = 0; i < array.Count; i++)
{
var oldValue = array[i];
var (removed, newValue) = ConvertArrayItem(field, oldValue);
if (removed)
{
array.RemoveAt(i);
i--;
}
else if (!ReferenceEquals(newValue.Value, oldValue.Value))
{
array[i] = newValue;
}
}
return (false, array);
}
private (bool Remove, JsonValue) ConvertComponents(JsonValue value, IField parent)
{
if (value.Value is not JsonArray array)
{
return (true, default);
}
for (int i = 0; i < array.Count; i++)
{
var oldValue = array[i];
var (removed, newValue) = ConvertComponent(oldValue, parent);
if (removed)
{
array.RemoveAt(i);
i--;
}
else if (!ReferenceEquals(newValue.Value, oldValue.Value))
{
// Faster to check for reference equality than for deep equals.
array[i] = newValue;
}
}
return (false, array);
}
private (bool Remove, JsonValue) ConvertComponent(JsonValue value, IField parent)
{
if (value.Value is not JsonObject obj || !obj.TryGetValue(Component.Discriminator, out var discriminator))
{
return (true, default);
}
if (!components.TryGetValue(DomainId.Create(discriminator.ToString()), out var component))
{
return (true, default);
}
return (false, ConvertNested(component.FieldCollection, obj, parent));
}
private (bool Remove, JsonValue) ConvertArrayItem(IArrayField field, JsonValue value)
{
if (value.Value is not JsonObject obj)
{
return (true, default);
}
return (false, ConvertNested(field.FieldCollection, obj, field));
}
private ContentFieldData ConvertValues(IField field, ContentFieldData source)
{
ContentFieldData? result = null;
foreach (var (key, oldValue) in source)
{
var (removed, newData) = ConvertByType(field, oldValue, null);
// Create a copy to avoid allocations if nothing has been changed.
if (removed)
{
result ??= new ContentFieldData(source);
result.Remove(key);
}
else if (!ReferenceEquals(newData.Value, oldValue.Value))
{
// Faster to check for reference equality than for deep equals.
result ??= new ContentFieldData(source);
result[key] = newData;
}
}
return result ?? source;
}
private JsonValue ConvertNested<T>(FieldCollection<T> fields, JsonObject source, IField parent) where T : IField
{
JsonObject? result = null;
foreach (var (key, oldValue) in source)
{
var newValue = oldValue;
var remove = false;
if (fields.ByName.TryGetValue(key, out var field))
{
(remove, newValue) = ConvertByType(field, oldValue, parent);
}
else if (key != Component.Discriminator)
{
remove = true;
}
// Create a copy to avoid allocations if nothing has been changed.
if (remove)
{
result ??= new JsonObject(source);
result.Remove(key);
}
else if (!ReferenceEquals(newValue.Value, oldValue.Value))
{
// Faster to check for reference equality than for deep equals.
result ??= new JsonObject(source);
result[key] = newValue;
}
}
result ??= source;
foreach (var converter in itemConverters)
{
result = converter.ConvertItem(parent, result);
}
return result ?? source;
}
private (bool Remove, JsonValue) ConvertValue(IField field, JsonValue value, IField? parent)
{
foreach (var converter in valueConverters)
{
// Use a tuple and not a nullable result to avoid boxing and allocations.
(var remove, value) = converter.ConvertValue(field, value, parent);
if (remove)
{
return (true, default);
}
}
return (false, value);
}
} }
} }

66
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeChangedTypes.cs

@ -0,0 +1,66 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public sealed class ExcludeChangedTypes : IContentFieldConverter, IContentValueConverter
{
private readonly IJsonSerializer jsonSerializer;
public ExcludeChangedTypes(IJsonSerializer jsonSerializer)
{
this.jsonSerializer = jsonSerializer;
}
public ContentFieldData? ConvertField(IRootField field, ContentFieldData source)
{
foreach (var (_, value) in source)
{
if (value.Value == default)
{
continue;
}
if (IsChangedType(field, value))
{
return null;
}
}
return source;
}
public (bool Remove, JsonValue) ConvertValue(IField field, JsonValue source, IField? parent)
{
if (parent == null || source == default)
{
return (false, source);
}
return (IsChangedType(field, source), source);
}
private bool IsChangedType(IField field, JsonValue source)
{
try
{
return !JsonValueValidator.IsValid(field, source, jsonSerializer);
}
catch
{
return true;
}
}
}
}

32
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ExcludeHidden.cs

@ -0,0 +1,32 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public sealed class ExcludeHidden : IContentFieldConverter, IContentValueConverter
{
public static readonly ExcludeHidden Instance = new ExcludeHidden();
private ExcludeHidden()
{
}
public ContentFieldData? ConvertField(IRootField field, ContentFieldData source)
{
return field.IsForApi() ? source : null;
}
public (bool Remove, JsonValue) ConvertValue(IField field, JsonValue source, IField? parent)
{
return field.IsForApi() ? (false, source) : (true, default);
}
}
}

403
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs

@ -1,403 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
#pragma warning disable MA0048 // File name must match type name
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public delegate ContentFieldData? FieldConverter(ContentFieldData data, IRootField field);
public static class FieldConverters
{
public static readonly FieldConverter Noop = (data, _) => data;
public static readonly FieldConverter ExcludeHidden = (data, field) =>
{
return field.IsForApi() ? data : null;
};
public static FieldConverter ExcludeChangedTypes(IJsonSerializer jsonSerializer)
{
return (data, field) =>
{
foreach (var (_, value) in data)
{
if (value.Value == default)
{
continue;
}
try
{
if (!JsonValueValidator.IsValid(field, value, jsonSerializer))
{
return null;
}
}
catch
{
return null;
}
}
return data;
};
}
public static FieldConverter ResolveInvariant(LanguagesConfig languages)
{
var iv = InvariantPartitioning.Key;
return (data, field) =>
{
if (field.Partitioning.Equals(Partitioning.Invariant) && !data.TryGetNonNull(iv, out _))
{
var result = new ContentFieldData(1);
if (data.TryGetNonNull(languages.Master, out var value))
{
result[iv] = value;
}
else if (data.Count > 0)
{
result[iv] = data.First().Value;
}
return result;
}
return data;
};
}
public static FieldConverter ResolveLanguages(LanguagesConfig languages)
{
var iv = InvariantPartitioning.Key;
return (data, field) =>
{
if (field.Partitioning.Equals(Partitioning.Language))
{
if (data.TryGetNonNull(iv, out var value))
{
var result = new ContentFieldData
{
[languages.Master] = value
};
return result;
}
while (true)
{
var isRemoved = false;
foreach (var (key, _) in data)
{
if (!languages.AllKeys.Contains(key))
{
data.Remove(key);
isRemoved = true;
break;
}
}
if (!isRemoved)
{
break;
}
}
}
return data;
};
}
public static FieldConverter ResolveFallbackLanguages(LanguagesConfig languages)
{
return (data, field) =>
{
if (field.Partitioning.Equals(Partitioning.Language))
{
foreach (var languageCode in languages.AllKeys)
{
if (data.TryGetNonNull(languageCode, out _))
{
continue;
}
foreach (var fallback in languages.GetPriorities(languageCode))
{
if (data.TryGetNonNull(fallback, out var value))
{
data[languageCode] = value;
break;
}
}
}
}
return data;
};
}
public static FieldConverter FilterLanguages(LanguagesConfig config, IEnumerable<Language>? languages)
{
if (languages?.Any() != true)
{
return Noop;
}
var languageSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var language in languages)
{
if (config.Contains(language.Iso2Code))
{
languageSet.Add(language.Iso2Code);
}
}
if (languageSet.Count == 0)
{
languageSet.Add(config.Master);
}
return (data, field) =>
{
if (field.Partitioning.Equals(Partitioning.Language))
{
while (true)
{
var isRemoved = false;
foreach (var (key, _) in data)
{
if (!languageSet.Contains(key))
{
data.Remove(key);
isRemoved = true;
break;
}
}
if (!isRemoved)
{
break;
}
}
}
return data;
};
}
public static FieldConverter ForValues(ResolvedComponents components, params ValueConverter[] converters)
{
return (data, field) =>
{
ContentFieldData? newData = null;
foreach (var (key, value) in data)
{
var newValue = ConvertByType(field, value, null, converters, components);
if (newValue == null)
{
newData ??= new ContentFieldData(data);
newData.Remove(key);
}
else if (!ReferenceEquals(newValue.Value.Value, value.Value))
{
newData ??= new ContentFieldData(data);
newData[key] = newValue.Value;
}
}
return newData ?? data;
};
}
private static JsonValue? ConvertByType<T>(T field, JsonValue value, IArrayField? parent, ValueConverter[] converters,
ResolvedComponents components) where T : IField
{
switch (field)
{
case IArrayField arrayField:
return ConvertArray(arrayField, value, converters, components);
case IField<ComponentFieldProperties>:
return ConvertComponent(value, converters, components);
case IField<ComponentsFieldProperties>:
return ConvertComponents(value, converters, components);
default:
return ConvertValue(field, value, parent, converters);
}
}
private static JsonValue? ConvertArray(IArrayField field, JsonValue value, ValueConverter[] converters,
ResolvedComponents components)
{
if (value.Value is JsonArray a)
{
JsonArray? result = null;
for (int i = 0, j = 0; i < a.Count; i++, j++)
{
var oldValue = a[i];
var newValue = ConvertArrayItem(field, oldValue, converters, components);
if (newValue == null)
{
result ??= new JsonArray(a);
result.RemoveAt(j);
j--;
}
else if (!ReferenceEquals(newValue.Value.Value, oldValue.Value))
{
result ??= new JsonArray(a);
result[j] = newValue.Value;
}
}
return result ?? value;
}
return null;
}
private static JsonValue? ConvertComponents(JsonValue? value, ValueConverter[] converters,
ResolvedComponents components)
{
if (value?.Value is JsonArray a)
{
JsonArray? result = null;
for (int i = 0, j = 0; i < a.Count; i++, j++)
{
var oldValue = a[i];
var newValue = ConvertComponent(oldValue, converters, components);
if (newValue == null)
{
result ??= new JsonArray(a);
result.RemoveAt(j);
j--;
}
else if (!ReferenceEquals(newValue.Value.Value, a[i].Value))
{
result ??= new JsonArray(a);
result[j] = newValue.Value;
}
}
return result ?? value;
}
return null;
}
private static JsonValue? ConvertComponent(JsonValue? value, ValueConverter[] converters,
ResolvedComponents components)
{
if (value?.Value is JsonObject o && o.TryGetValue(Component.Discriminator, out var found) && found.Value is string s)
{
var id = DomainId.Create(s);
if (components.TryGetValue(id, out var schema))
{
return ConvertNested(schema.FieldCollection, value.Value, null, converters, components);
}
else
{
return value;
}
}
return null;
}
private static JsonValue? ConvertArrayItem(IArrayField field, JsonValue value, ValueConverter[] converters,
ResolvedComponents components)
{
if (value.Value is JsonObject)
{
return ConvertNested(field.FieldCollection, value, field, converters, components);
}
return null;
}
private static JsonValue ConvertNested<T>(FieldCollection<T> fields, JsonValue source, IArrayField? parent, ValueConverter[] converters,
ResolvedComponents components) where T : IField
{
JsonObject? result = null;
var obj = source.AsObject;
foreach (var (key, value) in obj)
{
JsonValue? newValue = value;
if (fields.ByName.TryGetValue(key, out var field))
{
newValue = ConvertByType(field, value, parent, converters, components);
}
else if (key != Component.Discriminator)
{
newValue = null;
}
if (newValue == null)
{
result ??= new JsonObject(obj);
result.Remove(key);
}
else if (!ReferenceEquals(newValue.Value.Value, value.Value))
{
result ??= new JsonObject(obj);
result[key] = newValue.Value;
}
}
return result ?? source;
}
private static JsonValue? ConvertValue(IField field, JsonValue value, IArrayField? parent, ValueConverter[] converters)
{
var newValue = value;
for (var i = 0; i < converters.Length; i++)
{
var candidate = converters[i](newValue!, field, parent);
if (candidate == null)
{
return null;
}
else
{
newValue = candidate.Value;
}
}
return newValue;
}
}
}

39
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/IConverter.cs

@ -0,0 +1,39 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json.Objects;
#pragma warning disable MA0048 // File name must match type name
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public interface IConverter
{
}
public interface IContentFieldAfterConverter : IConverter
{
ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source);
}
public interface IContentFieldConverter : IConverter
{
ContentFieldData? ConvertField(IRootField field, ContentFieldData source);
}
public interface IContentItemConverter : IConverter
{
JsonObject ConvertItem(IField field, JsonObject source);
}
public interface IContentValueConverter : IConverter
{
(bool Remove, JsonValue) ConvertValue(IField field, JsonValue source, IField? parent);
}
}

78
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveAssetUrls.cs

@ -0,0 +1,78 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public sealed class ResolveAssetUrls : IContentValueConverter
{
private readonly NamedId<DomainId> appId;
private readonly IUrlGenerator urlGenerator;
private readonly Func<IField, IField?, bool> shouldHandle;
public ResolveAssetUrls(NamedId<DomainId> appId, IUrlGenerator urlGenerator, IReadOnlyCollection<string>? fields)
{
this.appId = appId;
if (fields == null || fields.Count == 0)
{
shouldHandle = (field, parent) => false;
}
else if (fields.Contains("*"))
{
shouldHandle = (field, parent) => true;
}
else
{
var paths = fields.Select(x => x.Split('.')).ToList();
shouldHandle = (field, parent) =>
{
for (var i = 0; i < paths.Count; i++)
{
var path = paths[i];
if (parent != null)
{
if (path.Length == 2 && path[0] == parent.Name && path[1] == field.Name)
{
return true;
}
}
else
{
if (path.Length == 1 && path[0] == field.Name)
{
return true;
}
}
}
return false;
};
}
this.urlGenerator = urlGenerator;
}
public (bool Remove, JsonValue) ConvertValue(IField field, JsonValue source, IField? parent)
{
if (field is IField<AssetsFieldProperties> && source.Value is JsonArray a && shouldHandle(field, parent))
{
for (var i = 0; i < a.Count; i++)
{
a[i] = urlGenerator.AssetContent(appId, a[i].ToString());
}
}
return (false, source);
}
}
}

58
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveInvariant.cs

@ -0,0 +1,58 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public sealed class ResolveInvariant : IContentFieldAfterConverter
{
private readonly LanguagesConfig languages;
public ResolveInvariant(LanguagesConfig languages)
{
this.languages = languages;
}
public ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source)
{
if (!field.Partitioning.Equals(Partitioning.Invariant))
{
return source;
}
if (source.TryGetNonNull(InvariantPartitioning.Key, out _))
{
return source;
}
if (source.TryGetNonNull(languages.Master, out var value))
{
source.Clear();
source[InvariantPartitioning.Key] = value;
return source;
}
if (source.Count > 0)
{
var first = source.First().Value;
source.Clear();
source[InvariantPartitioning.Key] = first;
return source;
}
source.Clear();
return source;
}
}
}

104
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveLanguages.cs

@ -0,0 +1,104 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public sealed class ResolveLanguages : IContentFieldAfterConverter
{
private readonly LanguagesConfig languages;
private readonly bool resolveFallback;
private readonly HashSet<string> languageCodes;
public ResolveLanguages(LanguagesConfig languages, bool resolveFallback = true, params Language[] filteredLanguages)
{
this.languages = languages;
if (filteredLanguages?.Length > 0)
{
languageCodes = languages.AllKeys.Intersect(filteredLanguages.Select(x => x.Iso2Code)).ToHashSet();
}
else
{
languageCodes = languages.AllKeys.ToHashSet();
}
if (languageCodes.Count == 0)
{
languageCodes.Add(languages.Master);
}
this.resolveFallback = resolveFallback;
}
public ContentFieldData? ConvertFieldAfter(IRootField field, ContentFieldData source)
{
if (!field.Partitioning.Equals(Partitioning.Language))
{
return source;
}
if (source.TryGetNonNull(InvariantPartitioning.Key, out var value))
{
var result = new ContentFieldData
{
[languages.Master] = value
};
return result;
}
while (true)
{
var isRemoved = false;
foreach (var (key, _) in source)
{
if (!languageCodes.Contains(key))
{
source.Remove(key);
isRemoved = true;
break;
}
}
if (!isRemoved)
{
break;
}
}
if (!resolveFallback)
{
return source;
}
foreach (var languageCode in languageCodes)
{
if (source.TryGetNonNull(languageCode, out _))
{
continue;
}
foreach (var fallback in languages.GetPriorities(languageCode))
{
if (source.TryGetNonNull(fallback, out var fallbackValue))
{
source[languageCode] = fallbackValue;
break;
}
}
}
return source;
}
}
}

111
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs

@ -1,111 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
#pragma warning disable MA0048 // File name must match type name
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public delegate JsonValue? ValueConverter(JsonValue value, IField field, IArrayField? parent);
public static class ValueConverters
{
public static readonly ValueConverter Noop = (value, field, parent) => value;
public static readonly ValueConverter ExcludeHidden = (value, field, parent) =>
{
return field.IsForApi() ? (JsonValue?)value : null;
};
public static ValueConverter ExcludeChangedTypes(IJsonSerializer jsonSerializer)
{
return (value, field, parent) =>
{
if (value == default)
{
return value;
}
try
{
if (!JsonValueValidator.IsValid(field, value, jsonSerializer))
{
return null;
}
}
catch
{
return null;
}
return value;
};
}
public static ValueConverter ResolveAssetUrls(NamedId<DomainId> appId, IReadOnlyCollection<string>? fields, IUrlGenerator urlGenerator)
{
if (fields?.Any() != true)
{
return Noop;
}
Func<IField, IField?, bool> shouldHandle;
if (fields.Contains("*"))
{
shouldHandle = (field, parent) => true;
}
else
{
var paths = fields.Select(x => x.Split('.')).ToList();
shouldHandle = (field, parent) =>
{
for (var i = 0; i < paths.Count; i++)
{
var path = paths[i];
if (parent != null)
{
if (path.Length == 2 && path[0] == parent.Name && path[1] == field.Name)
{
return true;
}
}
else
{
if (path.Length == 1 && path[0] == field.Name)
{
return true;
}
}
}
return false;
};
}
return (value, field, parent) =>
{
if (field is IField<AssetsFieldProperties> && value.Value is JsonArray a && shouldHandle(field, parent))
{
for (var i = 0; i < a.Count; i++)
{
a[i] = urlGenerator.AssetContent(appId, a[i].ToString());
}
}
return value;
};
}
}
}

21
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs

@ -6,28 +6,29 @@
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{ {
public static class ValueReferencesConverter public sealed class ValueReferencesConverter : IContentValueConverter
{ {
public static ValueConverter CleanReferences(HashSet<DomainId>? validIds = null) private readonly HashSet<DomainId>? validIds;
{
if (validIds == null) public ValueReferencesConverter(HashSet<DomainId>? validIds = null)
{ {
return ValueConverters.Noop; this.validIds = validIds;
} }
return (value, field, parent) => public (bool Remove, JsonValue) ConvertValue(IField field, JsonValue source, IField? parent)
{ {
if (value == default) if (validIds == null || source == default)
{ {
return value; return (false, source);
} }
return ReferencesCleaner.Cleanup(field, value, validIds); return (false, ReferencesCleaner.Cleanup(field, source, validIds));
};
} }
} }
} }

59
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs

@ -16,6 +16,8 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Tasks; using Squidex.Infrastructure.Tasks;
#pragma warning disable MA0073 // Avoid comparison with bool constant
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
{ {
public sealed class ConvertData : IContentEnricherStep public sealed class ConvertData : IContentEnricherStep
@ -24,8 +26,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
private readonly IJsonSerializer jsonSerializer; private readonly IJsonSerializer jsonSerializer;
private readonly IAssetRepository assetRepository; private readonly IAssetRepository assetRepository;
private readonly IContentRepository contentRepository; private readonly IContentRepository contentRepository;
private readonly FieldConverter excludedChangedField; private readonly ExcludeChangedTypes excludeChangedTypes;
private readonly FieldConverter excludedHiddenField;
public ConvertData(IUrlGenerator urlGenerator, IJsonSerializer jsonSerializer, public ConvertData(IUrlGenerator urlGenerator, IJsonSerializer jsonSerializer,
IAssetRepository assetRepository, IContentRepository contentRepository) IAssetRepository assetRepository, IContentRepository contentRepository)
@ -35,8 +36,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
this.assetRepository = assetRepository; this.assetRepository = assetRepository;
this.contentRepository = contentRepository; this.contentRepository = contentRepository;
excludedChangedField = FieldConverters.ExcludeChangedTypes(jsonSerializer); excludeChangedTypes = new ExcludeChangedTypes(jsonSerializer);
excludedHiddenField = FieldConverters.ExcludeHidden;
} }
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas, public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas,
@ -50,16 +50,16 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
var (schema, components) = await schemas(group.Key); var (schema, components) = await schemas(group.Key);
var converters = GenerateConverters(context, components, referenceCleaner).ToArray(); var converter = GenerateConverter(context, components, schema.SchemaDef, referenceCleaner);
foreach (var content in group) foreach (var content in group)
{ {
content.Data = content.Data.Convert(schema.SchemaDef, converters); content.Data = converter.Convert(content.Data);
} }
} }
} }
private async Task<ValueConverter?> CleanReferencesAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas, private async Task<ValueReferencesConverter?> CleanReferencesAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas,
CancellationToken ct) CancellationToken ct)
{ {
if (context.ShouldSkipCleanup()) if (context.ShouldSkipCleanup())
@ -89,7 +89,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
var foundIds = assets.Union(refContents).ToHashSet(); var foundIds = assets.Union(refContents).ToHashSet();
return ValueReferencesConverter.CleanReferences(foundIds); return new ValueReferencesConverter(foundIds);
} }
} }
@ -112,50 +112,43 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
return result; return result;
} }
private IEnumerable<FieldConverter> GenerateConverters(Context context, ResolvedComponents components, ValueConverter? cleanReferences) private ContentConverter GenerateConverter(Context context, ResolvedComponents components, Schema schema, ValueReferencesConverter? cleanReferences)
{ {
var converter = new ContentConverter(components, schema);
if (!context.IsFrontendClient) if (!context.IsFrontendClient)
{ {
yield return excludedHiddenField; converter.Add(ExcludeHidden.Instance);
yield return FieldConverters.ForValues(components, ValueConverters.ExcludeHidden);
} }
yield return excludedChangedField; converter.Add(excludeChangedTypes);
yield return FieldConverters.ForValues(components, ValueConverters.ExcludeChangedTypes(jsonSerializer));
if (cleanReferences != null) if (cleanReferences != null)
{ {
yield return FieldConverters.ForValues(components, cleanReferences); converter.Add(cleanReferences);
} }
yield return FieldConverters.ResolveInvariant(context.App.Languages); converter.Add(new ResolveInvariant(context.App.Languages));
yield return FieldConverters.ResolveLanguages(context.App.Languages);
if (!context.IsFrontendClient)
{
if (context.ShouldResolveLanguages())
{
yield return FieldConverters.ResolveFallbackLanguages(context.App.Languages);
}
var languages = context.Languages(); converter.Add(
new ResolveLanguages(context.App.Languages,
context.IsFrontendClient == false &&
context.ShouldResolveLanguages(),
context.Languages().ToArray()));
if (languages.Any()) if (!context.IsFrontendClient)
{ {
yield return FieldConverters.FilterLanguages(context.App.Languages, languages);
}
var assetUrls = context.AssetUrls().ToList(); var assetUrls = context.AssetUrls().ToList();
if (assetUrls.Count > 0) if (assetUrls.Count > 0)
{ {
var appId = context.App.NamedId(); converter.Add(new ResolveAssetUrls(context.App.NamedId(), urlGenerator, assetUrls));
var resolveAssetUrls = ValueConverters.ResolveAssetUrls(appId, assetUrls, urlGenerator);
yield return FieldConverters.ForValues(components, resolveAssetUrls);
} }
converter.Add(new AddSchemaNames(components));
} }
return converter;
} }
} }
} }

12
backend/tests/RunCoverage.ps1

@ -22,7 +22,7 @@ Write-Host "Recreated '$folderReports' folder"
New-Item -ItemType directory -Path $folderReports New-Item -ItemType directory -Path $folderReports
if ($all -Or $infrastructure) { if ($all -Or $infrastructure) {
&"$folderHome\.nuget\packages\OpenCover\4.7.922\tools\OpenCover.Console.exe" ` &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" `
-register:user ` -register:user `
-target:"C:\Program Files\dotnet\dotnet.exe" ` -target:"C:\Program Files\dotnet\dotnet.exe" `
-targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Infrastructure.Tests\Squidex.Infrastructure.Tests.csproj" ` -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Infrastructure.Tests\Squidex.Infrastructure.Tests.csproj" `
@ -34,7 +34,7 @@ if ($all -Or $infrastructure) {
} }
if ($all -Or $appsCore) { if ($all -Or $appsCore) {
&"$folderHome\.nuget\packages\OpenCover\4.7.922\tools\OpenCover.Console.exe" ` &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" `
-register:user ` -register:user `
-target:"C:\Program Files\dotnet\dotnet.exe" ` -target:"C:\Program Files\dotnet\dotnet.exe" `
-targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Domain.Apps.Core.Tests\Squidex.Domain.Apps.Core.Tests.csproj" ` -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Domain.Apps.Core.Tests\Squidex.Domain.Apps.Core.Tests.csproj" `
@ -46,7 +46,7 @@ if ($all -Or $appsCore) {
} }
if ($all -Or $appsEntities) { if ($all -Or $appsEntities) {
&"$folderHome\.nuget\packages\OpenCover\4.7.922\tools\OpenCover.Console.exe" ` &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" `
-register:user ` -register:user `
-target:"C:\Program Files\dotnet\dotnet.exe" ` -target:"C:\Program Files\dotnet\dotnet.exe" `
-targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Domain.Apps.Entities.Tests\Squidex.Domain.Apps.Entities.Tests.csproj" ` -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Domain.Apps.Entities.Tests\Squidex.Domain.Apps.Entities.Tests.csproj" `
@ -58,7 +58,7 @@ if ($all -Or $appsEntities) {
} }
if ($all -Or $users) { if ($all -Or $users) {
&"$folderHome\.nuget\packages\OpenCover\4.7.922\tools\OpenCover.Console.exe" ` &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" `
-register:user ` -register:user `
-target:"C:\Program Files\dotnet\dotnet.exe" ` -target:"C:\Program Files\dotnet\dotnet.exe" `
-targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Domain.Users.Tests\Squidex.Domain.Users.Tests.csproj" ` -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Domain.Users.Tests\Squidex.Domain.Users.Tests.csproj" `
@ -70,7 +70,7 @@ if ($all -Or $users) {
} }
if ($all -Or $web) { if ($all -Or $web) {
&"$folderHome\.nuget\packages\OpenCover\4.7.922\tools\OpenCover.Console.exe" ` &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" `
-register:user ` -register:user `
-target:"C:\Program Files\dotnet\dotnet.exe" ` -target:"C:\Program Files\dotnet\dotnet.exe" `
-targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Web.Tests\Squidex.Web.Tests.csproj" ` -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Web.Tests\Squidex.Web.Tests.csproj" `
@ -81,6 +81,6 @@ if ($all -Or $web) {
-oldStyle -oldStyle
} }
&"$folderHome\.nuget\packages\ReportGenerator\4.8.7\tools\net47\ReportGenerator.exe" ` &"$folderHome\.nuget\packages\ReportGenerator\5.1.9\tools\net47\ReportGenerator.exe" `
-reports:"$folderWorking\$folderReports\*.xml" ` -reports:"$folderWorking\$folderReports\*.xml" `
-targetdir:"$folderWorking\$folderReports\Output" -targetdir:"$folderWorking\$folderReports\Output"

129
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs

@ -82,6 +82,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
.Add("nested", JsonValue.Array(1, 2)))) .Add("nested", JsonValue.Array(1, 2))))
.Add(Component.Discriminator, DomainId.Empty)))); .Add(Component.Discriminator, DomainId.Empty))));
var result =
new ContentConverter(components, schema)
.Add(new ValueConverter())
.Convert(source);
var expected = var expected =
new ContentData() new ContentData()
.AddField("references", .AddField("references",
@ -116,13 +121,127 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
new JsonObject())) new JsonObject()))
.Add(Component.Discriminator, DomainId.Empty)))); .Add(Component.Discriminator, DomainId.Empty))));
var converter = new ValueConverter((data, field, parent) => field.Name != "assets1" ? (JsonValue?)null : data); Assert.Equal(expected, result);
}
[Fact]
public void Should_apply_item_conversion_on_all_levels()
{
var source =
new ContentData()
.AddField("references",
new ContentFieldData()
.AddInvariant(JsonValue.Array(1, 2)))
.AddField("assets1",
new ContentFieldData()
.AddInvariant(JsonValue.Array(1)))
.AddField("array",
new ContentFieldData()
.AddInvariant(
JsonValue.Array(
new JsonObject()
.Add("nested", JsonValue.Array(1, 2)))))
.AddField("component",
new ContentFieldData()
.AddInvariant(
new JsonObject()
.Add("references",
JsonValue.Array(1, 2))
.Add("assets1",
JsonValue.Array(1))
.Add("array",
JsonValue.Array(
new JsonObject()
.Add("nested", JsonValue.Array(1, 2))))
.Add(Component.Discriminator, DomainId.Empty)))
.AddField("components",
new ContentFieldData()
.AddInvariant(
JsonValue.Array(
new JsonObject()
.Add("references",
JsonValue.Array(1, 2))
.Add("assets1",
JsonValue.Array(1))
.Add("array",
JsonValue.Array(
new JsonObject()
.Add("nested", JsonValue.Array(1, 2))))
.Add(Component.Discriminator, DomainId.Empty))));
var result =
new ContentConverter(components, schema)
.Add(new ItemConverter())
.Convert(source);
var expected =
new ContentData()
.AddField("references",
new ContentFieldData()
.AddInvariant(JsonValue.Array(1, 2)))
.AddField("assets1",
new ContentFieldData()
.AddInvariant(JsonValue.Array(1)))
.AddField("array",
new ContentFieldData()
.AddInvariant(
JsonValue.Array(
new JsonObject()
.Add("extraField", 42)
.Add("nested", JsonValue.Array(1, 2)))))
.AddField("component",
new ContentFieldData()
.AddInvariant(
new JsonObject()
.Add("extraField", 42)
.Add("references",
JsonValue.Array(1, 2))
.Add("assets1",
JsonValue.Array(1))
.Add("array",
JsonValue.Array(
new JsonObject()
.Add("extraField", 42)
.Add("nested", JsonValue.Array(1, 2))))
.Add(Component.Discriminator, DomainId.Empty)))
.AddField("components",
new ContentFieldData()
.AddInvariant(
JsonValue.Array(
new JsonObject()
.Add("extraField", 42)
.Add("references",
JsonValue.Array(1, 2))
.Add("assets1",
JsonValue.Array(1))
.Add("array",
JsonValue.Array(
new JsonObject()
.Add("extraField", 42)
.Add("nested", JsonValue.Array(1, 2))))
.Add(Component.Discriminator, DomainId.Empty))));
Assert.Equal(expected, result);
}
private sealed class ItemConverter : IContentItemConverter
{
public JsonObject ConvertItem(IField field, JsonObject source)
{
source["extraField"] = 42;
return source;
}
}
var actual = private sealed class ValueConverter : IContentValueConverter
source.Convert(schema, {
FieldConverters.ForValues(components, converter)); public (bool Remove, JsonValue) ConvertValue(IField field, JsonValue source, IField? parent)
{
var remove = field.Name != "assets1";
Assert.Equal(expected, actual); return (remove, source);
}
} }
} }
} }

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

@ -27,90 +27,200 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
} }
[Fact] [Fact]
public void Should_convert_data_with_value_converter() public void Should_not_change_data_if_all_field_values_have_correct_type()
{ {
var field = Fields.String(1, "string", Partitioning.Invariant); var field1 = Fields.Number(1, "number1", Partitioning.Language);
var field2 = Fields.Number(2, "number2", Partitioning.Language);
var schema =
new Schema("my-schema")
.AddField(field1)
.AddField(field2);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("en", 1))
.AddField(field2.Name,
new ContentFieldData() new ContentFieldData()
.AddInvariant(new JsonObject()); .AddLocalized("en", JsonValue.Null)
.AddLocalized("de", 1));
var result = FieldConverters.ForValues(ResolvedComponents.Empty, (value, field, parent) => null)(source, field); var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ExcludeChangedTypes(TestUtils.DefaultSerializer))
.Convert(source);
var expected = new ContentFieldData(); var expected = source;
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
[Fact] [Fact]
public void Should_return_field_data_if_excluding_changed_types_and_all_values_are_valid() public void Should_remove_fields_with_invalid_data()
{ {
var field = Fields.Number(1, "number", Partitioning.Invariant); var field1 = Fields.Number(1, "number1", Partitioning.Language);
var field2 = Fields.Number(2, "number2", Partitioning.Language);
var schema =
new Schema("my-schema")
.AddField(field1)
.AddField(field2);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("en", JsonValue.Null) .AddLocalized("en", 1))
.AddLocalized("de", 1); .AddField(field2.Name,
new ContentFieldData()
.AddLocalized("en", "2")
.AddLocalized("de", 2));
var result = FieldConverters.ExcludeChangedTypes(TestUtils.DefaultSerializer)(source, field); var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ExcludeChangedTypes(TestUtils.DefaultSerializer))
.Convert(source);
Assert.Equal(source, result); var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("en", 1));
Assert.Equal(expected, result);
} }
[Fact] [Fact]
public void Should_return_null_if_excluding_changed_types_and_one_value_is_invalid() public void Should_remove_hidden_fields()
{ {
var field = Fields.Number(1, "number", Partitioning.Invariant); var field1 = Fields.Number(1, "number1", Partitioning.Language);
var field2 = Fields.Number(2, "number2", Partitioning.Language).Hide();
var schema =
new Schema("my-schema")
.AddField(field1)
.AddField(field2);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("en", "EN") .AddLocalized("en", 1))
.AddLocalized("de", 0); .AddField(field2.Name,
new ContentFieldData()
.AddLocalized("en", JsonValue.Null)
.AddLocalized("de", 1));
var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(ExcludeHidden.Instance)
.Convert(source);
var result = FieldConverters.ExcludeChangedTypes(TestUtils.DefaultSerializer)(source, field); var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("en", 1));
Assert.Null(result); Assert.Equal(expected, result);
} }
[Fact] [Fact]
public void Should_return_field_data_if_field_not_hidden() public void Should_not_remove_hidden_fields()
{ {
var field = Fields.String(1, "string", Partitioning.Language); var field1 = Fields.Number(1, "number1", Partitioning.Language);
var field2 = Fields.Number(2, "number2", Partitioning.Language);
var source = new ContentFieldData(); var schema =
new Schema("my-schema")
.AddField(field1)
.AddField(field2)
.HideField(2);
var result = FieldConverters.ExcludeHidden(source, field); var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("en", 1))
.AddField(field2.Name,
new ContentFieldData()
.AddLocalized("en", "2"));
var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ExcludeChangedTypes(TestUtils.DefaultSerializer))
.Convert(source);
var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("en", 1));
Assert.Equal(source, result); Assert.Equal(expected, result);
} }
[Fact] [Fact]
public void Should_return_null_if_field_hidden() public void Should_remove_old_languages()
{ {
var field = Fields.String(1, "string", Partitioning.Language); var field1 = Fields.String(1, "string1", Partitioning.Language);
var source = new ContentFieldData(); var schema =
new Schema("my-schema")
.AddField(field1);
var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("en", "EN")
.AddLocalized("de", "DE")
.AddLocalized("it", "IT"));
var result = FieldConverters.ExcludeHidden(source, field.Hide()); var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ResolveLanguages(languagesConfig))
.Convert(source);
Assert.Null(result); var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("en", "EN")
.AddLocalized("de", "DE"));
Assert.Equal(expected, result);
} }
[Fact] [Fact]
public void Should_resolve_languages_and_cleanup_old_languages() public void Should_remove_unwanted_languages()
{ {
var field = Fields.String(1, "string", Partitioning.Language); var field1 = Fields.String(1, "string1", Partitioning.Language);
var schema =
new Schema("my-schema")
.AddField(field1);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("en", "EN") .AddLocalized("en", "EN")
.AddLocalized("it", "IT"); .AddLocalized("de", "DE")
.AddLocalized("it", "IT"));
var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ResolveLanguages(languagesConfig, true, new[] { Language.DE }))
.Convert(source);
var expected = var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("en", "EN"); .AddLocalized("de", "DE"));
var result = FieldConverters.ResolveLanguages(languagesConfig)(source, field);
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -119,53 +229,99 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
[MemberData(nameof(InvalidValues))] [MemberData(nameof(InvalidValues))]
public void Should_resolve_master_language_from_invariant(JsonValue value) public void Should_resolve_master_language_from_invariant(JsonValue value)
{ {
var field = Fields.String(1, "string", Partitioning.Language); var field1 = Fields.String(1, "string", Partitioning.Language);
var schema =
new Schema("my-schema")
.AddField(field1);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("iv", "A") .AddLocalized("iv", "A")
.AddLocalized("it", "B"); .AddLocalized("it", "B"));
if (value != false) if (value != false)
{ {
source["en"] = value!; source[field1.Name]!["en"] = value!;
} }
var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ResolveLanguages(languagesConfig))
.Convert(source);
var expected = var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("en", "A"); .AddLocalized("en", "A"));
var result = FieldConverters.ResolveLanguages(languagesConfig)(source, field);
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
[Fact] [Theory]
public void Should_not_resolve_master_language_if_not_found() [MemberData(nameof(InvalidValues))]
public void Should_not_resolve_master_language_if_not_found(JsonValue value)
{ {
var field = Fields.String(1, "string", Partitioning.Invariant); var field1 = Fields.String(1, "string", Partitioning.Language);
var source = new ContentFieldData(); var schema =
new Schema("my-schema")
.AddField(field1);
var result = FieldConverters.ResolveLanguages(languagesConfig)(source, field); var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData()
.AddLocalized("es", "A")
.AddLocalized("it", "B"));
if (value != false)
{
source[field1.Name]!["en"] = value;
}
var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ResolveLanguages(languagesConfig))
.Convert(source);
var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData());
Assert.Equal(source, result); if (value != false)
{
expected[field1.Name]!["en"] = value;
}
Assert.Equal(expected, result);
} }
[Fact] [Fact]
public void Should_resolve_invariant() public void Should_keep_invariant()
{ {
var field = Fields.String(1, "string", Partitioning.Invariant); var field1 = Fields.String(1, "string", Partitioning.Invariant);
var schema =
new Schema("my-schema")
.AddField(field1);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddInvariant("A"); .AddInvariant("A"));
var expected = var result =
new ContentFieldData() new ContentConverter(ResolvedComponents.Empty, schema)
.AddInvariant("A"); .Add(new ResolveLanguages(languagesConfig))
.Convert(source);
var result = FieldConverters.ResolveInvariant(languagesConfig)(source, field); var expected = source;
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -174,42 +330,70 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
[MemberData(nameof(InvalidValues))] [MemberData(nameof(InvalidValues))]
public void Should_resolve_invariant_from_master_language(JsonValue value) public void Should_resolve_invariant_from_master_language(JsonValue value)
{ {
var field = Fields.String(1, "string", Partitioning.Invariant); var field1 = Fields.String(1, "string", Partitioning.Invariant);
var schema =
new Schema("my-schema")
.AddField(field1);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("de", "DE") .AddLocalized("de", "DE")
.AddLocalized("en", "EN"); .AddLocalized("en", "EN"));
if (value != false) if (value != false)
{ {
source[InvariantPartitioning.Key] = value!; source[field1.Name]![InvariantPartitioning.Key] = value;
} }
var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ResolveInvariant(languagesConfig))
.Convert(source);
var expected = var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddInvariant("EN"); .AddInvariant("EN"));
var result = FieldConverters.ResolveInvariant(languagesConfig)(source, field);
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
[Fact] [Theory]
public void Should_resolve_invariant_from_first_language() [MemberData(nameof(InvalidValues))]
public void Should_resolve_invariant_from_first_language(JsonValue value)
{ {
var field = Fields.String(1, "string", Partitioning.Invariant); var field1 = Fields.String(1, "string", Partitioning.Invariant);
var schema =
new Schema("my-schema")
.AddField(field1);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("de", "DE") .AddLocalized("de", "DE")
.AddLocalized("it", "IT"); .AddLocalized("it", "IT"));
if (value != false)
{
source[field1.Name]![InvariantPartitioning.Key] = value;
}
var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ResolveInvariant(languagesConfig))
.Convert(source);
var expected = var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddInvariant("DE"); .AddInvariant("DE"));
var result = FieldConverters.ResolveInvariant(languagesConfig)(source, field);
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -217,20 +401,36 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
[Fact] [Fact]
public void Should_not_resolve_invariant_if_not_found() public void Should_not_resolve_invariant_if_not_found()
{ {
var field = Fields.String(1, "string", Partitioning.Language); var field1 = Fields.String(1, "string", Partitioning.Invariant);
var source = new ContentFieldData(); var schema =
new Schema("my-schema")
.AddField(field1);
var result = FieldConverters.ResolveInvariant(languagesConfig)(source, field); var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData());
Assert.Same(source, result); var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ResolveLanguages(languagesConfig))
.Convert(source);
var expected = source;
Assert.Equal(expected, result);
} }
[Theory] [Theory]
[MemberData(nameof(InvalidValues))] [MemberData(nameof(InvalidValues))]
public void Should_resolve_from_fallback_language_if_found(JsonValue value) public void Should_resolve_from_fallback_language_if_found(JsonValue value)
{ {
var field = Fields.String(1, "string", Partitioning.Language); var field1 = Fields.String(1, "string", Partitioning.Language);
var schema =
new Schema("my-schema")
.AddField(field1);
var config = var config =
LanguagesConfig.English LanguagesConfig.English
@ -239,79 +439,60 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
.Set(Language.ES, false, Language.IT); .Set(Language.ES, false, Language.IT);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("en", "EN") .AddLocalized("en", "EN")
.AddLocalized("it", "IT"); .AddLocalized("it", "IT"));
if (value != false) if (value != false)
{ {
source["de"] = value!; source[field1.Name]!["de"] = value!;
} }
var result =
new ContentConverter(ResolvedComponents.Empty, schema)
.Add(new ResolveLanguages(config))
.Convert(source);
var expected = var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("en", "EN") .AddLocalized("en", "EN")
.AddLocalized("it", "IT") .AddLocalized("it", "IT")
.AddLocalized("es", "IT") .AddLocalized("es", "IT")
.AddLocalized("de", "EN"); .AddLocalized("de", "EN"));
var result = FieldConverters.ResolveFallbackLanguages(config)(source, field);
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
[Fact] [Fact]
public void Should_not_return_value_if_master_is_missing() public void Should_return_master_language_if_languages_to_filter_are_invalid()
{ {
var field = Fields.String(1, "string", Partitioning.Language); var field1 = Fields.String(1, "string", Partitioning.Language);
var source = var schema =
new ContentFieldData() new Schema("my-schema")
.AddLocalized("de", "DE"); .AddField(field1);
var expected =
new ContentFieldData()
.AddLocalized("de", "DE");
var result = FieldConverters.ResolveFallbackLanguages(languagesConfig)(source, field);
Assert.Equal(expected, result);
}
[Fact]
public void Should_filter_languages()
{
var field = Fields.String(1, "string", Partitioning.Language);
var source = var source =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("en", "EN") .AddLocalized("en", "EN")
.AddLocalized("de", "DE"); .AddLocalized("de", "DE"));
var expected =
new ContentFieldData()
.AddLocalized("de", "DE");
var result = FieldConverters.FilterLanguages(languagesConfig, new[] { Language.DE })(source, field);
Assert.Equal(expected, result);
}
[Fact] var result =
public void Should_return_master_language_if_languages_to_filter_are_invalid() new ContentConverter(ResolvedComponents.Empty, schema)
{ .Add(new ResolveLanguages(languagesConfig, true, Language.IT))
var field = Fields.String(1, "string", Partitioning.Language); .Convert(source);
var source =
new ContentFieldData()
.AddLocalized("en", "EN")
.AddLocalized("de", "DE");
var expected = var expected =
new ContentData()
.AddField(field1.Name,
new ContentFieldData() new ContentFieldData()
.AddLocalized("en", "EN"); .AddLocalized("en", "EN"));
var result = FieldConverters.FilterLanguages(languagesConfig, new[] { Language.CA })(source, field);
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -323,7 +504,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var source = new ContentFieldData(); var source = new ContentFieldData();
var result = FieldConverters.ResolveFallbackLanguages(languagesConfig)(source, field); var result =
new ResolveLanguages(languagesConfig)
.ConvertFieldAfter(field, source);
Assert.Same(source, result); Assert.Same(source, result);
} }
@ -335,7 +518,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var source = new ContentFieldData(); var source = new ContentFieldData();
var result = FieldConverters.FilterLanguages(languagesConfig, Enumerable.Empty<Language>())(source, field); var result =
new ResolveLanguages(languagesConfig, true, Array.Empty<Language>())
.ConvertFieldAfter(field, source);
Assert.Same(source, result); Assert.Same(source, result);
} }
@ -347,9 +532,44 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var source = new ContentFieldData(); var source = new ContentFieldData();
var result = FieldConverters.FilterLanguages(languagesConfig, null)(source, field); var result =
new ResolveLanguages(languagesConfig)
.ConvertFieldAfter(field, source);
Assert.Same(source, result); Assert.Same(source, result);
} }
/*
[Fact]
public void Should_add_schema_name_to_component()
{
var field = Fields.Component(1, "component", Partitioning.Invariant);
var componentId = DomainId.NewGuid();
var component = new Schema("my-component");
var components = new ResolvedComponents(new Dictionary<DomainId, Schema>
{
[componentId] = component
});
var source =
new ContentFieldData()
.AddInvariant(
new JsonObject()
.Add(Component.Discriminator, componentId));
var expected =
new ContentFieldData()
.AddInvariant(
new JsonObject()
.Add(Component.Discriminator, componentId)
.Add("schemaName", component.Name));
var result = FieldConverters.AddSchemaName(components)(data, field);
var expected = new ContentFieldData();
Assert.Equal(expected, result);
}*/
} }
} }

188
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs

@ -6,6 +6,7 @@
// ========================================================================== // ==========================================================================
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Core.TestHelpers;
@ -35,23 +36,27 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
} }
[Fact] [Fact]
public void Should_return_null_if_field_hidden() public void Should_return_true_if_field_hidden()
{ {
var source = JsonValue.Create(123); var source = JsonValue.Create(123);
var result = ValueConverters.ExcludeHidden(source, stringField.Hide(), null); var (remove, _) =
ExcludeHidden.Instance
.ConvertValue(stringField.Hide(), source, null);
Assert.Null(result); Assert.True(remove);
} }
[Fact] [Fact]
public void Should_return_null_if_field_has_wrong_type() public void Should_return_true_if_field_has_wrong_type()
{ {
var source = JsonValue.Create("invalid"); var source = JsonValue.Create("invalid");
var result = ValueConverters.ExcludeChangedTypes(TestUtils.DefaultSerializer)(source, numberField, null); var (remove, _) =
new ExcludeChangedTypes(TestUtils.DefaultSerializer)
.ConvertValue(numberField, source, numberField);
Assert.Null(result); Assert.True(remove);
} }
[Theory] [Theory]
@ -61,11 +66,19 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var field = Fields.Assets(1, "assets", Partitioning.Invariant); var field = Fields.Assets(1, "assets", Partitioning.Invariant);
var source = JsonValue.Array(id1, id2); var source =
JsonValue.Array(
id1,
id2);
var expected = JsonValue.Array($"url/to/{id1}", $"url/to/{id2}"); var (_, result) =
new ResolveAssetUrls(appId, urlGenerator, HashSet.Of(path))
.ConvertValue(field, source, null);
var result = ValueConverters.ResolveAssetUrls(appId, HashSet.Of(path), urlGenerator)(source, field, null); var expected =
JsonValue.Array(
$"url/to/{id1}",
$"url/to/{id2}");
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -77,11 +90,16 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var field = Fields.Assets(1, "assets", Partitioning.Invariant); var field = Fields.Assets(1, "assets", Partitioning.Invariant);
var source = JsonValue.Array(id1, id2); var source =
JsonValue.Array(
id1,
id2);
var expected = source; var (_, result) =
new ResolveAssetUrls(appId, urlGenerator, HashSet.Of(path))
.ConvertValue(field, source, null);
var result = ValueConverters.ResolveAssetUrls(appId, HashSet.Of(path), urlGenerator)(source, field, null); var expected = source;
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -93,11 +111,19 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var field = Fields.Array(1, "parent", Partitioning.Invariant, null, null, Fields.Assets(11, "assets")); var field = Fields.Array(1, "parent", Partitioning.Invariant, null, null, Fields.Assets(11, "assets"));
var source = JsonValue.Array(id1, id2); var source =
JsonValue.Array(
id1,
id2);
var expected = JsonValue.Array($"url/to/{id1}", $"url/to/{id2}"); var (_, result) =
new ResolveAssetUrls(appId, urlGenerator, HashSet.Of(path))
.ConvertValue(field.FieldsByName["assets"], source, field);
var result = ValueConverters.ResolveAssetUrls(appId, HashSet.Of(path), urlGenerator)(source, field.Fields[0], field); var expected =
JsonValue.Array(
$"url/to/{id1}",
$"url/to/{id2}");
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -111,11 +137,139 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var field = Fields.Array(1, "parent", Partitioning.Invariant, null, null, Fields.Assets(11, "assets")); var field = Fields.Array(1, "parent", Partitioning.Invariant, null, null, Fields.Assets(11, "assets"));
var source = JsonValue.Array(id1, id2); var source =
JsonValue.Array(
id1,
id2);
var (_, result) =
new ResolveAssetUrls(appId, urlGenerator, HashSet.Of(path))
.ConvertValue(field, source, null);
var expected = source;
Assert.Equal(expected, result);
}
[Fact]
public void Should_add_schema_name_if_component()
{
var field = Fields.Component(1, "component", Partitioning.Invariant);
var componentId = DomainId.NewGuid();
var component = new Schema("my-component");
var components = new ResolvedComponents(new Dictionary<DomainId, Schema>
{
[componentId] = component
});
var source =
new JsonObject()
.Add(Component.Discriminator, componentId);
var result =
new AddSchemaNames(components)
.ConvertItem(field, source);
var expected =
new JsonObject()
.Add(Component.Discriminator, componentId)
.Add("schemaName", component.Name);
Assert.Equal(expected, result);
}
[Fact]
public void Should_not_add_schema_name_if_field_already_exists()
{
var field = Fields.Component(1, "component", Partitioning.Invariant);
var componentId = DomainId.NewGuid();
var component = new Schema("my-component");
var components = new ResolvedComponents(new Dictionary<DomainId, Schema>
{
[componentId] = component
});
var source =
new JsonObject()
.Add(Component.Discriminator, componentId)
.Add("schemaName", "existing");
var result =
new AddSchemaNames(components)
.ConvertItem(field, source);
var expected = source;
Assert.Equal(expected, result);
}
[Fact]
public void Should_not_add_schema_name_if_array_field()
{
var field = Fields.Array(1, "component", Partitioning.Invariant);
var componentId = DomainId.NewGuid();
var component = new Schema("my-component");
var components = new ResolvedComponents(new Dictionary<DomainId, Schema>
{
[componentId] = component
});
var source =
new JsonObject()
.Add(Component.Discriminator, componentId);
var result =
new AddSchemaNames(components)
.ConvertItem(field, source);
var expected = source;
Assert.Equal(expected, result);
}
[Fact]
public void Should_not_add_schema_name_if_not_a_component()
{
var field = Fields.Component(1, "component", Partitioning.Invariant);
var componentId = DomainId.NewGuid();
var component = new Schema("my-component");
var components = new ResolvedComponents(new Dictionary<DomainId, Schema>
{
[componentId] = component
});
var source =
new JsonObject();
var result =
new AddSchemaNames(components)
.ConvertItem(field, source);
var expected = source; var expected = source;
var result = ValueConverters.ResolveAssetUrls(appId, HashSet.Of(path), urlGenerator)(source, field.Fields[0], field); Assert.Equal(expected, result);
}
[Fact]
public void Should_not_add_schema_name_if_component_not_found()
{
var field = Fields.Component(1, "component", Partitioning.Invariant);
var componentId = DomainId.NewGuid();
var source =
new JsonObject()
.Add(Component.Discriminator, componentId);
var result =
new AddSchemaNames(ResolvedComponents.Empty)
.ConvertItem(field, source);
var expected = source;
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }

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

@ -190,13 +190,12 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
.Add("nestedAssets", JsonValue.Array(id2)))) .Add("nestedAssets", JsonValue.Array(id2))))
.Add(Component.Discriminator, DomainId.Empty)))); .Add(Component.Discriminator, DomainId.Empty))));
var converter = var result =
FieldConverters.ForValues(components, new ContentConverter(components, schema)
ValueReferencesConverter.CleanReferences(new HashSet<DomainId> { id2 })); .Add(new ValueReferencesConverter(new HashSet<DomainId> { id2 }))
.Convert(source);
var actual = source.Convert(schema, converter); Assert.Equal(expected, result);
Assert.Equal(expected, actual);
} }
[Fact] [Fact]
@ -282,7 +281,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
[MemberData(nameof(ReferencingFields))] [MemberData(nameof(ReferencingFields))]
public void Should_return_same_value_from_field_if_value_is_json_null(IField field) public void Should_return_same_value_from_field_if_value_is_json_null(IField field)
{ {
var result = ValueReferencesConverter.CleanReferences(RandomIds())(JsonValue.Null, field, null); var (_, result) = new ValueReferencesConverter(RandomIds()).ConvertValue(field, JsonValue.Null, null);
Assert.Equal(JsonValue.Null, result); Assert.Equal(JsonValue.Null, result);
} }
@ -296,7 +295,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var value = CreateValue(id1, id2); var value = CreateValue(id1, id2);
var result = ValueReferencesConverter.CleanReferences(HashSet.Of(id1))(value, field, null); var (_, result) = new ValueReferencesConverter(HashSet.Of(id1)).ConvertValue(field, value, null);
Assert.Equal(CreateValue(id1), result); Assert.Equal(CreateValue(id1), result);
} }

Loading…
Cancel
Save