Browse Source

Content enrichment improvements.

pull/478/head
Sebastian 6 years ago
parent
commit
ae5de75c3e
  1. 57
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/IFieldVisitor.cs
  2. 34
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs
  3. 8
      backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueExtensions.cs
  4. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueFactory.cs
  5. 6
      backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueGenerator.cs
  6. 82
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs
  7. 63
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs
  8. 41
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs
  9. 90
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs
  10. 15
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs
  11. 12
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonError.cs
  12. 12
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs
  13. 12
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs
  14. 3
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  15. 52
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_EventHandling.cs
  16. 5
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs
  17. 35
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/CleanupReferences.cs
  18. 15
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Extensions.cs
  19. 2
      backend/src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs
  20. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs
  21. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs
  22. 3
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs
  23. 70
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs
  24. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs
  25. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs
  26. 5
      backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs
  27. 7
      backend/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs
  28. 36
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs
  29. 12
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs
  30. 289
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs
  31. 11
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs
  32. 12
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs
  33. 15
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Tags/TagNormalizerTests.cs
  34. 4
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs
  35. 2
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/UIFieldTests.cs
  36. 31
      backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/AExtensions.cs
  37. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetEnricherTests.cs
  38. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryServiceTests.cs
  39. 146
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ConvertDataTests.cs
  40. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs
  41. 6
      backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AExtensions.cs
  42. 4
      backend/tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs

57
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/IFieldVisitor.cs

@ -5,30 +5,65 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
#pragma warning disable CS8653 // A default expression introduces a null value for a type parameter.
namespace Squidex.Domain.Apps.Core.Schemas
{
public interface IFieldVisitor<out T>
{
T Visit(IArrayField field);
T Visit(IArrayField field)
{
return default;
}
T Visit(IField<AssetsFieldProperties> field);
T Visit(IField<AssetsFieldProperties> field)
{
return default;
}
T Visit(IField<BooleanFieldProperties> field);
T Visit(IField<BooleanFieldProperties> field)
{
return default;
}
T Visit(IField<DateTimeFieldProperties> field);
T Visit(IField<DateTimeFieldProperties> field)
{
return default;
}
T Visit(IField<GeolocationFieldProperties> field);
T Visit(IField<GeolocationFieldProperties> field)
{
return default;
}
T Visit(IField<JsonFieldProperties> field);
T Visit(IField<JsonFieldProperties> field)
{
return default;
}
T Visit(IField<NumberFieldProperties> field);
T Visit(IField<NumberFieldProperties> field)
{
return default;
}
T Visit(IField<ReferencesFieldProperties> field);
T Visit(IField<ReferencesFieldProperties> field)
{
return default;
}
T Visit(IField<StringFieldProperties> field);
T Visit(IField<StringFieldProperties> field)
{
return default;
}
T Visit(IField<TagsFieldProperties> field);
T Visit(IField<TagsFieldProperties> field)
{
return default;
}
T Visit(IField<UIFieldProperties> field);
T Visit(IField<UIFieldProperties> field)
{
return default;
}
}
}

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

