diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/IFieldVisitor.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/IFieldVisitor.cs index 710560f6d..0b2340cab 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/IFieldVisitor.cs +++ b/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 { - T Visit(IArrayField field); + T Visit(IArrayField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } - T Visit(IField field); + T Visit(IField field) + { + return default; + } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs index ae814cfc1..ebfd7ec2e 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs +++ b/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 KeyNameResolver = f => f.Name; private static readonly Func 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); diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnrichmentExtensions.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueExtensions.cs similarity index 63% rename from backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnrichmentExtensions.cs rename to backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueExtensions.cs index 1a9fe25a1..b8006651f 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnrichmentExtensions.cs +++ b/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); } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/DefaultValueFactory.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueFactory.cs similarity index 98% rename from backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/DefaultValueFactory.cs rename to backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueFactory.cs index 2f131b904..9ce3334d4 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/DefaultValueFactory.cs +++ b/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 { diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnricher.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueGenerator.cs similarity index 93% rename from backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnricher.cs rename to backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueGenerator.cs index 32a44d8a1..995cf6d8e 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnricher.cs +++ b/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); diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs index a1f36e156..8993f2613 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs @@ -17,79 +17,81 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds { public static class ContentReferencesExtensions { - public static IEnumerable GetReferencedIds(this IdContentData source, Schema schema, Ids strategy = Ids.All) + public static HashSet 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()); - foreach (var id in ids) - { - yield return id; - } - } + AddReferencedIds(source, schema.Fields, extractor); + + return extractor.Result; } - public static IEnumerable GetReferencedIds(this IdContentData source, IField field, Ids strategy = Ids.All) + public static void AddReferencedIds(this NamedContentData source, Schema schema, HashSet 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 GetReferencedIds(this NamedContentData source, Schema schema, Ids strategy = Ids.All) + public static void AddReferencedIds(this NamedContentData source, IEnumerable fields, HashSet 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 GetReferencedIds(this NamedContentData source, IEnumerable fields, Ids strategy = Ids.All) + public static void AddReferencedIds(this NamedContentData source, IField field, HashSet result) { - Guard.NotNull(fields); + Guard.NotNull(field); + + var extractor = new ReferencesExtractor(result); + AddReferencedIds(source, field, extractor); + } + + private static void AddReferencedIds(NamedContentData source, IEnumerable fields, ReferencesExtractor extractor) + { foreach (var field in fields) { - var ids = source.GetReferencedIds(field, strategy); - - foreach (var id in ids) - { - yield return id; - } + AddReferencedIds(source, field, extractor); } } - public static IEnumerable 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 GetReferencedIds(this IField field, IJsonValue? value) + { + var result = new HashSet(); + + 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 = ", ") { Guard.NotNull(schema); diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs index c9bef6381..376bcbde8 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs +++ b/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 { - private readonly IJsonValue value; - private readonly ICollection? oldReferences; + private readonly HashSet validIds; + private IJsonValue value; - private ReferencesCleaner(IJsonValue value, ICollection? oldReferences) + public ReferencesCleaner(HashSet validIds) { - this.value = value; + Guard.NotNull(validIds); - this.oldReferences = oldReferences; + this.validIds = validIds; } - public static IJsonValue CleanReferences(IField field, IJsonValue value, ICollection? oldReferences) + public void SetValue(IJsonValue newValue) { - return field.Accept(new ReferencesCleaner(value, oldReferences)); + value = newValue; } public IJsonValue Visit(IField field) @@ -36,31 +37,9 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds public IJsonValue Visit(IField 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 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); + } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs index bc13e06d8..c4acb33ad 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs +++ b/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 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? 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 ids) - { - var result = JsonValue.Array(); - - foreach (var id in ids) - { - result.Add(JsonValue.Create(id.ToString())); - } - - return result; - } - - public static HashSet ToGuidSet(this IJsonValue? value) + public static void AddIds(this IJsonValue? value, HashSet result) { if (value is JsonArray array) { - var result = new HashSet(); - 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(); } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs index dc11ff502..3a2f0fc81 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs +++ b/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> + internal sealed class ReferencesExtractor : IFieldVisitor { - private readonly IJsonValue? value; - private readonly Ids strategy; + private readonly HashSet result; + private IJsonValue? value; - private ReferencesExtractor(IJsonValue? value, Ids strategy) + public HashSet Result { - this.value = value; - - this.strategy = strategy; + get { return result; } } - public static IEnumerable ExtractReferences(IField field, IJsonValue? value, Ids strategy) + public ReferencesExtractor(HashSet result) { - return field.Accept(new ReferencesExtractor(value, strategy)); + Guard.NotNull(result); + + this.result = result; } - public IEnumerable Visit(IArrayField field) + public void SetValue(IJsonValue? newValue) { - var result = new List(); + value = newValue; + } + public None Visit(IArrayField field) + { if (value is JsonArray array) { foreach (var item in array.OfType()) @@ -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))); + SetValue(nestedValue); + + nestedField.Accept(this); } } } } - return result; - } - - public IEnumerable Visit(IField field) - { - var ids = value.ToGuidSet(); - - return ids; - } - - public IEnumerable Visit(IField field) - { - var ids = value.ToGuidSet(); - - if (strategy == Ids.All && field.Properties.SchemaIds != null) - { - foreach (var schemaId in field.Properties.SchemaIds) - { - ids.Add(schemaId); - } - } - - return ids; + return None.Value; } - public IEnumerable Visit(IField field) + public None Visit(IField field) { - return Enumerable.Empty(); - } + value.AddIds(result); - public IEnumerable Visit(IField field) - { - return Enumerable.Empty(); + return None.Value; } - public IEnumerable Visit(IField field) + public None Visit(IField field) { - return Enumerable.Empty(); - } + value.AddIds(result); - public IEnumerable Visit(IField field) - { - return Enumerable.Empty(); - } - - public IEnumerable Visit(IField field) - { - return Enumerable.Empty(); - } - - public IEnumerable Visit(IField field) - { - return Enumerable.Empty(); - } - - public IEnumerable Visit(IField field) - { - return Enumerable.Empty(); - } - - public IEnumerable Visit(IField field) - { - return Enumerable.Empty(); + return None.Value; } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs index 384a90ccb..651c152ec 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs +++ b/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 deletedReferencedIds) + public static ValueConverter CleanReferences(HashSet? validIds = null) { - var ids = new HashSet(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); }; } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/Ids.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonError.cs similarity index 64% rename from backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/Ids.cs rename to backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonError.cs index 037ee9747..e4e5a300f 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/Ids.cs +++ b/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; + } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs index 81d8404ee..efdfa97b9 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs @@ -89,6 +89,18 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets } } + public async Task> QueryIdsAsync(Guid appId, HashSet ids) + { + using (Profiler.TraceMethod("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> QueryAsync(Guid appId, HashSet ids) { using (Profiler.TraceMethod("QueryAsyncByIds")) diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs index 7d32fd556..e35b8a027 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs +++ b/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 { private NamedContentData? data; @@ -42,12 +43,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents [BsonRequired] [BsonElement("rf")] [BsonRepresentation(BsonType.String)] - public List? ReferencedIds { get; set; } - - [BsonRequired] - [BsonElement("rd")] - [BsonRepresentation(BsonType.String)] - public List ReferencedIdsDeleted { get; set; } = new List(); + public HashSet? 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); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs index 1c84d4426..b829da413 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs +++ b/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 collection, CancellationToken ct = default) { - await cleanupReferences.PrepareAsync(collection, ct); await queryContentAsync.PrepareAsync(collection, ct); await queryContentsById.PrepareAsync(collection, ct); await queryContentsByQuery.PrepareAsync(collection, ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_EventHandling.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_EventHandling.cs deleted file mode 100644 index 8f381868b..000000000 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_EventHandling.cs +++ /dev/null @@ -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 @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; - } - } -} \ No newline at end of file diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs index 7e0c2dddf..43e1ca3b3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs +++ b/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 }); diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/CleanupReferences.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/CleanupReferences.cs deleted file mode 100644 index 1174035b1..000000000 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/CleanupReferences.cs +++ /dev/null @@ -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( - 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)); - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Extensions.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Extensions.cs index 24492802a..da1865921 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Extensions.cs +++ b/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 ToReferencedIds(this IdContentData data, Schema schema) - { - return data.GetReferencedIds(schema).Distinct().ToList(); - } - - public static NamedContentData FromMongoModel(this IdContentData result, Schema schema, List 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) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs index cc6f19bf7..55770eb87 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs @@ -21,6 +21,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.Repositories Task> QueryAsync(Guid appId, HashSet ids); + Task> QueryIdsAsync(Guid appId, HashSet ids); + Task FindAssetAsync(Guid id); Task FindAssetBySlugAsync(Guid appId, string slug); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs index 89b52b3bb..4fdcad406 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs +++ b/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) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs index 14d52aa4b..d6dbbabc3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs +++ b/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; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs index 836fea5af..0ff733d13 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs @@ -74,7 +74,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries foreach (var step in steps) { - await step.EnrichAsync(context, results, GetSchema); + using (Profiler.TraceMethod(step.ToString()!)) + { + await step.EnrichAsync(context, results, GetSchema); + } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs index f1e0a3436..50c685475 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs +++ b/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 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 GenerateConverters(Context context) + private async Task CleanReferencesAsync(Context context, IEnumerable contents, ProvideSchema schemas) + { + var ids = new HashSet(); + + 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(taskForAssets.Result.Union(taskForContents.Result)); + + return ValueReferencesConverter.CleanReferences(foundIds); + } + + return null; + } + + private async Task> QueryContentIdsAsync(Context context, HashSet ids) + { + var result = await contentRepository.QueryIdsAsync(context.App.Id, ids); + + return result.Select(x => x.Id); + } + + private async Task> QueryAssetIdsAsync(Context context, HashSet ids) + { + var result = await assetRepository.QueryIdsAsync(context.App.Id, ids); + + return result; + } + + private IEnumerable 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); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs index e91e01ba1..4b1b84f04 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs +++ b/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 contents, ILookup assets) { + var temp = new HashSet(); + 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); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs index 1bad80603..03a1ab81b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs +++ b/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); } } diff --git a/backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs b/backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs index 8f61cb249..f91d3a048 100644 --- a/backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs +++ b/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)) { diff --git a/backend/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs b/backend/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs index 015ad46b9..2fa28acf0 100644 --- a/backend/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs +++ b/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; diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs index 994bcff6c..2edef332a 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs +++ b/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(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(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(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); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EnrichContent/ContentEnrichmentTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs similarity index 95% rename from backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EnrichContent/ContentEnrichmentTests.cs rename to backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs index eed1e3fc7..a870ccd75 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EnrichContent/ContentEnrichmentTests.cs +++ b/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)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()); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs index f4282c76c..b4889febe 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs +++ b/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(); + + 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 { 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 = field.GetReferencedIds(null).ToArray(); - var result = sut.GetReferencedIds(JsonValue.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); + + var result = field.GetReferencedIds(value); - Assert.Equal(new[] { schemaId }, result); + Assert.Equal(new HashSet { 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 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 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 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); } } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs index aea83312a..1e122ffec 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs +++ b/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); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs index 271953f3c..f9dd981fe 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs +++ b/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(() => ExecuteScript(original, "data.obj.iv.invalid = 1")); Assert.Throws(() => 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"); } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Tags/TagNormalizerTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Tags/TagNormalizerTests.cs index 86d1690c8..781eaed34 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Tags/TagNormalizerTests.cs +++ b/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>.That.IsSameSequenceAs("n_raw2_1", "n_raw2_2", "n_raw4"), - A>.That.IsSameSequenceAs("o_raw2_1", "o_raw2_2", "o_raw4"))) + A>.That.Is("n_raw2_1", "n_raw2_2", "n_raw4"), + A>.That.Is("o_raw2_1", "o_raw2_2", "o_raw4"))) .Returns(new Dictionary { ["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>.That.IsSameSequenceAs("name2_1", "name2_2", "name4"), + A>.That.Is("name2_1", "name2_2", "name4"), A>.That.IsEmpty())) .Returns(new Dictionary { @@ -86,7 +87,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Tags var newData = GenerateData("id"); A.CallTo(() => tagService.NormalizeTagsAsync(appId, TagGroups.Schemas(schemaId), - A>.That.IsSameSequenceAs("id2_1", "id2_2", "id4"), + A>.That.Is("id2_1", "id2_2", "id4"), A>.That.IsEmpty())) .Returns(new Dictionary { @@ -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")) diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs index 4d3511c5a..7e7620a57 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs +++ b/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()))); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/UIFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/UIFieldTests.cs index 2977b6acb..375d9813c 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/UIFieldTests.cs +++ b/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)))); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/AExtensions.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/AExtensions.cs new file mode 100644 index 000000000..b32b6d26c --- /dev/null +++ b/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(this INegatableArgumentConstraintManager that, params T[]? values) + { + return values == null ? that.IsNull() : that.IsSameSequenceAs(values); + } + + public static HashSet Is(this INegatableArgumentConstraintManager> that, IEnumerable? values) + { + return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Count()); + } + + public static HashSet Is(this INegatableArgumentConstraintManager> that, params T[]? values) + { + return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Length); + } + } +} diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetEnricherTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetEnricherTests.cs index 09a203b9e..d6c1340dd 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetEnricherTests.cs +++ b/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>.That.Has("id1", "id2"))) + A.CallTo(() => tagService.DenormalizeTagsAsync(appId.Id, TagGroups.Assets, A>.That.Is("id1", "id2"))) .Returns(new Dictionary { ["id1"] = "name1", @@ -137,7 +137,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries AppId = appId }; - A.CallTo(() => tagService.DenormalizeTagsAsync(appId.Id, TagGroups.Assets, A>.That.Has("id1", "id2", "id3"))) + A.CallTo(() => tagService.DenormalizeTagsAsync(appId.Id, TagGroups.Assets, A>.That.Is("id1", "id2", "id3"))) .Returns(new Dictionary { ["id1"] = "name1", diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryServiceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryServiceTests.cs index 47686cf1f..89c5954fb 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryServiceTests.cs +++ b/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>.That.IsSameSequenceAs(ids))) + A.CallTo(() => assetRepository.QueryAsync(appId.Id, A>.That.Is(ids))) .Returns(ResultList.CreateFrom(8, found1, found2)); A.CallTo(() => assetEnricher.EnrichAsync(A>.That.IsSameSequenceAs(found1, found2), requestContext)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ConvertDataTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ConvertDataTests.cs new file mode 100644 index 000000000..2d791753a --- /dev/null +++ b/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(); + private readonly IAssetRepository assetRepository = A.Fake(); + private readonly IContentRepository contentRepository = A.Fake(); + private readonly Context requestContext; + private readonly NamedId appId = NamedId.Of(Guid.NewGuid(), "my-app"); + private readonly NamedId 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>.That.Is(id1, id2))) + .Returns(new List { id2 }); + + A.CallTo(() => contentRepository.QueryIdsAsync(appId.Id, A>.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 + }; + } + } +} diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs index 326d867a7..6956424a9 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs +++ b/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); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AExtensions.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AExtensions.cs index 34a4c705f..b7ca6f0ef 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AExtensions.cs +++ b/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 Has(this INegatableArgumentConstraintManager> that, params T[]? values) + public static HashSet Is(this INegatableArgumentConstraintManager> that, IEnumerable? 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 Has(this INegatableArgumentConstraintManager> that, params T[]? values) + public static HashSet Is(this INegatableArgumentConstraintManager> that, params T[]? values) { return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Length); } diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs b/backend/tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs index 5957654fc..21cfb0d17 100644 --- a/backend/tools/TestSuite/TestSuite.ApiTests/ContentCleanupTests.cs +++ b/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); } } }