@ -7,11 +7,9 @@
using System;
using System.Collections.Generic;
using System.Text;
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
{
@ -20,38 +18,6 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
private static readonly Func<IRootField, string> KeyNameResolver = f => f.Name;
private static readonly Func<IRootField, long> KeyIdResolver = f => f.Id;
private static void AppendText(IJsonValue value, StringBuilder stringBuilder, int maxFieldLength, string separator, bool allowObjects)
{
if (value.Type == JsonValueType.String)
{
var text = value.ToString();
if (text.Length <= maxFieldLength)
{
if (stringBuilder.Length > 0)
{
stringBuilder.Append(separator);
}
stringBuilder.Append(text);
}
}
else if (value is JsonArray array)
{
foreach (var item in array)
{
AppendText(item, stringBuilder, maxFieldLength, separator, true);
}
}
else if (value is JsonObject obj && allowObjects)
{
foreach (var item in obj.Values)
{
AppendText(item, stringBuilder, maxFieldLength, separator, true);
}
}
}
public static NamedContentData ConvertId2Name(this IdContentData content, Schema schema, params FieldConverter[] converters)
{
Guard.NotNull(schema);

8
backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnrichmentExtensions.cs → backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueExtensions.cs

@ -8,13 +8,13 @@
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Core.EnrichContent
namespace Squidex.Domain.Apps.Core.DefaultValues
{
public static class ContentEnrichmentExtensions
public static class DefaultValueExtensions
{
public static void Enrich(this NamedContentData data, Schema schema, PartitionResolver partitionResolver)
public static void GenerateDefaultValues(this NamedContentData data, Schema schema, PartitionResolver partitionResolver)
{
var enricher = new ContentEnricher(schema, partitionResolver);
var enricher = new DefaultValueGenerator(schema, partitionResolver);
enricher.Enrich(data);
}

2
backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/DefaultValueFactory.cs → backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueFactory.cs

@ -11,7 +11,7 @@ using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.EnrichContent
namespace Squidex.Domain.Apps.Core.DefaultValues
{
public sealed class DefaultValueFactory : IFieldVisitor<IJsonValue>
{

6
backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnricher.cs → backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueGenerator.cs

@ -11,14 +11,14 @@ using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.EnrichContent
namespace Squidex.Domain.Apps.Core.DefaultValues
{
public sealed class ContentEnricher
public sealed class DefaultValueGenerator
{
private readonly Schema schema;
private readonly PartitionResolver partitionResolver;
public ContentEnricher(Schema schema, PartitionResolver partitionResolver)
public DefaultValueGenerator(Schema schema, PartitionResolver partitionResolver)
{
Guard.NotNull(schema);
Guard.NotNull(partitionResolver);

82
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs

@ -17,77 +17,79 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ContentReferencesExtensions
{
public static IEnumerable<Guid> GetReferencedIds(this IdContentData source, Schema schema, Ids strategy = Ids.All)
public static HashSet<Guid> GetReferencedIds(this NamedContentData source, Schema schema)
{
Guard.NotNull(schema);
foreach (var field in schema.Fields)
{
var ids = source.GetReferencedIds(field, strategy);
var extractor = new ReferencesExtractor(new HashSet<Guid>());
foreach (var id in ids)
{
yield return id;
}
}
AddReferencedIds(source, schema.Fields, extractor);
return extractor.Result;
}
public static IEnumerable<Guid> GetReferencedIds(this IdContentData source, IField field, Ids strategy = Ids.All)
public static void AddReferencedIds(this NamedContentData source, Schema schema, HashSet<Guid> result)
{
Guard.NotNull(field);
Guard.NotNull(schema);
if (source.TryGetValue(field.Id, out var fieldData) && fieldData != null)
{
foreach (var partitionValue in fieldData)
{
var ids = field.GetReferencedIds(partitionValue.Value, strategy);
var extractor = new ReferencesExtractor(result);
foreach (var id in ids)
{
yield return id;
}
}
}
AddReferencedIds(source, schema.Fields, extractor);
}
public static IEnumerable<Guid> GetReferencedIds(this NamedContentData source, Schema schema, Ids strategy = Ids.All)
public static void AddReferencedIds(this NamedContentData source, IEnumerable<IField> fields, HashSet<Guid> result)
{
Guard.NotNull(schema);
Guard.NotNull(fields);
var extractor = new ReferencesExtractor(result);
return GetReferencedIds(source, schema.Fields, strategy);
AddReferencedIds(source, fields, extractor);
}
public static IEnumerable<Guid> GetReferencedIds(this NamedContentData source, IEnumerable<IField> fields, Ids strategy = Ids.All)
public static void AddReferencedIds(this NamedContentData source, IField field, HashSet<Guid> result)
{
Guard.NotNull(fields);
Guard.NotNull(field);
foreach (var field in fields)
{
var ids = source.GetReferencedIds(field, strategy);
var extractor = new ReferencesExtractor(result);
foreach (var id in ids)
{
yield return id;
AddReferencedIds(source, field, extractor);
}
private static void AddReferencedIds(NamedContentData source, IEnumerable<IField> fields, ReferencesExtractor extractor)
{
foreach (var field in fields)
{
AddReferencedIds(source, field, extractor);
}
}
public static IEnumerable<Guid> GetReferencedIds(this NamedContentData source, IField field, Ids strategy = Ids.All)
private static void AddReferencedIds(NamedContentData source, IField field, ReferencesExtractor extractor)
{
Guard.NotNull(field);
if (source.TryGetValue(field.Name, out var fieldData) && fieldData != null)
{
foreach (var partitionValue in fieldData)
{
var ids = field.GetReferencedIds(partitionValue.Value, strategy);
extractor.SetValue(partitionValue.Value);
foreach (var id in ids)
{
yield return id;
field.Accept(extractor);
}
}
}
public static HashSet<Guid> GetReferencedIds(this IField field, IJsonValue? value)
{
var result = new HashSet<Guid>();
if (value != null)
{
var extractor = new ReferencesExtractor(result);
extractor.SetValue(value);
field.Accept(extractor);
}
return result;
}
public static JsonObject FormatReferences(this NamedContentData data, Schema schema, IFieldPartitioning partitioning, string separator = ", ")

63
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs

@ -8,25 +8,26 @@
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public sealed class ReferencesCleaner : IFieldVisitor<IJsonValue>
{
private readonly IJsonValue value;
private readonly ICollection<Guid>? oldReferences;
private readonly HashSet<Guid> validIds;
private IJsonValue value;
private ReferencesCleaner(IJsonValue value, ICollection<Guid>? oldReferences)
public ReferencesCleaner(HashSet<Guid> validIds)
{
this.value = value;
Guard.NotNull(validIds);
this.oldReferences = oldReferences;
this.validIds = validIds;
}
public static IJsonValue CleanReferences(IField field, IJsonValue value, ICollection<Guid>? oldReferences)
public void SetValue(IJsonValue newValue)
{
return field.Accept(new ReferencesCleaner(value, oldReferences));
value = newValue;
}
public IJsonValue Visit(IField<AssetsFieldProperties> field)
@ -36,31 +37,9 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
public IJsonValue Visit(IField<ReferencesFieldProperties> field)
{
if (oldReferences?.Contains(field.Properties.SingleId()) == true)
{
return JsonValue.Array();
}
return CleanIds();
}
private IJsonValue CleanIds()
{
var ids = value.ToGuidSet();
var isRemoved = false;
if (oldReferences != null)
{
foreach (var oldReference in oldReferences)
{
isRemoved |= ids.Remove(oldReference);
}
}
return isRemoved ? ids.ToJsonArray() : value;
}
public IJsonValue Visit(IField<BooleanFieldProperties> field)
{
return value;
@ -105,5 +84,31 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
return value;
}
private IJsonValue CleanIds()
{
if (value is JsonArray array)
{
var result = new JsonArray(array);
for (var i = 0; i < result.Count; i++)
{
if (!IsValidReference(result[i]))
{
result.RemoveAt(i);
i--;
}
}
return result;
}
return value;
}
private bool IsValidReference(IJsonValue item)
{
return item is JsonString s && Guid.TryParse(s.Value, out var guid) && validIds.Contains(guid);
}
}
}

41
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs

@ -7,51 +7,16 @@
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ReferencesExtensions
{
public static IEnumerable<Guid> GetReferencedIds(this IField field, IJsonValue? value, Ids strategy = Ids.All)
{
return ReferencesExtractor.ExtractReferences(field, value, strategy);
}
public static IJsonValue CleanReferences(this IField field, IJsonValue value, ICollection<Guid>? oldReferences)
{
if (IsNull(value))
{
return value;
}
return ReferencesCleaner.CleanReferences(field, value, oldReferences);
}
private static bool IsNull(IJsonValue value)
{
return value == null || value.Type == JsonValueType.Null;
}
public static JsonArray ToJsonArray(this HashSet<Guid> ids)
{
var result = JsonValue.Array();
foreach (var id in ids)
{
result.Add(JsonValue.Create(id.ToString()));
}
return result;
}
public static HashSet<Guid> ToGuidSet(this IJsonValue? value)
public static void AddIds(this IJsonValue? value, HashSet<Guid> result)
{
if (value is JsonArray array)
{
var result = new HashSet<Guid>();
foreach (var id in array)
{
if (id.Type == JsonValueType.String && Guid.TryParse(id.ToString(), out var guid))
@ -59,11 +24,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
result.Add(guid);
}
}
return result;
}
return new HashSet<Guid>();
}
}
}

90
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs

@ -9,31 +9,35 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public sealed class ReferencesExtractor : IFieldVisitor<IEnumerable<Guid>>
internal sealed class ReferencesExtractor : IFieldVisitor<None>
{
private readonly IJsonValue? value;
private readonly Ids strategy;
private readonly HashSet<Guid> result;
private IJsonValue? value;
private ReferencesExtractor(IJsonValue? value, Ids strategy)
public HashSet<Guid> Result
{
this.value = value;
this.strategy = strategy;
get { return result; }
}
public static IEnumerable<Guid> ExtractReferences(IField field, IJsonValue? value, Ids strategy)
public ReferencesExtractor(HashSet<Guid> result)
{
return field.Accept(new ReferencesExtractor(value, strategy));
Guard.NotNull(result);
this.result = result;
}
public IEnumerable<Guid> Visit(IArrayField field)
public void SetValue(IJsonValue? newValue)
{
var result = new List<Guid>();
value = newValue;
}
public None Visit(IArrayField field)
{
if (value is JsonArray array)
{
foreach (var item in array.OfType<JsonObject>())
@ -42,75 +46,29 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
if (item.TryGetValue(nestedField.Name, out var nestedValue))
{
result.AddRange(nestedField.Accept(new ReferencesExtractor(nestedValue, strategy)));
}
}
}
}
return result;
}
public IEnumerable<Guid> Visit(IField<AssetsFieldProperties> field)
{
var ids = value.ToGuidSet();
return ids;
}
public IEnumerable<Guid> Visit(IField<ReferencesFieldProperties> field)
{
var ids = value.ToGuidSet();
SetValue(nestedValue);
if (strategy == Ids.All && field.Properties.SchemaIds != null)
{
foreach (var schemaId in field.Properties.SchemaIds)
{
ids.Add(schemaId);
}
}
return ids;
nestedField.Accept(this);
}
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>();
return None.Value;
}
public IEnumerable<Guid> Visit(IField<NumberFieldProperties> field)
public None Visit(IField<AssetsFieldProperties> field)
{
return Enumerable.Empty<Guid>();
}
value.AddIds(result);
public IEnumerable<Guid> Visit(IField<StringFieldProperties> field)
{
return Enumerable.Empty<Guid>();
return None.Value;
}
public IEnumerable<Guid> Visit(IField<TagsFieldProperties> field)
public None Visit(IField<ReferencesFieldProperties> field)
{
return Enumerable.Empty<Guid>();
}
value.AddIds(result);
public IEnumerable<Guid> Visit(IField<UIFieldProperties> field)
{
return Enumerable.Empty<Guid>();
return None.Value;
}
}
}

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

@ -14,18 +14,25 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ValueReferencesConverter
{
public static ValueConverter CleanReferences(IEnumerable<Guid> deletedReferencedIds)
public static ValueConverter CleanReferences(HashSet<Guid>? validIds = null)
{
var ids = new HashSet<Guid>(deletedReferencedIds);
if (validIds == null || validIds.Count == 0)
{
return (value, field) => value;
}
var cleaner = new ReferencesCleaner(validIds);
return (value, field) =>
{
if (value.Type == JsonValueType.Null)
{
return value;
return value!;
}
return field.CleanReferences(value, ids);
cleaner.SetValue(value);
return field.Accept(cleaner);
};
}
}

12
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/Ids.cs → backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonError.cs

@ -5,11 +5,15 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
namespace Squidex.Domain.Apps.Core.ValidateContent
{
public enum Ids
public sealed class JsonError
{
All,
ContentOnly
public string Error { get; }
public JsonError(string error)
{
Error = error;
}
}
}

12
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs

@ -89,6 +89,18 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
public async Task<IReadOnlyList<Guid>> QueryIdsAsync(Guid appId, HashSet<Guid> ids)
{
using (Profiler.TraceMethod<MongoAssetRepository>("QueryAsyncByIds"))
{
var find = Collection.Find(x => ids.Contains(x.Id)).Only(x => x.Id);
var assetItems = await find.ToListAsync();
return assetItems.Select(x => Guid.Parse(x["_si"].AsString)).ToList();
}
}
public async Task<IResultList<IAssetEntity>> QueryAsync(Guid appId, HashSet<Guid> ids)
{
using (Profiler.TraceMethod<MongoAssetRepository>("QueryAsyncByIds"))

12
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs

@ -19,6 +19,7 @@ using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
[BsonIgnoreExtraElements]
public sealed class MongoContentEntity : IContentEntity, IVersionedEntity<Guid>
{
private NamedContentData? data;
@ -42,12 +43,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
[BsonRequired]
[BsonElement("rf")]
[BsonRepresentation(BsonType.String)]
public List<Guid>? ReferencedIds { get; set; }
[BsonRequired]
[BsonElement("rd")]
[BsonRepresentation(BsonType.String)]
public List<Guid> ReferencedIdsDeleted { get; set; } = new List<Guid>();
public HashSet<Guid>? ReferencedIds { get; set; }
[BsonRequired]
[BsonElement("ss")]
@ -122,11 +118,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
public void ParseData(Schema schema, IJsonSerializer serializer)
{
data = DataByIds?.FromMongoModel(schema, ReferencedIdsDeleted, serializer);
data = DataByIds?.FromMongoModel(schema, serializer);
if (DataDraftByIds != null)
{
dataDraft = DataDraftByIds.FromMongoModel(schema, ReferencedIdsDeleted, serializer);
dataDraft = DataDraftByIds.FromMongoModel(schema, serializer);
}
}
}

3
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs

@ -36,7 +36,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
private readonly IJsonSerializer serializer;
private readonly string typeAssetDeleted;
private readonly string typeContentDeleted;
private readonly CleanupReferences cleanupReferences;
private readonly QueryContent queryContentAsync;
private readonly QueryContentsByIds queryContentsById;
private readonly QueryContentsByQuery queryContentsByQuery;
@ -59,7 +58,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
this.serializer = serializer;
cleanupReferences = new CleanupReferences();
queryContentAsync = new QueryContent(serializer);
queryContentsById = new QueryContentsByIds(serializer, appProvider);
queryContentsByQuery = new QueryContentsByQuery(serializer, indexer);
@ -72,7 +70,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default)
{
await cleanupReferences.PrepareAsync(collection, ct);
await queryContentAsync.PrepareAsync(collection, ct);
await queryContentsById.PrepareAsync(collection, ct);
await queryContentsByQuery.PrepareAsync(collection, ct);

52
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_EventHandling.cs

@ -1,52 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
public partial class MongoContentRepository : IEventConsumer
{
public string Name
{
get { return GetType().Name; }
}
public string EventsFilter
{
get { return "^(content-)|(asset-)"; }
}
public bool Handles(StoredEvent @event)
{
return @event.Data.Type == typeAssetDeleted || @event.Data.Type == typeContentDeleted;
}
public Task On(Envelope<IEvent> @event)
{
switch (@event.Payload)
{
case AssetDeleted e:
return cleanupReferences.DoAsync(e.AssetId);
case ContentDeleted e:
return cleanupReferences.DoAsync(e.ContentId);
}
return TaskHelper.Done;
}
Task IEventConsumer.ClearAsync()
{
return TaskHelper.Done;
}
}
}

5
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs

@ -9,6 +9,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.ExtractReferenceIds;
using Squidex.Domain.Apps.Entities.Contents.State;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
@ -66,7 +67,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
var schema = await GetSchemaAsync(value.AppId.Id, value.SchemaId.Id);
var idData = value.Data!.ToMongoModel(schema.SchemaDef, serializer);
var idData = value.Data.ToMongoModel(schema.SchemaDef, serializer);
var idDraftData = idData;
if (!ReferenceEquals(value.Data, value.DataDraft))
@ -81,7 +82,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
IsDeleted = value.IsDeleted,
IndexedAppId = value.AppId.Id,
IndexedSchemaId = value.SchemaId.Id,
ReferencedIds = idData.ToReferencedIds(schema.SchemaDef),
ReferencedIds = value.Data.GetReferencedIds(schema.SchemaDef),
ScheduledAt = value.ScheduleJob?.DueTime,
Version = newVersion
});

35
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/CleanupReferences.cs

@ -1,35 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
internal sealed class CleanupReferences : OperationBase
{
protected override Task PrepareAsync(CancellationToken ct = default)
{
var index =
new CreateIndexModel<MongoContentEntity>(
Index.Ascending(x => x.ReferencedIds));
return Collection.Indexes.CreateOneAsync(index, cancellationToken: ct);
}
public Task DoAsync(Guid id)
{
return Collection.UpdateManyAsync(
Filter.And(
Filter.AnyEq(x => x.ReferencedIds, id),
Filter.AnyNe(x => x.ReferencedIdsDeleted, id)),
Update.AddToSet(x => x.ReferencedIdsDeleted, id));
}
}
}

15
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Extensions.cs

@ -6,12 +6,10 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.ExtractReferenceIds;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb;
@ -20,20 +18,13 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
public static class Extensions
{
public static List<Guid> ToReferencedIds(this IdContentData data, Schema schema)
{
return data.GetReferencedIds(schema).Distinct().ToList();
}
public static NamedContentData FromMongoModel(this IdContentData result, Schema schema, List<Guid> deletedIds, IJsonSerializer serializer)
public static NamedContentData FromMongoModel(this IdContentData result, Schema schema, IJsonSerializer serializer)
{
return result.ConvertId2Name(schema,
FieldConverters.ForValues(
ValueConverters.DecodeJson(serializer),
ValueReferencesConverter.CleanReferences(deletedIds)),
ValueConverters.DecodeJson(serializer)),
FieldConverters.ForNestedId2Name(
ValueConverters.DecodeJson(serializer),
ValueReferencesConverter.CleanReferences(deletedIds)));
ValueConverters.DecodeJson(serializer)));
}
public static IdContentData ToMongoModel(this NamedContentData result, Schema schema, IJsonSerializer serializer)

2
backend/src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs

@ -21,6 +21,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.Repositories
Task<IResultList<IAssetEntity>> QueryAsync(Guid appId, HashSet<Guid> ids);
Task<IReadOnlyList<Guid>> QueryIdsAsync(Guid appId, HashSet<Guid> ids);
Task<IAssetEntity?> FindAssetAsync(Guid id);
Task<IAssetEntity?> FindAssetBySlugAsync(Guid appId, string slug);

2
backend/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs

@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
});
}
await ctx.EnrichAsync(c.Data);
await ctx.GenerateDefaultValuesAsync(c.Data);
if (!c.DoNotValidate)
{

6
backend/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs

@ -9,7 +9,7 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.EnrichContent;
using Squidex.Domain.Apps.Core.DefaultValues;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Core.ValidateContent;
@ -76,9 +76,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
return context;
}
public Task EnrichAsync(NamedContentData data)
public Task GenerateDefaultValuesAsync(NamedContentData data)
{
data.Enrich(schemaEntity.SchemaDef, appEntity.PartitionResolver());
data.GenerateDefaultValues(schemaEntity.SchemaDef, appEntity.PartitionResolver());
return TaskHelper.Done;
}

3
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs

@ -73,10 +73,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
}
foreach (var step in steps)
{
using (Profiler.TraceMethod(step.ToString()!))
{
await step.EnrichAsync(context, results, GetSchema);
}
}
}
return results;
}

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

@ -5,10 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.ExtractReferenceIds;
using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
@ -16,20 +20,28 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
public sealed class ConvertData : IContentEnricherStep
{
private readonly IAssetUrlGenerator assetUrlGenerator;
private readonly IAssetRepository assetRepository;
private readonly IContentRepository contentRepository;
public ConvertData(IAssetUrlGenerator assetUrlGenerator)
public ConvertData(IAssetUrlGenerator assetUrlGenerator, IAssetRepository assetRepository, IContentRepository contentRepository)
{
Guard.NotNull(assetUrlGenerator);
Guard.NotNull(assetRepository);
Guard.NotNull(contentRepository);
this.assetUrlGenerator = assetUrlGenerator;
this.assetRepository = assetRepository;
this.contentRepository = contentRepository;
}
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas)
{
var converters = GenerateConverters(context).ToArray();
var resolveDataDraft = context.IsUnpublished() || context.IsFrontendClient;
var referenceCleaner = await CleanReferencesAsync(context, contents, schemas);
var converters = GenerateConverters(context, referenceCleaner).ToArray();
foreach (var group in contents.GroupBy(x => x.SchemaId.Id))
{
var schema = await schemas(group.Key);
@ -53,7 +65,51 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
}
}
private IEnumerable<FieldConverter> GenerateConverters(Context context)
private async Task<ValueConverter?> CleanReferencesAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas)
{
var ids = new HashSet<Guid>();
foreach (var group in contents.GroupBy(x => x.SchemaId.Id))
{
var schema = await schemas(group.Key);
foreach (var content in group)
{
content.Data?.AddReferencedIds(schema.SchemaDef, ids);
content.DataDraft?.AddReferencedIds(schema.SchemaDef, ids);
}
}
if (ids.Count > 0)
{
var taskForAssets = QueryAssetIdsAsync(context, ids);
var taskForContents = QueryContentIdsAsync(context, ids);
await Task.WhenAll(taskForAssets, taskForContents);
var foundIds = new HashSet<Guid>(taskForAssets.Result.Union(taskForContents.Result));
return ValueReferencesConverter.CleanReferences(foundIds);
}
return null;
}
private async Task<IEnumerable<Guid>> QueryContentIdsAsync(Context context, HashSet<Guid> ids)
{
var result = await contentRepository.QueryIdsAsync(context.App.Id, ids);
return result.Select(x => x.Id);
}
private async Task<IEnumerable<Guid>> QueryAssetIdsAsync(Context context, HashSet<Guid> ids)
{
var result = await assetRepository.QueryIdsAsync(context.App.Id, ids);
return result;
}
private IEnumerable<FieldConverter> GenerateConverters(Context context, ValueConverter? cleanReferences)
{
if (!context.IsFrontendClient)
{
@ -64,6 +120,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
yield return FieldConverters.ExcludeChangedTypes();
yield return FieldConverters.ForNestedName2Name(ValueConverters.ExcludeChangedTypes());
if (cleanReferences != null)
{
yield return FieldConverters.ForValues(cleanReferences);
yield return FieldConverters.ForNestedName2Name(cleanReferences);
}
yield return FieldConverters.ResolveInvariant(context.App.LanguagesConfig);
yield return FieldConverters.ResolveLanguages(context.App.LanguagesConfig);

6
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs

@ -63,6 +63,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
private void ResolveAssetsUrls(ISchemaEntity schema, IGrouping<Guid, ContentEntity> contents, ILookup<Guid, IEnrichedAssetEntity> assets)
{
var temp = new HashSet<Guid>();
foreach (var field in schema.SchemaDef.ResolvingAssets())
{
foreach (var content in contents)
@ -79,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
foreach (var (partitionKey, partitionValue) in fieldData)
{
var referencedImage =
field.GetReferencedIds(partitionValue, Ids.ContentOnly)
field.GetReferencedIds(partitionValue)
.Select(x => assets[x])
.SelectMany(x => x)
.FirstOrDefault(x => x.Type == AssetType.Image);
@ -117,7 +119,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
{
foreach (var content in contents)
{
ids.AddRange(content.DataDraft.GetReferencedIds(schema.SchemaDef.ResolvingAssets(), Ids.ContentOnly));
content.DataDraft.AddReferencedIds(schema.SchemaDef.ResolvingAssets(), ids);
}
}

4
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs

@ -81,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
foreach (var (partition, partitionValue) in fieldData)
{
var referencedContents =
field.GetReferencedIds(partitionValue, Ids.ContentOnly)
field.GetReferencedIds(partitionValue)
.Select(x => references[x])
.SelectMany(x => x)
.ToList();
@ -143,7 +143,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
{
foreach (var content in contents)
{
ids.AddRange(content.DataDraft.GetReferencedIds(schema.SchemaDef.ResolvingReferences(), Ids.ContentOnly));
content.DataDraft.AddReferencedIds(schema.SchemaDef.ResolvingReferences(), ids);
}
}

5
backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs

@ -25,6 +25,11 @@ namespace Squidex.Infrastructure.Json.Objects
{
}
public JsonArray(JsonArray source)
: base(source)
{
}
internal JsonArray(params object?[] values)
: base(ToList(values))
{

7
backend/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs

@ -68,6 +68,8 @@ namespace Squidex.Infrastructure.Json.Objects
return Create(i);
case long l:
return Create(l);
case Guid g:
return Create(g);
case Instant i:
return Create(i);
}
@ -75,6 +77,11 @@ namespace Squidex.Infrastructure.Json.Objects
throw new ArgumentException("Invalid json type");
}
public static IJsonValue Create(Guid value)
{
return Create(value.ToString());
}
public static IJsonValue Create(bool value)
{
return value ? True : False;

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

@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var input =
new ContentFieldData()
.AddValue("iv", JsonValue.Object());
.AddJsonValue(JsonValue.Object());
var actual = FieldConverters.ForValues((f, i) => Value.Unset)(input, field);
@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var input =
new ContentFieldData()
.AddValue("iv", JsonValue.Object());
.AddJsonValue(JsonValue.Object());
var actual = FieldConverters.ForValues(ValueConverters.EncodeJson(TestUtils.DefaultSerializer))(input, field);
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var input =
new ContentFieldData()
.AddValue("iv",
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("field1", 100)
@ -84,7 +84,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var expected =
new ContentFieldData()
.AddValue("iv",
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("1", 100)));
@ -102,7 +102,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var input =
new ContentFieldData()
.AddValue("iv",
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("field1", 100)
@ -113,7 +113,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var expected =
new ContentFieldData()
.AddValue("iv",
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("field1", 100)));
@ -479,11 +479,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var source =
new ContentFieldData()
.AddValue("iv", JsonValue.Array("1", "2"));
.AddJsonValue(JsonValue.Array("1", "2"));
var expected =
new ContentFieldData()
.AddValue("iv", JsonValue.Array("url/to/1", "url/to/2"));
.AddJsonValue(JsonValue.Array("url/to/1", "url/to/2"));
var result = FieldConverters.ResolveAssetUrls(new HashSet<string>(new[] { "assets" }), assetUrlGenerator)(source, field);
@ -499,13 +499,13 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var source =
new ContentFieldData()
.AddValue("iv", JsonValue.Array(
.AddJsonValue(JsonValue.Array(
JsonValue.Object()
.Add("assets", JsonValue.Array("1", "2"))));
var expected =
new ContentFieldData()
.AddValue("iv", JsonValue.Array(
.AddJsonValue(JsonValue.Array(
JsonValue.Object()
.Add("assets", JsonValue.Array("url/to/1", "url/to/2"))));
@ -521,11 +521,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var source =
new ContentFieldData()
.AddValue("iv", JsonValue.Array("1", "2"));
.AddJsonValue(JsonValue.Array("1", "2"));
var expected =
new ContentFieldData()
.AddValue("iv", JsonValue.Array("url/to/1", "url/to/2"));
.AddJsonValue(JsonValue.Array("url/to/1", "url/to/2"));
var result = FieldConverters.ResolveAssetUrls(new HashSet<string>(new[] { "*" }), assetUrlGenerator)(source, field);
@ -541,13 +541,13 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var source =
new ContentFieldData()
.AddValue("iv", JsonValue.Array(
.AddJsonValue(JsonValue.Array(
JsonValue.Object()
.Add("assets", JsonValue.Array("1", "2"))));
var expected =
new ContentFieldData()
.AddValue("iv", JsonValue.Array(
.AddJsonValue(JsonValue.Array(
JsonValue.Object()
.Add("assets", JsonValue.Array("url/to/1", "url/to/2"))));
@ -563,11 +563,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var source =
new ContentFieldData()
.AddValue("iv", JsonValue.Array("1", "2"));
.AddJsonValue(JsonValue.Array("1", "2"));
var expected =
new ContentFieldData()
.AddValue("iv", JsonValue.Array("1", "2"));
.AddJsonValue(JsonValue.Array("1", "2"));
var result = FieldConverters.ResolveAssetUrls(new HashSet<string>(new[] { "other" }), assetUrlGenerator)(source, field);
@ -581,11 +581,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
var source =
new ContentFieldData()
.AddValue("iv", JsonValue.Array("1", "2"));
.AddJsonValue(JsonValue.Array("1", "2"));
var expected =
new ContentFieldData()
.AddValue("iv", JsonValue.Array("1", "2"));
.AddJsonValue(JsonValue.Array("1", "2"));
var result = FieldConverters.ResolveAssetUrls(null, assetUrlGenerator)(source, field);

12
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EnrichContent/ContentEnrichmentTests.cs → backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs

@ -8,7 +8,7 @@
using NodaTime;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.EnrichContent;
using Squidex.Domain.Apps.Core.DefaultValues;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
@ -16,15 +16,15 @@ using Xunit;
#pragma warning disable xUnit2004 // Do not use equality check to test for boolean conditions
namespace Squidex.Domain.Apps.Core.Operations.EnrichContent
namespace Squidex.Domain.Apps.Core.Operations.DefaultValues
{
public class ContentEnrichmentTests
public class DefaultValuesTests
{
private readonly Instant now = Instant.FromUtc(2017, 10, 12, 16, 30, 10);
private readonly LanguagesConfig languagesConfig = LanguagesConfig.English.Set(Language.DE);
private readonly Schema schema;
public ContentEnrichmentTests()
public DefaultValuesTests()
{
schema =
new Schema("my-schema")
@ -50,7 +50,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent
new ContentFieldData()
.AddValue("iv", 456));
data.Enrich(schema, languagesConfig.ToResolver());
data.GenerateDefaultValues(schema, languagesConfig.ToResolver());
Assert.Equal(456, ((JsonScalar<double>)data["my-number"]!["iv"]).Value);
@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent
new ContentFieldData()
.AddValue("iv", 456));
data.Enrich(schema, languagesConfig.ToResolver());
data.GenerateDefaultValues(schema, languagesConfig.ToResolver());
Assert.Equal("en-string", data["my-string"]!["de"].ToString());
Assert.Equal("en-string", data["my-string"]!["en"].ToString());

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

@ -6,6 +6,7 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
@ -21,39 +22,16 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
{
public class ReferenceExtractionTests
{
private readonly Guid schemaId = Guid.NewGuid();
private readonly Schema schema;
public ReferenceExtractionTests()
{
schema =
new Schema("my-schema")
.AddNumber(1, "field1", Partitioning.Language)
.AddNumber(2, "field2", Partitioning.Invariant)
.AddNumber(3, "field3", Partitioning.Invariant)
.AddAssets(5, "assets1", Partitioning.Invariant)
.AddAssets(6, "assets2", Partitioning.Invariant)
.AddArray(7, "array", Partitioning.Invariant, a => a
.AddAssets(71, "assets71"))
.AddJson(4, "json", Partitioning.Language)
.UpdateField(3, f => f.Hide());
}
[Fact]
public void Should_get_ids_from_id_data()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var input =
new IdContentData()
.AddField(5,
new ContentFieldData()
.AddValue("iv", JsonValue.Array(id1.ToString(), id2.ToString())));
var ids = input.GetReferencedIds(schema).ToArray();
Assert.Equal(new[] { id1, id2 }, ids);
.AddReferences(1, "references", Partitioning.Invariant)
.AddAssets(2, "assets", Partitioning.Invariant)
.AddArray(3, "array", Partitioning.Invariant, a => a
.AddAssets(31, "nested"));
}
[Fact]
@ -64,11 +42,13 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var input =
new NamedContentData()
.AddField("assets1",
.AddField("assets",
new ContentFieldData()
.AddValue("iv", JsonValue.Array(id1.ToString(), id2.ToString())));
.AddJsonValue(JsonValue.Array(id1.ToString(), id2.ToString())));
var ids = input.GetReferencedIds(schema).ToArray();
var ids = new HashSet<Guid>();
input.AddReferencedIds(schema, ids);
Assert.Equal(new[] { id1, id2 }, ids);
}
@ -79,53 +59,44 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var input =
new IdContentData()
.AddField(5,
var source =
new NamedContentData()
.AddField("references",
new ContentFieldData()
.AddValue("iv", JsonValue.Array(id1.ToString(), id2.ToString())));
var converter = FieldConverters.ForValues(ValueReferencesConverter.CleanReferences(new[] { id2 }));
var actual = input.ConvertId2Id(schema, converter);
var cleanedValue = (JsonArray)actual[5]!["iv"];
Assert.Equal(1, cleanedValue.Count);
Assert.Equal(id1.ToString(), cleanedValue[0].ToString());
}
[Fact]
public void Should_return_ids_from_assets_field()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant);
var result = sut.GetReferencedIds(CreateValue(id1, id2)).ToArray();
Assert.Equal(new[] { id1, id2 }, result);
}
[Fact]
public void Should_return_empty_list_from_assets_field_for_referenced_ids_when_null()
{
var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant);
.AddJsonValue(JsonValue.Array(id1, id2)))
.AddField("assets",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1)))
.AddField("array",
new ContentFieldData()
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("nested", JsonValue.Array(id1, id2)))));
var result = sut.GetReferencedIds(null).ToArray();
var expected =
new NamedContentData()
.AddField("references",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id2)))
.AddField("assets",
new ContentFieldData()
.AddJsonValue(JsonValue.Array()))
.AddField("array",
new ContentFieldData()
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("nested", JsonValue.Array(id2)))));
Assert.Empty(result);
}
var cleaner = ValueReferencesConverter.CleanReferences(new HashSet<Guid> { id2 });
[Fact]
public void Should_return_empty_list_from_assets_field_for_referenced_ids_when_other_type()
{
var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant);
var converter = FieldConverters.ForValues(cleaner);
var converterNested = FieldConverters.ForNestedName2Name(cleaner);
var result = sut.GetReferencedIds(JsonValue.Create("invalid")).ToArray();
var actual = source.ConvertName2Name(schema, converter, converterNested);
Assert.Empty(result);
Assert.Equal(expected, actual);
}
[Fact]
@ -138,171 +109,127 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
Assert.Empty(result);
}
[Fact]
public void Should_return_null_from_assets_field_when_removing_references_from_null_array()
{
var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant);
var result = sut.CleanReferences(JsonValue.Null, null);
Assert.Equal(JsonValue.Null, result);
}
[Fact]
public void Should_remove_deleted_references_from_assets_field()
[Theory]
[MemberData(nameof(ReferencingNestedFields))]
public void Should_return_ids_from_nested_field(NestedField field)
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant);
var result = sut.CleanReferences(CreateValue(id1, id2), HashSet.Of(id2));
Assert.Equal(CreateValue(id1), result);
}
[Fact]
public void Should_return_same_token_from_assets_field_when_removing_references_and_nothing_to_remove()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant);
var token = CreateValue(id1, id2);
var result = sut.CleanReferences(token, HashSet.Of(Guid.NewGuid()));
Assert.Same(token, result);
}
[Fact]
public void Should_return_ids_from_nested_references_field()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var sut =
Fields.Array(1, "my-array", Partitioning.Invariant,
Fields.References(1, "my-refs",
new ReferencesFieldProperties { SchemaId = schemaId }));
var arrayField = Fields.Array(1, "my-array", Partitioning.Invariant, field);
var value =
JsonValue.Array(
JsonValue.Object()
.Add("my-refs", CreateValue(id1, id2)));
.Add(field.Name, CreateValue(id1, id2)));
var result = sut.GetReferencedIds(value).ToArray();
var result = arrayField.GetReferencedIds(value).ToArray();
Assert.Equal(new[] { id1, id2, schemaId }, result);
Assert.Equal(new[] { id1, id2 }, result);
}
[Fact]
public void Should_return_ids_from_references_field()
[Theory]
[MemberData(nameof(ReferencingFields))]
public void Should_return_empty_list_from_field_when_value_item_is_invalid(IField field)
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var result = field.GetReferencedIds(JsonValue.Array("invalid")).ToArray();
var sut = Fields.References(1, "my-refs", Partitioning.Invariant,
new ReferencesFieldProperties { SchemaId = schemaId });
Assert.Empty(result);
}
var result = sut.GetReferencedIds(CreateValue(id1, id2)).ToArray();
[Theory]
[MemberData(nameof(ReferencingFields))]
public void Should_return_empty_list_from_field_when_value_is_invalid(IField field)
{
var result = field.GetReferencedIds(JsonValue.Create("invalid")).ToArray();
Assert.Equal(new[] { id1, id2, schemaId }, result);
Assert.Empty(result);
}
[Fact]
public void Should_return_ids_from_references_field_without_schema_id()
[Theory]
[MemberData(nameof(ReferencingFields))]
public void Should_return_empty_list_from_field_when_value_is_empty(IField field)
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var result = field.GetReferencedIds(JsonValue.Array()).ToArray();
var sut = Fields.References(1, "my-refs", Partitioning.Invariant,
new ReferencesFieldProperties { SchemaId = schemaId });
Assert.Empty(result);
}
var result = sut.GetReferencedIds(CreateValue(id1, id2), Ids.ContentOnly).ToArray();
[Theory]
[MemberData(nameof(ReferencingFields))]
public void Should_return_empty_list_from_field_when_value_is_json_null(IField field)
{
var result = field.GetReferencedIds(null).ToArray();
Assert.Equal(new[] { id1, id2 }, result);
Assert.Empty(result);
}
[Fact]
public void Should_return_list_from_references_field_with_schema_id_list_for_referenced_ids_when_null()
[Theory]
[MemberData(nameof(ReferencingFields))]
public void Should_return_empty_list_from_field_when_value_is_null(IField field)
{
var sut = Fields.References(1, "my-refs", Partitioning.Invariant,
new ReferencesFieldProperties { SchemaId = schemaId });
var result = sut.GetReferencedIds(JsonValue.Null).ToArray();
var result = field.GetReferencedIds(null).ToArray();
Assert.Equal(new[] { schemaId }, result);
Assert.Empty(result);
}
[Fact]
public void Should_return_list_from_references_field_with_schema_id_for_referenced_ids_when_other_type()
[Theory]
[MemberData(nameof(ReferencingFields))]
public void Should_return_ids_from_field(IField field)
{
var sut = Fields.References(1, "my-refs", Partitioning.Invariant,
new ReferencesFieldProperties { SchemaId = schemaId });
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var result = sut.GetReferencedIds(JsonValue.Create("invalid")).ToArray();
var value = CreateValue(id1, id2);
Assert.Equal(new[] { schemaId }, result);
var result = field.GetReferencedIds(value);
Assert.Equal(new HashSet<Guid> { id1, id2 }, result);
}
[Fact]
public void Should_return_null_from_references_field_when_removing_references_from_null_array()
[Theory]
[MemberData(nameof(ReferencingFields))]
public void Should_return_same_value_from_field_when_value_is_json_null(IField field)
{
var sut = Fields.References(1, "my-refs", Partitioning.Invariant);
var result = sut.CleanReferences(JsonValue.Null, null);
var result = ValueReferencesConverter.CleanReferences(RandomIds())(JsonValue.Null, field);
Assert.Equal(JsonValue.Null, result);
}
[Fact]
public void Should_remove_deleted_references_from_references_field()
[Theory]
[MemberData(nameof(ReferencingFields))]
public void Should_remove_deleted_ids_from_field(IField field)
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var sut = Fields.References(1, "my-refs", Partitioning.Invariant,
new ReferencesFieldProperties { SchemaId = schemaId });
var value = CreateValue(id1, id2);
var result = sut.CleanReferences(CreateValue(id1, id2), HashSet.Of(id2));
var result = ValueReferencesConverter.CleanReferences(HashSet.Of(id1))(value, field);
Assert.Equal(CreateValue(id1), result);
}
[Fact]
public void Should_remove_all_references_from_references_field_when_schema_is_removed()
public static IEnumerable<object[]> ReferencingNestedFields()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var sut = Fields.References(1, "my-refs", Partitioning.Invariant,
new ReferencesFieldProperties { SchemaId = schemaId });
var result = sut.CleanReferences(CreateValue(id1, id2), HashSet.Of(schemaId));
Assert.Equal(CreateValue(), result);
yield return new object[] { Fields.References(1, "my-refs") };
yield return new object[] { Fields.Assets(1, "my-assets") };
}
[Fact]
public void Should_return_same_token_from_references_field_when_removing_references_and_nothing_to_remove()
public static IEnumerable<object[]> ReferencingFields()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var sut = Fields.References(1, "my-refs", Partitioning.Invariant);
var value = CreateValue(id1, id2);
var result = sut.CleanReferences(value, HashSet.Of(Guid.NewGuid()));
yield return new object[] { Fields.References(1, "my-refs", Partitioning.Invariant) };
yield return new object[] { Fields.Assets(1, "my-assets", Partitioning.Invariant) };
}
Assert.Same(value, result);
private static HashSet<Guid> RandomIds()
{
return HashSet.Of(Guid.NewGuid());
}
private static IJsonValue CreateValue(params Guid[] ids)
private static IJsonValue CreateValue(params object[] ids)
{
return ids == null ? JsonValue.Null : JsonValue.Array(ids.Select(x => (object)x.ToString()).ToArray());
return JsonValue.Array(ids);
}
}
}

11
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs

@ -282,7 +282,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
new NamedContentData()
.AddField("city",
new ContentFieldData()
.AddValue("iv", JsonValue.Array()))
.AddJsonValue(JsonValue.Array()))
};
var result = sut.Format(script, @event);
@ -301,7 +301,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
new NamedContentData()
.AddField("city",
new ContentFieldData()
.AddValue("iv", JsonValue.Object().Add("name", "Berlin")))
.AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
};
var result = sut.Format(script, @event);
@ -339,8 +339,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
new NamedContentData()
.AddField("city",
new ContentFieldData()
.AddValue("iv", JsonValue.Array(
"Berlin")))
.AddJsonValue(JsonValue.Array("Berlin")))
};
var result = sut.Format(script, @event);
@ -359,7 +358,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
new NamedContentData()
.AddField("city",
new ContentFieldData()
.AddValue("iv", JsonValue.Object().Add("name", "Berlin")))
.AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
};
var result = sut.Format(script, @event);
@ -378,7 +377,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
new NamedContentData()
.AddField("city",
new ContentFieldData()
.AddValue("iv", JsonValue.Object().Add("name", "Berlin")))
.AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
};
var result = sut.Format(script, @event);

12
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs

@ -155,13 +155,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
new NamedContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", JsonValue.Array(1.0, 2.0)));
.AddJsonValue(JsonValue.Array(1.0, 2.0)));
var expected =
new NamedContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", JsonValue.Array(1.0, 4.0, 5.0)));
.AddJsonValue(JsonValue.Array(1.0, 4.0, 5.0)));
var result = ExecuteScript(original, @"data.number.iv = [data.number.iv[0], data.number.iv[1] + 2, 5]");
@ -175,13 +175,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
new NamedContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", JsonValue.Object().Add("lat", 1.0)));
.AddJsonValue(JsonValue.Object().Add("lat", 1.0)));
var expected =
new NamedContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", JsonValue.Object().Add("lat", 1.0).Add("lon", 4.0)));
.AddJsonValue(JsonValue.Object().Add("lat", 1.0).Add("lon", 4.0)));
var result = ExecuteScript(original, @"data.number.iv = { lat: data.number.iv.lat, lon: data.number.iv.lat + 3 }");
@ -265,7 +265,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
new NamedContentData()
.AddField("obj",
new ContentFieldData()
.AddValue("iv", JsonValue.Object().Add("readonly", 1)));
.AddJsonValue(JsonValue.Object().Add("readonly", 1)));
Assert.Throws<JavaScriptException>(() => ExecuteScript(original, "data.obj.iv.invalid = 1"));
Assert.Throws<JavaScriptException>(() => ExecuteScript(original, "data.obj.iv.readonly = 2"));
@ -278,7 +278,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
new NamedContentData()
.AddField("obj",
new ContentFieldData()
.AddValue("iv", JsonValue.Array()));
.AddJsonValue(JsonValue.Array()));
ExecuteScript(original, "data.obj.iv[0] = 1");
}

15
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Tags/TagNormalizerTests.cs

@ -12,6 +12,7 @@ using FakeItEasy;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Tags;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Infrastructure.Json.Objects;
using Xunit;
@ -44,8 +45,8 @@ namespace Squidex.Domain.Apps.Core.Operations.Tags
var oldData = GenerateData("o_raw");
A.CallTo(() => tagService.NormalizeTagsAsync(appId, TagGroups.Schemas(schemaId),
A<HashSet<string>>.That.IsSameSequenceAs("n_raw2_1", "n_raw2_2", "n_raw4"),
A<HashSet<string>>.That.IsSameSequenceAs("o_raw2_1", "o_raw2_2", "o_raw4")))
A<HashSet<string>>.That.Is("n_raw2_1", "n_raw2_2", "n_raw4"),
A<HashSet<string>>.That.Is("o_raw2_1", "o_raw2_2", "o_raw4")))
.Returns(new Dictionary<string, string>
{
["n_raw2_2"] = "id2_2",
@ -65,7 +66,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Tags
var newData = GenerateData("name");
A.CallTo(() => tagService.NormalizeTagsAsync(appId, TagGroups.Schemas(schemaId),
A<HashSet<string>>.That.IsSameSequenceAs("name2_1", "name2_2", "name4"),
A<HashSet<string>>.That.Is("name2_1", "name2_2", "name4"),
A<HashSet<string>>.That.IsEmpty()))
.Returns(new Dictionary<string, string>
{
@ -86,7 +87,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Tags
var newData = GenerateData("id");
A.CallTo(() => tagService.NormalizeTagsAsync(appId, TagGroups.Schemas(schemaId),
A<HashSet<string>>.That.IsSameSequenceAs("id2_1", "id2_2", "id4"),
A<HashSet<string>>.That.Is("id2_1", "id2_2", "id4"),
A<HashSet<string>>.That.IsEmpty()))
.Returns(new Dictionary<string, string>
{
@ -114,16 +115,16 @@ namespace Squidex.Domain.Apps.Core.Operations.Tags
return new NamedContentData()
.AddField("tags1",
new ContentFieldData()
.AddValue("iv", JsonValue.Array($"{prefix}1")))
.AddJsonValue(JsonValue.Array($"{prefix}1")))
.AddField("tags2",
new ContentFieldData()
.AddValue("iv", JsonValue.Array($"{prefix}2_1", $"{prefix}2_2")))
.AddJsonValue(JsonValue.Array($"{prefix}2_1", $"{prefix}2_2")))
.AddField("string",
new ContentFieldData()
.AddValue("iv", $"{prefix}stringValue"))
.AddField("array",
new ContentFieldData()
.AddValue("iv",
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("nestedTags1", JsonValue.Array($"{prefix}3"))

4
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs

@ -348,7 +348,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
new NamedContentData()
.AddField("my-field",
new ContentFieldData()
.AddValue("iv",
.AddJsonValue(
JsonValue.Array(
JsonValue.Object(),
JsonValue.Object().Add("my-nested", 1),
@ -387,7 +387,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
new NamedContentData()
.AddField("my-field",
new ContentFieldData()
.AddValue("iv",
.AddJsonValue(
JsonValue.Array(
JsonValue.Object())));

2
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/UIFieldTests.cs

@ -100,7 +100,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
var data =
new NamedContentData()
.AddField("my-array", new ContentFieldData()
.AddValue("iv",
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("my-ui", null))));

31
backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/AExtensions.cs

@ -0,0 +1,31 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using FakeItEasy;
namespace Squidex.Domain.Apps.Core.TestHelpers
{
public static class AExtensions
{
public static T[] Is<T>(this INegatableArgumentConstraintManager<T[]> that, params T[]? values)
{
return values == null ? that.IsNull() : that.IsSameSequenceAs(values);
}
public static HashSet<T> Is<T>(this INegatableArgumentConstraintManager<HashSet<T>> that, IEnumerable<T>? values)
{
return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Count());
}
public static HashSet<T> Is<T>(this INegatableArgumentConstraintManager<HashSet<T>> that, params T[]? values)
{
return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Length);
}
}
}

4
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetEnricherTests.cs

@ -59,7 +59,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
AppId = appId
};
A.CallTo(() => tagService.DenormalizeTagsAsync(appId.Id, TagGroups.Assets, A<HashSet<string>>.That.Has("id1", "id2")))
A.CallTo(() => tagService.DenormalizeTagsAsync(appId.Id, TagGroups.Assets, A<HashSet<string>>.That.Is("id1", "id2")))
.Returns(new Dictionary<string, string>
{
["id1"] = "name1",
@ -137,7 +137,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
AppId = appId
};
A.CallTo(() => tagService.DenormalizeTagsAsync(appId.Id, TagGroups.Assets, A<HashSet<string>>.That.Has("id1", "id2", "id3")))
A.CallTo(() => tagService.DenormalizeTagsAsync(appId.Id, TagGroups.Assets, A<HashSet<string>>.That.Is("id1", "id2", "id3")))
.Returns(new Dictionary<string, string>
{
["id1"] = "name1",

2
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryServiceTests.cs

@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
var ids = HashSet.Of(found1.Id, found2.Id);
A.CallTo(() => assetRepository.QueryAsync(appId.Id, A<HashSet<Guid>>.That.IsSameSequenceAs(ids)))
A.CallTo(() => assetRepository.QueryAsync(appId.Id, A<HashSet<Guid>>.That.Is(ids)))
.Returns(ResultList.CreateFrom(8, found1, found2));
A.CallTo(() => assetEnricher.EnrichAsync(A<IEnumerable<IAssetEntity>>.That.IsSameSequenceAs(found1, found2), requestContext))

146
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ConvertDataTests.cs

@ -0,0 +1,146 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Contents.Queries.Steps;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Contents.Queries
{
public class ConvertDataTests
{
private readonly ISchemaEntity schema;
private readonly IAssetUrlGenerator assetUrlGenerator = A.Fake<IAssetUrlGenerator>();
private readonly IAssetRepository assetRepository = A.Fake<IAssetRepository>();
private readonly IContentRepository contentRepository = A.Fake<IContentRepository>();
private readonly Context requestContext;
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app");
private readonly NamedId<Guid> schemaId = NamedId.Of(Guid.NewGuid(), "my-schema");
private readonly ProvideSchema schemaProvider;
private readonly ConvertData sut;
public ConvertDataTests()
{
requestContext = new Context(Mocks.ApiUser(), Mocks.App(appId));
var schemaDef =
new Schema("my-schema")
.AddReferences(1, "references", Partitioning.Invariant)
.AddAssets(2, "assets", Partitioning.Invariant)
.AddArray(3, "array", Partitioning.Invariant, a => a
.AddAssets(31, "nested"));
schema = Mocks.Schema(appId, schemaId, schemaDef);
schemaProvider = x => Task.FromResult(schema);
sut = new ConvertData(assetUrlGenerator, assetRepository, contentRepository);
}
[Fact]
public async Task Should_convert_data_only()
{
var source = PublishedContent();
await sut.EnrichAsync(requestContext, Enumerable.Repeat(source, 1), schemaProvider);
Assert.NotNull(source.Data);
Assert.Null(source.DataDraft);
}
[Fact]
public async Task Should_convert_data_and_data_draft_when_frontend_user()
{
var source = PublishedContent();
var ctx = new Context(Mocks.FrontendUser(), Mocks.App(appId));
await sut.EnrichAsync(ctx, Enumerable.Repeat(source, 1), schemaProvider);
Assert.NotNull(source.Data);
Assert.NotNull(source.DataDraft);
}
[Fact]
public async Task Should_cleanup_references()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var source =
new NamedContentData()
.AddField("references",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1, id2)))
.AddField("assets",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1)))
.AddField("array",
new ContentFieldData()
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("nested", JsonValue.Array(id1, id2)))));
var expected =
new NamedContentData()
.AddField("references",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id2)))
.AddField("assets",
new ContentFieldData()
.AddJsonValue(JsonValue.Array()))
.AddField("array",
new ContentFieldData()
.AddJsonValue(
JsonValue.Array(
JsonValue.Object()
.Add("nested", JsonValue.Array(id2)))));
var content = PublishedContent();
content.Data = source;
content.DataDraft = source;
A.CallTo(() => assetRepository.QueryIdsAsync(appId.Id, A<HashSet<Guid>>.That.Is(id1, id2)))
.Returns(new List<Guid> { id2 });
A.CallTo(() => contentRepository.QueryIdsAsync(appId.Id, A<HashSet<Guid>>.That.Is(id1, id2)))
.Returns(new List<(Guid, Guid)> { (id2, id2) });
var ctx = new Context(Mocks.FrontendUser(), Mocks.App(appId));
await sut.EnrichAsync(ctx, Enumerable.Repeat(content, 1), schemaProvider);
Assert.Equal(expected, content.Data);
Assert.Equal(expected, content.DataDraft);
}
private ContentEntity PublishedContent()
{
return new ContentEntity
{
Status = Status.Published,
Data = new NamedContentData(),
DataDraft = new NamedContentData(),
SchemaId = schemaId
};
}
}
}

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs

@ -137,8 +137,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
new NamedContentData()
.AddField("asset1",
new ContentFieldData()
.AddValue("iv",
$"url/to/{image1.Id}"))
.AddValue("iv", $"url/to/{image1.Id}"))
.AddField("asset2",
new ContentFieldData()),
source[0].ReferenceData);
@ -149,8 +148,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
new ContentFieldData())
.AddField("asset2",
new ContentFieldData()
.AddValue("en",
$"url/to/{image2.Id}")),
.AddValue("en", $"url/to/{image2.Id}")),
source[1].ReferenceData);
}

6
backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AExtensions.cs

@ -24,12 +24,12 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
return values == null ? that.IsNull() : that.IsSameSequenceAs(values);
}
public static IEnumerable<T> Has<T>(this INegatableArgumentConstraintManager<IEnumerable<T>> that, params T[]? values)
public static HashSet<T> Is<T>(this INegatableArgumentConstraintManager<HashSet<T>> that, IEnumerable<T>? values)
{
return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Length);
return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Count());
}
public static HashSet<T> Has<T>(this INegatableArgumentConstraintManager<HashSet<T>> that, params T[]? values)
public static HashSet<T> Is<T>(this INegatableArgumentConstraintManager<HashSet<T>> that, params T[]? values)
{
return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Length);
}

4
backend/tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs

@ -66,7 +66,7 @@ namespace TestSuite.ApiTests
var content_1 = await contents.CreateAsync(data);
Assert.Equal(data.String, content_1.DataDraft.String);
Assert.Equal(data.String, content_1.Data.String);
// STEP 3: Delete a field from schema.
@ -77,7 +77,7 @@ namespace TestSuite.ApiTests
var content_2 = await contents.ChangeStatusAsync(content_1.Id, "Published");
// Should not return deleted field.
Assert.Null(content_2.DataDraft.String);
Assert.Null(content_2.Data.String);
}
}
}

Loading…
Cancel
Save