Browse Source

Data simplified (#624)

* Simplified data usage.

* Code simplified.

* Fixed tests.

* Migration fixed.
pull/627/head
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
b0040382f1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentActionHandler.cs
  2. 4
      backend/extensions/Squidex.Extensions/Validation/CompositeUniqueValidator.cs
  3. 27
      backend/src/Migrations/MigrationPath.cs
  4. 2
      backend/src/Migrations/OldEvents/ContentCreated.cs
  5. 2
      backend/src/Migrations/OldEvents/ContentUpdateProposed.cs
  6. 64
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs
  7. 17
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonParseResult.cs
  8. 75
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonValue.cs
  9. 57
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/IdContentData.cs
  10. 79
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs
  11. 4
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedContentEvent.cs
  12. 109
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs
  13. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverterFlat.cs
  14. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs
  15. 53
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldIdentifier.cs
  16. 35
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs
  17. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueExtensions.cs
  18. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueGenerator.cs
  19. 14
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs
  20. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleVariable.cs
  21. 6
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentDataObject.cs
  22. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/DefaultConverter.cs
  23. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/IScriptEngine.cs
  24. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs
  25. 8
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptVars.cs
  26. 6
      backend/src/Squidex.Domain.Apps.Core.Operations/Tags/TagNormalizer.cs
  27. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/Templates/Extensions/ContentFluidExtension.cs
  28. 6
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs
  29. 68
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs
  30. 148
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueValidator.cs
  31. 32
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Fields.cs
  32. 10
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs
  33. 16
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs
  34. 26
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs
  35. 15
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  36. 43
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs
  37. 105
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Adapt.cs
  38. 12
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/AdaptionVisitor.cs
  39. 47
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/DataConverter.cs
  40. 7
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/OperationBase.cs
  41. 17
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryAsStream.cs
  42. 7
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryById.cs
  43. 14
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByIds.cs
  44. 21
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByQuery.cs
  45. 3
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryReferences.cs
  46. 5
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryReferrers.cs
  47. 8
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryScheduled.cs
  48. 4
      backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs
  49. 2
      backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs
  50. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs
  51. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateJob.cs
  52. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentDataCommand.cs
  53. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs
  54. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentsSearchSource.cs
  55. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/DefaultContentWorkflow.cs
  56. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.State.cs
  57. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs
  58. 14
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentOperationContext.cs
  59. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentVersion.cs
  60. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs
  61. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentActions.cs
  62. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs
  63. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentResolvers.cs
  64. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/Converters.cs
  65. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/IContentEntity.cs
  66. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/IContentWorkflow.cs
  67. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/IEnrichedContentEntity.cs
  68. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs
  69. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs
  70. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs
  71. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs
  72. 55
      backend/src/Squidex.Domain.Apps.Entities/Contents/Text/Extensions.cs
  73. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/Text/TextIndexingProcess.cs
  74. 2
      backend/src/Squidex.Domain.Apps.Events/Contents/ContentCreated.cs
  75. 2
      backend/src/Squidex.Domain.Apps.Events/Contents/ContentDraftCreated.cs
  76. 2
      backend/src/Squidex.Domain.Apps.Events/Contents/ContentUpdated.cs
  77. 10
      backend/src/Squidex.Infrastructure.Azure/EventSourcing/StreamPosition.cs
  78. 10
      backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/StreamPosition.cs
  79. 5
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonHelper.cs
  80. 10
      backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs
  81. 22
      backend/src/Squidex.Infrastructure/ObjectPool/DefaultPools.cs
  82. 36
      backend/src/Squidex.Infrastructure/ObjectPool/MemoryStreamPooledObjectPolicy.cs
  83. 8
      backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
  84. 1
      backend/src/Squidex.Infrastructure/States/ISnapshotStore.cs
  85. 8
      backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  86. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateJobDto.cs
  87. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs
  88. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs
  89. 1
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs
  90. 88
      backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs
  91. 4
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs
  92. 93
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs
  93. 18
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs
  94. 69
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs
  95. 4
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs
  96. 10
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs
  97. 6
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceFormattingTests.cs
  98. 24
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs
  99. 4
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs
  100. 56
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs

2
backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentActionHandler.cs

@ -57,7 +57,7 @@ namespace Squidex.Extensions.Actions.CreateContent
var json = await FormatAsync(action.Data, @event); var json = await FormatAsync(action.Data, @event);
ruleJob.Data = jsonSerializer.Deserialize<NamedContentData>(json); ruleJob.Data = jsonSerializer.Deserialize<ContentData>(json);
if (!string.IsNullOrEmpty(action.Client)) if (!string.IsNullOrEmpty(action.Client))
{ {

4
backend/extensions/Squidex.Extensions/Validation/CompositeUniqueValidator.cs

@ -32,7 +32,7 @@ namespace Squidex.Extensions.Validation
public async Task ValidateAsync(object value, ValidationContext context, AddError addError) public async Task ValidateAsync(object value, ValidationContext context, AddError addError)
{ {
if (value is NamedContentData data) if (value is ContentData data)
{ {
var validateableFields = context.Schema.Fields.Where(IsValidateableField); var validateableFields = context.Schema.Fields.Where(IsValidateableField);
@ -62,7 +62,7 @@ namespace Squidex.Extensions.Validation
} }
} }
private static ClrValue TryGetValue(IRootField field, NamedContentData data) private static ClrValue TryGetValue(IRootField field, ContentData data)
{ {
var value = JsonValue.Null; var value = JsonValue.Null;

27
backend/src/Migrations/MigrationPath.cs

@ -18,7 +18,7 @@ namespace Migrations
{ {
public sealed class MigrationPath : IMigrationPath public sealed class MigrationPath : IMigrationPath
{ {
private const int CurrentVersion = 24; private const int CurrentVersion = 25;
private readonly IServiceProvider serviceProvider; private readonly IServiceProvider serviceProvider;
public MigrationPath(IServiceProvider serviceProvider) public MigrationPath(IServiceProvider serviceProvider)
@ -96,12 +96,6 @@ namespace Migrations
} }
else else
{ {
// Version 17: Rename slug field.
if (version < 17)
{
yield return serviceProvider.GetService<RenameAssetSlugField>();
}
// Version 20: Rename slug field. // Version 20: Rename slug field.
if (version < 20) if (version < 20)
{ {
@ -117,17 +111,16 @@ namespace Migrations
} }
// Version 21: Introduce content drafts V2. // Version 21: Introduce content drafts V2.
if (version < 21) // Version 25: Convert content ids to names.
if (version < 25)
{ {
yield return serviceProvider.GetRequiredService<RebuildContents>(); yield return serviceProvider.GetRequiredService<RebuildContents>();
} }
else
// Version 16: Introduce file name slugs for assets.
if (version < 16)
{ {
// Version 22: Introduce domain id. yield return serviceProvider.GetRequiredService<CreateAssetSlugs>();
if (version < 22)
{
yield return serviceProvider.GetRequiredService<ConvertDocumentIds>().ForContents();
}
} }
} }
@ -137,12 +130,6 @@ namespace Migrations
yield return serviceProvider.GetRequiredService<ConvertRuleEventsJson>(); yield return serviceProvider.GetRequiredService<ConvertRuleEventsJson>();
} }
// Version 16: Introduce file name slugs for assets.
if (version < 16)
{
yield return serviceProvider.GetRequiredService<CreateAssetSlugs>();
}
// Version 19: Unify indexes. // Version 19: Unify indexes.
if (version < 19) if (version < 19)
{ {

2
backend/src/Migrations/OldEvents/ContentCreated.cs

@ -21,7 +21,7 @@ namespace Migrations.OldEvents
{ {
public Status Status { get; set; } public Status Status { get; set; }
public NamedContentData Data { get; set; } public ContentData Data { get; set; }
public IEvent Migrate() public IEvent Migrate()
{ {

2
backend/src/Migrations/OldEvents/ContentUpdateProposed.cs

@ -18,7 +18,7 @@ namespace Migrations.OldEvents
[Obsolete("New Event introduced")] [Obsolete("New Event introduced")]
public sealed class ContentUpdateProposed : ContentEvent, IMigrated<IEvent> public sealed class ContentUpdateProposed : ContentEvent, IMigrated<IEvent>
{ {
public NamedContentData Data { get; set; } public ContentData Data { get; set; }
public IEvent Migrate() public IEvent Migrate()
{ {

64
backend/src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs

@ -13,29 +13,38 @@ using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.Contents namespace Squidex.Domain.Apps.Core.Contents
{ {
public abstract class ContentData<T> : Dictionary<T, ContentFieldData?>, IEquatable<ContentData<T>> where T : notnull public sealed class ContentData : Dictionary<string, ContentFieldData?>, IEquatable<ContentData>
{ {
public IEnumerable<KeyValuePair<T, ContentFieldData?>> ValidValues public IEnumerable<KeyValuePair<string, ContentFieldData?>> ValidValues
{ {
get { return this.Where(x => x.Value != null); } get { return this.Where(x => x.Value != null); }
} }
protected ContentData(IEqualityComparer<T> comparer) public ContentData()
: base(comparer) : base(StringComparer.Ordinal)
{ {
} }
protected ContentData(ContentData<T> source, IEqualityComparer<T> comparer) public ContentData(ContentData source)
: base(source, comparer) : base(source, StringComparer.Ordinal)
{ {
} }
protected ContentData(int capacity, IEqualityComparer<T> comparer) public ContentData(int capacity)
: base(capacity, comparer) : base(capacity, StringComparer.Ordinal)
{ {
} }
protected static TResult MergeTo<TResult>(TResult target, params TResult[] sources) where TResult : ContentData<T> public ContentData AddField(string name, ContentFieldData? data)
{
Guard.NotNullOrEmpty(name, nameof(name));
this[name] = data;
return this;
}
private static ContentData MergeTo(ContentData target, params ContentData[] sources)
{ {
Guard.NotEmpty(sources, nameof(sources)); Guard.NotEmpty(sources, nameof(sources));
@ -66,9 +75,21 @@ namespace Squidex.Domain.Apps.Core.Contents
return target; return target;
} }
protected static TResult Clean<TResult>(TResult source, TResult target) where TResult : ContentData<T> public static ContentData Merge(params ContentData[] contents)
{
return MergeTo(new ContentData(), contents);
}
public ContentData MergeInto(ContentData target)
{
return Merge(target, this);
}
public ContentData ToCleaned()
{ {
foreach (var fieldValue in source.ValidValues) var target = new ContentData();
foreach (var fieldValue in ValidValues)
{ {
if (fieldValue.Value != null) if (fieldValue.Value != null)
{ {
@ -91,10 +112,10 @@ namespace Squidex.Domain.Apps.Core.Contents
public override bool Equals(object? obj) public override bool Equals(object? obj)
{ {
return Equals(obj as ContentData<T>); return Equals(obj as ContentData);
} }
public bool Equals(ContentData<T>? other) public bool Equals(ContentData? other)
{ {
return other != null && (ReferenceEquals(this, other) || this.EqualsDictionary(other)); return other != null && (ReferenceEquals(this, other) || this.EqualsDictionary(other));
} }
@ -103,5 +124,22 @@ namespace Squidex.Domain.Apps.Core.Contents
{ {
return this.DictionaryHashCode(); return this.DictionaryHashCode();
} }
public override string ToString()
{
return $"{{{string.Join(", ", this.Select(x => $"\"{x.Key}\":{x.Value}"))}}}";
}
public ContentData Clone()
{
var clone = new ContentData(Count);
foreach (var (key, value) in this)
{
clone[key] = value?.Clone()!;
}
return clone;
}
} }
} }

17
backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonParseResult.cs

@ -0,0 +1,17 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Core.Contents
{
public enum GeoJsonParseResult
{
Success,
InvalidLatitude,
InvalidLongitude,
InvalidValue
}
}

75
backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonValue.cs

@ -0,0 +1,75 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GeoJSON.Net;
using GeoJSON.Net.Geometry;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.ObjectPool;
using Squidex.Infrastructure.Validation;
namespace Squidex.Domain.Apps.Core.Contents
{
public static class GeoJsonValue
{
public static GeoJsonParseResult TryParse(IJsonValue value, IJsonSerializer serializer, out GeoJSONObject geoJSON)
{
Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(value, nameof(value));
geoJSON = null!;
if (value is JsonObject geoObject)
{
try
{
var stream = DefaultPools.MemoryStream.Get();
try
{
serializer.Serialize(value, stream, true);
stream.Position = 0;
geoJSON = serializer.Deserialize<GeoJSONObject>(stream, null, leaveOpen: true);
return GeoJsonParseResult.Success;
}
finally
{
DefaultPools.MemoryStream.Return(stream);
}
}
catch
{
if (geoObject.TryGetValue<JsonNumber>("latitude", out var lat))
{
if (!lat.Value.IsBetween(-90, 90))
{
return GeoJsonParseResult.InvalidLatitude;
}
}
if (geoObject.TryGetValue<JsonNumber>("longitude", out var lon))
{
if (!lon.Value.IsBetween(-180, 180))
{
return GeoJsonParseResult.InvalidLongitude;
}
}
geoJSON = new Point(new Position(lat!.Value, lon!.Value));
return GeoJsonParseResult.Success;
}
}
return GeoJsonParseResult.InvalidValue;
}
}
}

57
backend/src/Squidex.Domain.Apps.Core.Model/Contents/IdContentData.cs

@ -1,57 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Infrastructure;
#pragma warning disable CA1067 // Override Object.Equals(object) when implementing IEquatable<T>
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class IdContentData : ContentData<long>, IEquatable<IdContentData>
{
public IdContentData()
: base(EqualityComparer<long>.Default)
{
}
public IdContentData(int capacity)
: base(capacity, EqualityComparer<long>.Default)
{
}
public static IdContentData Merge(params IdContentData[] contents)
{
return MergeTo(new IdContentData(), contents);
}
public IdContentData MergeInto(IdContentData target)
{
return Merge(target, this);
}
public IdContentData ToCleaned()
{
return Clean(this, new IdContentData());
}
public IdContentData AddField(long id, ContentFieldData? data)
{
Guard.GreaterThan(id, 0, nameof(id));
this[id] = data;
return this;
}
public bool Equals(IdContentData? other)
{
return base.Equals(other);
}
}
}

79
backend/src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs

@ -1,79 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Linq;
using Squidex.Infrastructure;
#pragma warning disable CA1067 // Override Object.Equals(object) when implementing IEquatable<T>
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class NamedContentData : ContentData<string>, IEquatable<NamedContentData>
{
public NamedContentData()
: base(StringComparer.Ordinal)
{
}
public NamedContentData(NamedContentData source)
: base(source, StringComparer.Ordinal)
{
}
public NamedContentData(int capacity)
: base(capacity, StringComparer.Ordinal)
{
}
public static NamedContentData Merge(params NamedContentData[] contents)
{
return MergeTo(new NamedContentData(), contents);
}
public NamedContentData MergeInto(NamedContentData target)
{
return Merge(target, this);
}
public NamedContentData ToCleaned()
{
return Clean(this, new NamedContentData());
}
public NamedContentData AddField(string name, ContentFieldData? data)
{
Guard.NotNullOrEmpty(name, nameof(name));
this[name] = data;
return this;
}
public NamedContentData Clone()
{
var clone = new NamedContentData(Count);
foreach (var (key, value) in this)
{
clone[key] = value?.Clone()!;
}
return clone;
}
public bool Equals(NamedContentData? other)
{
return base.Equals(other);
}
public override string ToString()
{
return $"{{{string.Join(", ", this.Select(x => $"\"{x.Key}\":{x.Value}"))}}}";
}
}
}

4
backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedContentEvent.cs

@ -25,9 +25,9 @@ namespace Squidex.Domain.Apps.Core.Rules.EnrichedEvents
public RefToken LastModifiedBy { get; set; } public RefToken LastModifiedBy { get; set; }
public NamedContentData Data { get; set; } public ContentData Data { get; set; }
public NamedContentData? DataOld { get; set; } public ContentData? DataOld { get; set; }
public Status Status { get; set; } public Status Status { get; set; }

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

@ -5,52 +5,19 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Linq;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ConvertContent namespace Squidex.Domain.Apps.Core.ConvertContent
{ {
public static class ContentConverter public static class ContentConverter
{ {
public static NamedContentData ConvertId2Name(this IdContentData content, Schema schema, params FieldConverter[] converters) public static ContentData Convert(this ContentData content, Schema schema, params FieldConverter[] converters)
{ {
Guard.NotNull(schema, nameof(schema)); Guard.NotNull(schema, nameof(schema));
var result = new NamedContentData(content.Count); var result = new ContentData(content.Count);
foreach (var (fieldId, data) in content)
{
if (data == null || !schema.FieldsById.TryGetValue(fieldId, out var field))
{
continue;
}
ContentFieldData? newData = data;
ConvertArray(newData, field, FieldIdentifier.ById, FieldIdentifier.ByName);
if (newData != null)
{
newData = ConvertData(converters, field, newData);
}
if (newData != null)
{
result.Add(field.Name, newData);
}
}
return result;
}
public static NamedContentData ConvertName2Name(this NamedContentData content, Schema schema, params FieldConverter[] converters)
{
Guard.NotNull(schema, nameof(schema));
var result = new NamedContentData(content.Count);
foreach (var (fieldName, data) in content) foreach (var (fieldName, data) in content)
{ {
@ -75,40 +42,6 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
return result; return result;
} }
public static IdContentData ConvertName2IdCloned(this NamedContentData content, Schema schema, params FieldConverter[] converters)
{
Guard.NotNull(schema, nameof(schema));
var result = new IdContentData(content.Count);
foreach (var (fieldName, data) in content)
{
if (data == null || !schema.FieldsByName.TryGetValue(fieldName, out var field))
{
continue;
}
ContentFieldData? newData = data.Clone();
if (newData != null)
{
newData = ConvertData(converters, field, newData);
}
if (newData != null)
{
ConvertArray(newData, field, FieldIdentifier.ByName, FieldIdentifier.ById);
}
if (newData != null)
{
result.Add(field.Id, newData);
}
}
return result;
}
private static ContentFieldData? ConvertData(FieldConverter[] converters, IRootField field, ContentFieldData data) private static ContentFieldData? ConvertData(FieldConverter[] converters, IRootField field, ContentFieldData data)
{ {
if (converters != null) if (converters != null)
@ -126,43 +59,5 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
return data; return data;
} }
private static void ConvertArray(ContentFieldData data, IRootField? field, FieldIdentifier sourceIdentifier, FieldIdentifier targetIdentifier)
{
if (field is IArrayField arrayField)
{
foreach (var (_, value) in data)
{
if (value is JsonArray array)
{
foreach (var nested in array.OfType<JsonObject>())
{
var properties = nested.ToList();
nested.Clear();
foreach (var (nestedKey, nestedValue) in properties)
{
if (nestedValue == null)
{
continue;
}
var nestedField = sourceIdentifier.GetField(arrayField, nestedKey);
if (nestedField == null)
{
continue;
}
var targetKey = targetIdentifier.GetStringKey(nestedField);
nested[targetKey] = nestedValue;
}
}
}
}
}
}
} }
} }

4
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverterFlat.cs

@ -14,7 +14,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
{ {
public static class ContentConverterFlat public static class ContentConverterFlat
{ {
public static Dictionary<string, object?> ToFlatten(this NamedContentData content) public static Dictionary<string, object?> ToFlatten(this ContentData content)
{ {
var result = new Dictionary<string, object?>(); var result = new Dictionary<string, object?>();
@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
return result; return result;
} }
public static FlatContentData ToFlatten(this NamedContentData content, string fallback) public static FlatContentData ToFlatten(this ContentData content, string fallback)
{ {
var result = new FlatContentData(); var result = new FlatContentData();

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

@ -42,9 +42,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
try try
{ {
var (_, error) = JsonValueConverter.ConvertValue(field, value, jsonSerializer); if (!JsonValueValidator.IsValid(field, value, jsonSerializer))
if (error != null)
{ {
return null; return null;
} }

53
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldIdentifier.cs

@ -1,53 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public abstract class FieldIdentifier
{
public static readonly FieldIdentifier ByName = new FieldByName();
public static readonly FieldIdentifier ById = new FieldById();
public abstract IField? GetField(IArrayField arrayField, string key);
public abstract string GetStringKey(IField field);
private sealed class FieldByName : FieldIdentifier
{
public override IField? GetField(IArrayField arrayField, string key)
{
return arrayField.FieldsByName.GetValueOrDefault(key);
}
public override string GetStringKey(IField field)
{
return field.Name;
}
}
private sealed class FieldById : FieldIdentifier
{
public override IField? GetField(IArrayField arrayField, string key)
{
if (long.TryParse(key, out var id))
{
return arrayField.FieldsById.GetValueOrDefault(id);
}
return null;
}
public override string GetStringKey(IField field)
{
return field.Id.ToString();
}
}
}
}

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

@ -8,7 +8,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -39,9 +38,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
try try
{ {
var (_, error) = JsonValueConverter.ConvertValue(field, value, jsonSerializer); if (!JsonValueValidator.IsValid(field, value, jsonSerializer))
if (error != null)
{ {
return null; return null;
} }
@ -55,36 +52,6 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
}; };
} }
public static ValueConverter DecodeJson(IJsonSerializer jsonSerializer)
{
return (value, field, parent) =>
{
if (field is IField<JsonFieldProperties> && value is JsonString s)
{
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(s.Value));
return jsonSerializer.Deserialize<IJsonValue>(decoded);
}
return value;
};
}
public static ValueConverter EncodeJson(IJsonSerializer jsonSerializer)
{
return (value, field, parent) =>
{
if (value.Type != JsonValueType.Null && field is IField<JsonFieldProperties>)
{
var encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(jsonSerializer.Serialize(value)));
return JsonValue.Create(encoded);
}
return value;
};
}
public static ValueConverter ResolveAssetUrls(NamedId<DomainId> appId, IReadOnlyCollection<string>? fields, IUrlGenerator urlGenerator) public static ValueConverter ResolveAssetUrls(NamedId<DomainId> appId, IReadOnlyCollection<string>? fields, IUrlGenerator urlGenerator)
{ {
if (fields?.Any() != true) if (fields?.Any() != true)

2
backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueExtensions.cs

@ -12,7 +12,7 @@ namespace Squidex.Domain.Apps.Core.DefaultValues
{ {
public static class DefaultValueExtensions public static class DefaultValueExtensions
{ {
public static void GenerateDefaultValues(this NamedContentData data, Schema schema, PartitionResolver partitionResolver) public static void GenerateDefaultValues(this ContentData data, Schema schema, PartitionResolver partitionResolver)
{ {
var enricher = new DefaultValueGenerator(schema, partitionResolver); var enricher = new DefaultValueGenerator(schema, partitionResolver);

2
backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueGenerator.cs

@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Core.DefaultValues
this.partitionResolver = partitionResolver; this.partitionResolver = partitionResolver;
} }
public void Enrich(NamedContentData data) public void Enrich(ContentData data)
{ {
Guard.NotNull(data, nameof(data)); Guard.NotNull(data, nameof(data));

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

@ -16,7 +16,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{ {
public static class ContentReferencesExtensions public static class ContentReferencesExtensions
{ {
public static HashSet<DomainId> GetReferencedIds(this NamedContentData source, Schema schema, int referencesPerField = int.MaxValue) public static HashSet<DomainId> GetReferencedIds(this ContentData source, Schema schema, int referencesPerField = int.MaxValue)
{ {
Guard.NotNull(schema, nameof(schema)); Guard.NotNull(schema, nameof(schema));
@ -27,14 +27,14 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
return result; return result;
} }
public static void AddReferencedIds(this NamedContentData source, Schema schema, HashSet<DomainId> result, int referencesPerField = int.MaxValue) public static void AddReferencedIds(this ContentData source, Schema schema, HashSet<DomainId> result, int referencesPerField = int.MaxValue)
{ {
Guard.NotNull(schema, nameof(schema)); Guard.NotNull(schema, nameof(schema));
AddReferencedIds(source, schema.Fields, result, referencesPerField); AddReferencedIds(source, schema.Fields, result, referencesPerField);
} }
public static void AddReferencedIds(this NamedContentData source, IEnumerable<IField> fields, HashSet<DomainId> result, int referencesPerField = int.MaxValue) public static void AddReferencedIds(this ContentData source, IEnumerable<IField> fields, HashSet<DomainId> result, int referencesPerField = int.MaxValue)
{ {
Guard.NotNull(fields, nameof(fields)); Guard.NotNull(fields, nameof(fields));
Guard.NotNull(result, nameof(result)); Guard.NotNull(result, nameof(result));
@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
} }
} }
public static void AddReferencedIds(this NamedContentData source, IField field, HashSet<DomainId> result, int referencesPerField = int.MaxValue) public static void AddReferencedIds(this ContentData source, IField field, HashSet<DomainId> result, int referencesPerField = int.MaxValue)
{ {
Guard.NotNull(field, nameof(field)); Guard.NotNull(field, nameof(field));
Guard.NotNull(result, nameof(result)); Guard.NotNull(result, nameof(result));
@ -59,7 +59,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
} }
} }
private static void AddReferencedIds(NamedContentData source, HashSet<DomainId> result, int referencesPerField, IField field) private static void AddReferencedIds(ContentData source, HashSet<DomainId> result, int referencesPerField, IField field)
{ {
if (source.TryGetValue(field.Name, out var fieldData) && fieldData != null) if (source.TryGetValue(field.Name, out var fieldData) && fieldData != null)
{ {
@ -82,7 +82,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
return result; return result;
} }
public static JsonObject FormatReferences(this NamedContentData data, Schema schema, IFieldPartitioning partitioning, string separator = ", ") public static JsonObject FormatReferences(this ContentData data, Schema schema, IFieldPartitioning partitioning, string separator = ", ")
{ {
Guard.NotNull(schema, nameof(schema)); Guard.NotNull(schema, nameof(schema));
Guard.NotNull(partitioning, nameof(partitioning)); Guard.NotNull(partitioning, nameof(partitioning));
@ -97,7 +97,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
return result; return result;
} }
private static string FormatReferenceFields(this NamedContentData data, Schema schema, string partitionKey, string separator) private static string FormatReferenceFields(this ContentData data, Schema schema, string partitionKey, string separator)
{ {
Guard.NotNull(schema, nameof(schema)); Guard.NotNull(schema, nameof(schema));

2
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleVariable.cs

@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
{ {
var segment = path[i]; var segment = path[i];
if (current is NamedContentData data) if (current is ContentData data)
{ {
if (!data.TryGetValue(segment, out var temp) || temp == null) if (!data.TryGetValue(segment, out var temp) || temp == null)
{ {

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

@ -20,14 +20,14 @@ namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper
{ {
public sealed class ContentDataObject : ObjectInstance public sealed class ContentDataObject : ObjectInstance
{ {
private readonly NamedContentData contentData; private readonly ContentData contentData;
private HashSet<string> fieldsToDelete; private HashSet<string> fieldsToDelete;
private Dictionary<string, PropertyDescriptor> fieldProperties; private Dictionary<string, PropertyDescriptor> fieldProperties;
private bool isChanged; private bool isChanged;
public override bool Extensible => true; public override bool Extensible => true;
public ContentDataObject(Engine engine, NamedContentData contentData) public ContentDataObject(Engine engine, ContentData contentData)
: base(engine) : base(engine)
{ {
this.contentData = contentData; this.contentData = contentData;
@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper
isChanged = true; isChanged = true;
} }
public bool TryUpdate(out NamedContentData result) public bool TryUpdate(out ContentData result)
{ {
result = contentData; result = contentData;

2
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/DefaultConverter.cs

@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.Scripting
case Status status: case Status status:
result = status.ToString(); result = status.ToString();
return true; return true;
case NamedContentData content: case ContentData content:
result = new ContentDataObject(engine, content); result = new ContentDataObject(engine, content);
return true; return true;
} }

2
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/IScriptEngine.cs

@ -15,7 +15,7 @@ namespace Squidex.Domain.Apps.Core.Scripting
{ {
Task<IJsonValue> ExecuteAsync(ScriptVars vars, string script, ScriptOptions options = default); Task<IJsonValue> ExecuteAsync(ScriptVars vars, string script, ScriptOptions options = default);
Task<NamedContentData> TransformAsync(ScriptVars vars, string script, ScriptOptions options = default); Task<ContentData> TransformAsync(ScriptVars vars, string script, ScriptOptions options = default);
IJsonValue Execute(ScriptVars vars, string script, ScriptOptions options = default); IJsonValue Execute(ScriptVars vars, string script, ScriptOptions options = default);

4
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs

@ -71,14 +71,14 @@ namespace Squidex.Domain.Apps.Core.Scripting
} }
} }
public async Task<NamedContentData> TransformAsync(ScriptVars vars, string script, ScriptOptions options = default) public async Task<ContentData> TransformAsync(ScriptVars vars, string script, ScriptOptions options = default)
{ {
Guard.NotNull(vars, nameof(vars)); Guard.NotNull(vars, nameof(vars));
Guard.NotNullOrEmpty(script, nameof(script)); Guard.NotNullOrEmpty(script, nameof(script));
using (var cts = new CancellationTokenSource(TimeoutExecution)) using (var cts = new CancellationTokenSource(TimeoutExecution))
{ {
var tcs = new TaskCompletionSource<NamedContentData>(); var tcs = new TaskCompletionSource<ContentData>();
using (cts.Token.Register(() => tcs.TrySetCanceled())) using (cts.Token.Register(() => tcs.TrySetCanceled()))
{ {

8
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptVars.cs

@ -57,15 +57,15 @@ namespace Squidex.Domain.Apps.Core.Scripting
set => SetValue(value); set => SetValue(value);
} }
public NamedContentData? Data public ContentData? Data
{ {
get => GetValue<NamedContentData?>(); get => GetValue<ContentData?>();
set => SetValue(value); set => SetValue(value);
} }
public NamedContentData? DataOld public ContentData? DataOld
{ {
get => GetValue<NamedContentData?>(); get => GetValue<ContentData?>();
set set
{ {
SetValue(value, "oldData"); SetValue(value, "oldData");

6
backend/src/Squidex.Domain.Apps.Core.Operations/Tags/TagNormalizer.cs

@ -16,7 +16,7 @@ namespace Squidex.Domain.Apps.Core.Tags
{ {
public static class TagNormalizer public static class TagNormalizer
{ {
public static async Task NormalizeAsync(this ITagService tagService, DomainId appId, DomainId schemaId, Schema schema, NamedContentData newData, NamedContentData? oldData) public static async Task NormalizeAsync(this ITagService tagService, DomainId appId, DomainId schemaId, Schema schema, ContentData newData, ContentData? oldData)
{ {
Guard.NotNull(tagService, nameof(tagService)); Guard.NotNull(tagService, nameof(tagService));
Guard.NotNull(schema, nameof(schema)); Guard.NotNull(schema, nameof(schema));
@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Core.Tags
} }
} }
public static async Task DenormalizeAsync(this ITagService tagService, DomainId appId, DomainId schemaId, Schema schema, params NamedContentData[] datas) public static async Task DenormalizeAsync(this ITagService tagService, DomainId appId, DomainId schemaId, Schema schema, params ContentData[] datas)
{ {
Guard.NotNull(tagService, nameof(tagService)); Guard.NotNull(tagService, nameof(tagService));
Guard.NotNull(schema, nameof(schema)); Guard.NotNull(schema, nameof(schema));
@ -79,7 +79,7 @@ namespace Squidex.Domain.Apps.Core.Tags
} }
} }
private static void GetValues(Schema schema, HashSet<string> values, List<JsonArray> arrays, params NamedContentData[] datas) private static void GetValues(Schema schema, HashSet<string> values, List<JsonArray> arrays, params ContentData[] datas)
{ {
foreach (var field in schema.Fields) foreach (var field in schema.Fields)
{ {

2
backend/src/Squidex.Domain.Apps.Core.Operations/Templates/Extensions/ContentFluidExtension.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Core.Templates.Extensions
FluidValue.SetTypeMapping<JsonNumber>(x => FluidValue.Create(x.Value)); FluidValue.SetTypeMapping<JsonNumber>(x => FluidValue.Create(x.Value));
FluidValue.SetTypeMapping<JsonNull>(x => FluidValue.Create(null)); FluidValue.SetTypeMapping<JsonNull>(x => FluidValue.Create(null));
memberAccessStrategy.Register<NamedContentData, object?>( memberAccessStrategy.Register<ContentData, object?>(
(value, name) => value.GetOrDefault(name)); (value, name) => value.GetOrDefault(name));
memberAccessStrategy.Register<JsonObject, object?>( memberAccessStrategy.Register<JsonObject, object?>(

6
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs

@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
errors.Add(new ValidationError(message, pathString)); errors.Add(new ValidationError(message, pathString));
} }
public Task ValidateInputPartialAsync(NamedContentData data) public Task ValidateInputPartialAsync(ContentData data)
{ {
Guard.NotNull(data, nameof(data)); Guard.NotNull(data, nameof(data));
@ -62,7 +62,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
return validator.ValidateAsync(data, context, AddError); return validator.ValidateAsync(data, context, AddError);
} }
public Task ValidateInputAsync(NamedContentData data) public Task ValidateInputAsync(ContentData data)
{ {
Guard.NotNull(data, nameof(data)); Guard.NotNull(data, nameof(data));
@ -71,7 +71,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
return validator.ValidateAsync(data, context, AddError); return validator.ValidateAsync(data, context, AddError);
} }
public Task ValidateContentAsync(NamedContentData data) public Task ValidateContentAsync(ContentData data)
{ {
Guard.NotNull(data, nameof(data)); Guard.NotNull(data, nameof(data));

68
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs

@ -6,16 +6,13 @@
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using GeoJSON.Net;
using GeoJSON.Net.Geometry;
using NodaTime.Text; using NodaTime.Text;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Translations;
using Squidex.Infrastructure.Validation;
namespace Squidex.Domain.Apps.Core.ValidateContent namespace Squidex.Domain.Apps.Core.ValidateContent
{ {
@ -124,54 +121,19 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
public (object? Result, JsonError? Error) Visit(IField<GeolocationFieldProperties> field, Args args) public (object? Result, JsonError? Error) Visit(IField<GeolocationFieldProperties> field, Args args)
{ {
if (args.Value is JsonObject geoObject) var result = GeoJsonValue.TryParse(args.Value, args.JsonSerializer, out var value);
{
try
{
using (var stream = new MemoryStream())
{
args.JsonSerializer.Serialize(args.Value, stream, true);
stream.Position = 0;
var geoJson = args.JsonSerializer.Deserialize<GeoJSONObject>(stream);
return (geoJson, null);
}
}
catch
{
if (geoObject.TryGetValue<JsonNumber>("latitude", out var lat))
{
if (!lat.Value.IsBetween(-90, 90))
{
return (null, new JsonError(T.Get("contents.invalidGeolocationLatitude")));
}
}
else
{
return (null, new JsonError(T.Get("contents.invalidGeolocation")));
}
if (geoObject.TryGetValue<JsonNumber>("longitude", out var lon)) switch (result)
{ {
if (!lon.Value.IsBetween(-180, 180)) case GeoJsonParseResult.InvalidLatitude:
{ return (null, new JsonError(T.Get("contents.invalidGeolocationLatitude")));
return (null, new JsonError(T.Get("contents.invalidGeolocationLongitude"))); case GeoJsonParseResult.InvalidLongitude:
} return (null, new JsonError(T.Get("contents.invalidGeolocationLongitude")));
} case GeoJsonParseResult.InvalidValue:
else return (null, new JsonError(T.Get("contents.invalidGeolocation")));
{ default:
return (null, new JsonError(T.Get("contents.invalidGeolocation"))); return (value, null);
}
var position = new Point(new Position(lat.Value, lon.Value));
return (position, null);
}
} }
return (null, new JsonError(T.Get("contents.invalidGeolocation")));
} }
public (object? Result, JsonError? Error) Visit(IField<JsonFieldProperties> field, Args args) public (object? Result, JsonError? Error) Visit(IField<JsonFieldProperties> field, Args args)
@ -211,11 +173,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
foreach (var item in array) foreach (var item in array)
{ {
if (item is JsonNull) if (item is JsonString s && !string.IsNullOrWhiteSpace(s.Value))
{
result.Add(null);
}
else if (item is JsonString s)
{ {
result.Add(s.Value); result.Add(s.Value);
} }

148
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueValidator.cs

@ -0,0 +1,148 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using NodaTime.Text;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ValidateContent
{
public sealed class JsonValueValidator : IFieldVisitor<bool, JsonValueValidator.Args>
{
private static readonly JsonValueValidator Instance = new JsonValueValidator();
public readonly struct Args
{
public readonly IJsonValue Value;
public readonly IJsonSerializer JsonSerializer;
public Args(IJsonValue value, IJsonSerializer jsonSerializer)
{
Value = value;
JsonSerializer = jsonSerializer;
}
}
private JsonValueValidator()
{
}
public static bool IsValid(IField field, IJsonValue value, IJsonSerializer jsonSerializer)
{
Guard.NotNull(field, nameof(field));
Guard.NotNull(value, nameof(value));
var args = new Args(value, jsonSerializer);
return field.Accept(Instance, args);
}
public bool Visit(IArrayField field, Args args)
{
return IsValidObjectList(args.Value);
}
public bool Visit(IField<AssetsFieldProperties> field, Args args)
{
return IsValidStringList(args.Value);
}
public bool Visit(IField<ReferencesFieldProperties> field, Args args)
{
return IsValidStringList(args.Value);
}
public bool Visit(IField<TagsFieldProperties> field, Args args)
{
return IsValidStringList(args.Value);
}
public bool Visit(IField<BooleanFieldProperties> field, Args args)
{
return args.Value is JsonBoolean;
}
public bool Visit(IField<NumberFieldProperties> field, Args args)
{
return args.Value is JsonNumber;
}
public bool Visit(IField<StringFieldProperties> field, Args args)
{
return args.Value is JsonString;
}
public bool Visit(IField<UIFieldProperties> field, Args args)
{
return true;
}
public bool Visit(IField<DateTimeFieldProperties> field, Args args)
{
if (args.Value.Type == JsonValueType.String)
{
var parseResult = InstantPattern.General.Parse(args.Value.ToString());
return parseResult.Success;
}
return false;
}
public bool Visit(IField<GeolocationFieldProperties> field, Args args)
{
var result = GeoJsonValue.TryParse(args.Value, args.JsonSerializer, out _);
return result == GeoJsonParseResult.Success;
}
public bool Visit(IField<JsonFieldProperties> field, Args args)
{
return true;
}
private static bool IsValidStringList(IJsonValue value)
{
if (value is JsonArray array)
{
foreach (var item in array)
{
if (item is not JsonString)
{
return false;
}
}
return true;
}
return false;
}
private static bool IsValidObjectList(IJsonValue value)
{
if (value is JsonArray array)
{
foreach (var item in array)
{
if (item is not JsonObject)
{
return false;
}
}
return true;
}
return false;
}
}
}

32
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Fields.cs

@ -1,32 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using MongoDB.Bson.Serialization;
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
internal static class Fields
{
private static readonly Lazy<string> AssetIdField = new Lazy<string>(GetAssetIdField);
private static readonly Lazy<string> AssetFolderIdField = new Lazy<string>(GetAssetFolderIdField);
public static string AssetId => AssetIdField.Value;
public static string AssetFolderId => AssetFolderIdField.Value;
private static string GetAssetIdField()
{
return BsonClassMap.LookupClassMap(typeof(MongoAssetEntity)).GetMemberMap(nameof(MongoAssetEntity.Id)).ElementName;
}
private static string GetAssetFolderIdField()
{
return BsonClassMap.LookupClassMap(typeof(MongoAssetFolderEntity)).GetMemberMap(nameof(MongoAssetFolderEntity.Id)).ElementName;
}
}
}

10
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs

@ -25,12 +25,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors
query.Filter = FirstPascalPathConverter<ClrValue>.Transform(query.Filter); query.Filter = FirstPascalPathConverter<ClrValue>.Transform(query.Filter);
} }
query.Sort = query.Sort if (query.Sort != null)
.Select(x => {
new SortNode( query.Sort = query.Sort.Select(x => new SortNode(x.Path.ToFirstPascalCase(), x.Order)).ToList();
x.Path.ToFirstPascalCase(), }
x.Order))
.ToList();
return query; return query;
} }

16
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs

@ -34,18 +34,18 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
private readonly QueryScheduled queryScheduled; private readonly QueryScheduled queryScheduled;
private readonly string name; private readonly string name;
public MongoContentCollection(string name, IMongoDatabase database, IAppProvider appProvider, DataConverter dataConverter) public MongoContentCollection(string name, IMongoDatabase database, IAppProvider appProvider)
: base(database) : base(database)
{ {
this.name = name; this.name = name;
queryAsStream = new QueryAsStream(dataConverter, appProvider); queryAsStream = new QueryAsStream();
queryBdId = new QueryById(dataConverter); queryBdId = new QueryById();
queryByIds = new QueryByIds(dataConverter); queryByIds = new QueryByIds();
queryByQuery = new QueryByQuery(dataConverter, appProvider); queryByQuery = new QueryByQuery(appProvider);
queryReferences = new QueryReferences(dataConverter, queryByIds); queryReferences = new QueryReferences(queryByIds);
queryReferrers = new QueryReferrers(dataConverter); queryReferrers = new QueryReferrers();
queryScheduled = new QueryScheduled(dataConverter); queryScheduled = new QueryScheduled();
} }
public IMongoCollection<MongoContentEntity> GetInternalCollection() public IMongoCollection<MongoContentEntity> GetInternalCollection()

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

@ -6,14 +6,10 @@
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ExtractReferenceIds;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
@ -22,8 +18,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
[BsonIgnoreExtraElements] [BsonIgnoreExtraElements]
public sealed class MongoContentEntity : IContentEntity, IVersionedEntity<DomainId> public sealed class MongoContentEntity : IContentEntity, IVersionedEntity<DomainId>
{ {
private NamedContentData data;
[BsonId] [BsonId]
[BsonElement("_id")] [BsonElement("_id")]
public DomainId DocumentId { get; set; } public DomainId DocumentId { get; set; }
@ -63,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
[BsonIgnoreIfNull] [BsonIgnoreIfNull]
[BsonElement("do")] [BsonElement("do")]
[BsonJson] [BsonJson]
public IdContentData DataByIds { get; set; } public ContentData Data { get; set; }
[BsonIgnoreIfNull] [BsonIgnoreIfNull]
[BsonElement("sa")] [BsonElement("sa")]
@ -97,27 +91,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
[BsonElement("mb")] [BsonElement("mb")]
public RefToken LastModifiedBy { get; set; } public RefToken LastModifiedBy { get; set; }
[BsonIgnore]
public NamedContentData Data
{
get { return data; }
}
public DomainId UniqueId public DomainId UniqueId
{ {
get { return DocumentId; } get { return DocumentId; }
} }
public void LoadData(NamedContentData data, Schema schema, DataConverter converter)
{
ReferencedIds = data.GetReferencedIds(schema).ToHashSet();
DataByIds = converter.ToMongoModel(data, schema);
}
public void ParseData(Schema schema, DataConverter converter)
{
data = converter.FromMongoModel(DataByIds, schema);
}
} }
} }

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

@ -15,19 +15,15 @@ using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Hosting; using Squidex.Hosting;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{ {
public partial class MongoContentRepository : IContentRepository, IInitializable public partial class MongoContentRepository : IContentRepository, IInitializable
{ {
private readonly IAppProvider appProvider;
private readonly DataConverter converter;
private readonly MongoContentCollection collectionAll; private readonly MongoContentCollection collectionAll;
private readonly MongoContentCollection collectionPublished; private readonly MongoContentCollection collectionPublished;
@ -36,22 +32,17 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
StatusSerializer.Register(); StatusSerializer.Register();
} }
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider, IJsonSerializer serializer) public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider)
{ {
Guard.NotNull(appProvider, nameof(appProvider)); Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(serializer, nameof(serializer));
this.appProvider = appProvider;
converter = new DataConverter(serializer);
collectionAll = collectionAll =
new MongoContentCollection( new MongoContentCollection(
"States_Contents_All2", database, appProvider, converter); "States_Contents_All3", database, appProvider);
collectionPublished = collectionPublished =
new MongoContentCollection( new MongoContentCollection(
"States_Contents_Published2", database, appProvider, converter); "States_Contents_Published3", database, appProvider);
} }
public async Task InitializeAsync(CancellationToken ct = default) public async Task InitializeAsync(CancellationToken ct = default)

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

@ -10,7 +10,6 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.DomainObject; using Squidex.Domain.Apps.Entities.Contents.DomainObject;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
@ -51,15 +50,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
if (contentEntity != null) if (contentEntity != null)
{ {
var schema = await GetSchemaAsync(contentEntity.IndexedAppId, contentEntity.IndexedSchemaId);
if (schema == null)
{
return (null!, EtagVersion.NotFound);
}
contentEntity.ParseData(schema.SchemaDef, converter);
return (SimpleMapper.Map(contentEntity, new ContentDomainObject.State()), contentEntity.Version); return (SimpleMapper.Map(contentEntity, new ContentDomainObject.State()), contentEntity.Version);
} }
@ -76,25 +66,17 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
return; return;
} }
var schema = await GetSchemaAsync(value.AppId.Id, value.SchemaId.Id); await Task.WhenAll(
UpsertDraftContentAsync(value, oldVersion, newVersion),
if (schema == null) UpsertOrDeletePublishedAsync(value, oldVersion, newVersion));
{
return;
}
var saveDraft = UpsertDraftContentAsync(value, oldVersion, newVersion, schema);
var savePublic = UpsertOrDeletePublishedAsync(value, oldVersion, newVersion, schema);
await Task.WhenAll(saveDraft, savePublic);
} }
} }
private async Task UpsertOrDeletePublishedAsync(ContentDomainObject.State value, long oldVersion, long newVersion, ISchemaEntity schema) private async Task UpsertOrDeletePublishedAsync(ContentDomainObject.State value, long oldVersion, long newVersion)
{ {
if (value.Status == Status.Published && !value.IsDeleted) if (value.Status == Status.Published && !value.IsDeleted)
{ {
await UpsertPublishedContentAsync(value, oldVersion, newVersion, schema); await UpsertPublishedContentAsync(value, oldVersion, newVersion);
} }
else else
{ {
@ -109,7 +91,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
return collectionPublished.RemoveAsync(documentId); return collectionPublished.RemoveAsync(documentId);
} }
private async Task UpsertDraftContentAsync(ContentDomainObject.State value, long oldVersion, long newVersion, ISchemaEntity schema) private async Task UpsertDraftContentAsync(ContentDomainObject.State value, long oldVersion, long newVersion)
{ {
var content = SimpleMapper.Map(value, new MongoContentEntity var content = SimpleMapper.Map(value, new MongoContentEntity
{ {
@ -123,12 +105,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
content.ScheduleJob = value.ScheduleJob; content.ScheduleJob = value.ScheduleJob;
content.NewStatus = value.NewStatus; content.NewStatus = value.NewStatus;
content.LoadData(value.Data, schema.SchemaDef, converter);
await collectionAll.UpsertVersionedAsync(content.DocumentId, oldVersion, content); await collectionAll.UpsertVersionedAsync(content.DocumentId, oldVersion, content);
} }
private async Task UpsertPublishedContentAsync(ContentDomainObject.State value, long oldVersion, long newVersion, ISchemaEntity schema) private async Task UpsertPublishedContentAsync(ContentDomainObject.State value, long oldVersion, long newVersion)
{ {
var content = SimpleMapper.Map(value, new MongoContentEntity var content = SimpleMapper.Map(value, new MongoContentEntity
{ {
@ -142,16 +122,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
content.ScheduleJob = null; content.ScheduleJob = null;
content.NewStatus = null; content.NewStatus = null;
content.LoadData(value.CurrentVersion.Data, schema.SchemaDef, converter);
await collectionPublished.UpsertVersionedAsync(content.DocumentId, oldVersion, content); await collectionPublished.UpsertVersionedAsync(content.DocumentId, oldVersion, content);
} }
private async Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, DomainId schemaId)
{
var schema = await appProvider.GetSchemaAsync(appId, schemaId, true);
return schema;
}
} }
} }

105
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Adapt.cs

@ -8,96 +8,93 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Core.GenerateEdmSchema; using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
public static class Adapt public static class Adapt
{ {
private static readonly Dictionary<string, string> PropertyMap = private static Dictionary<string, PropertyPath> pathMap;
typeof(MongoContentEntity).GetProperties() private static Dictionary<string, string> propertyMap;
.ToDictionary(
ToElementName,
ToName,
StringComparer.OrdinalIgnoreCase);
private static string ToName(PropertyInfo x) public static IReadOnlyDictionary<string, string> PropertyMap
{ {
return x.GetCustomAttribute<BsonElementAttribute>()?.ElementName ?? x.Name; get
} {
if (propertyMap == null)
{
propertyMap =
BsonClassMap.LookupClassMap(typeof(MongoContentEntity)).AllMemberMaps
.ToDictionary(
x => x.MemberName,
x => x.ElementName,
StringComparer.OrdinalIgnoreCase);
}
private static string ToElementName(PropertyInfo x) return propertyMap;
{ }
return x.Name;
} }
public static Func<PropertyPath, PropertyPath> Path(Schema? schema) public static IReadOnlyDictionary<string, PropertyPath> PathMap
{ {
return propertyNames => get
{ {
var result = new List<string>(propertyNames); if (pathMap == null)
if (result.Count > 1 && schema != null)
{ {
var rootEdmName = result[1].UnescapeEdmField(); pathMap = PropertyMap.ToDictionary(x => x.Key, x => (PropertyPath)x.Value);
var rootField = schema.FieldsByName[rootEdmName]; }
result[1] = rootField.Id.ToString(); return pathMap;
}
}
if (rootField is IArrayField arrayField && result.Count > 3) public static PropertyPath MapPath(PropertyPath path)
{ {
var nestedEdmName = result[3].UnescapeEdmField(); if (path.Count == 1 && PathMap.TryGetValue(path[0], out var mappedPath))
var nestedField = arrayField.FieldsByName[nestedEdmName]; {
return mappedPath;
}
result[3] = nestedField.Id.ToString(); var result = new List<string>(path);
}
}
if (result.Count > 2) if (result.Count > 0)
{
if (PropertyMap.TryGetValue(path[0], out var mapped))
{ {
result[2] = result[2].UnescapeEdmField(); result[0] = mapped;
} }
}
if (result.Count > 0) for (var i = 1; i < path.Count; i++)
{ {
if (result[0].Equals("Data", StringComparison.CurrentCultureIgnoreCase)) result[i] = result[i].UnescapeEdmField().EscapeJson();
{ }
result[0] = "do";
}
else
{
result[0] = PropertyMap[propertyNames[0]];
}
}
return result; return result;
};
} }
public static ClrQuery AdjustToModel(this ClrQuery query, DomainId appId, Schema? schema) public static ClrQuery AdjustToModel(this ClrQuery query, DomainId appId)
{ {
var pathConverter = Path(schema);
if (query.Filter != null) if (query.Filter != null)
{ {
query.Filter = AdaptionVisitor.Adapt(query.Filter, pathConverter, appId); query.Filter = AdaptionVisitor.AdaptFilter(query.Filter, appId);
} }
query.Sort = query.Sort.Select(x => new SortNode(pathConverter(x.Path), x.Order)).ToList(); if (query.Sort != null)
{
query.Sort = query.Sort.Select(x => new SortNode(MapPath(x.Path), x.Order)).ToList();
}
return query; return query;
} }
public static FilterNode<ClrValue>? AdjustToModel(this FilterNode<ClrValue> filter, DomainId appId, Schema schema) public static FilterNode<ClrValue>? AdjustToModel(this FilterNode<ClrValue> filter, DomainId appId)
{ {
var pathConverter = Path(schema); return AdaptionVisitor.AdaptFilter(filter, appId);
return AdaptionVisitor.Adapt(filter, pathConverter, appId);
} }
} }
} }

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

@ -20,14 +20,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
public struct Args public struct Args
{ {
public readonly Func<PropertyPath, PropertyPath> PathConverter;
public readonly DomainId AppId; public readonly DomainId AppId;
public Args(Func<PropertyPath, PropertyPath> pathConverter, DomainId appId) public Args(DomainId appId)
{ {
PathConverter = pathConverter;
AppId = appId; AppId = appId;
} }
} }
@ -36,9 +32,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
} }
public static FilterNode<ClrValue>? Adapt(FilterNode<ClrValue> filter, Func<PropertyPath, PropertyPath> pathConverter, DomainId appId) public static FilterNode<ClrValue>? AdaptFilter(FilterNode<ClrValue> filter, DomainId appId)
{ {
var args = new Args(pathConverter, appId); var args = new Args(appId);
return filter.Accept(Instance, args); return filter.Accept(Instance, args);
} }
@ -74,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
} }
else else
{ {
path = args.PathConverter(path); path = Adapt.MapPath(path);
if (clrValue is List<Guid> guidList) if (clrValue is List<Guid> guidList)
{ {

47
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/DataConverter.cs

@ -1,47 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
public sealed class DataConverter
{
private readonly FieldConverter[] decodeJsonConverters;
private readonly FieldConverter[] encodeJsonConverters;
public DataConverter(IJsonSerializer serializer)
{
var decoder = ValueConverters.DecodeJson(serializer);
decodeJsonConverters = new[]
{
FieldConverters.ForValues(decoder, ValueConverters.ForNested(decoder))
};
var encoder = ValueConverters.EncodeJson(serializer);
encodeJsonConverters = new[]
{
FieldConverters.ForValues(encoder, ValueConverters.ForNested(encoder))
};
}
public NamedContentData FromMongoModel(IdContentData result, Schema schema)
{
return result.ConvertId2Name(schema, decodeJsonConverters);
}
public IdContentData ToMongoModel(NamedContentData result, Schema schema)
{
return result.ConvertName2IdCloned(schema, encodeJsonConverters);
}
}
}

7
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/OperationBase.cs

@ -21,13 +21,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
public IMongoCollection<MongoContentEntity> Collection { get; private set; } public IMongoCollection<MongoContentEntity> Collection { get; private set; }
public DataConverter DataConverter { get; }
protected OperationBase(DataConverter dataConverter)
{
DataConverter = dataConverter;
}
public Task PrepareAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default) public Task PrepareAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default)
{ {
Collection = collection; Collection = collection;

17
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryAsStream.cs

@ -16,14 +16,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
public sealed class QueryAsStream : OperationBase public sealed class QueryAsStream : OperationBase
{ {
private readonly IAppProvider appProvider;
public QueryAsStream(DataConverter converter, IAppProvider appProvider)
: base(converter)
{
this.appProvider = appProvider;
}
protected override async Task PrepareAsync(CancellationToken ct = default) protected override async Task PrepareAsync(CancellationToken ct = default)
{ {
var indexBySchema = var indexBySchema =
@ -48,14 +40,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
foreach (var entity in cursor.Current) foreach (var entity in cursor.Current)
{ {
var schema = await appProvider.GetSchemaAsync(appId, entity.SchemaId.Id, false); yield return entity;
if (schema != null)
{
entity.ParseData(schema.SchemaDef, DataConverter);
yield return entity;
}
} }
} }
} }

7
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryById.cs

@ -15,11 +15,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
internal sealed class QueryById : OperationBase internal sealed class QueryById : OperationBase
{ {
public QueryById(DataConverter dataConverter)
: base(dataConverter)
{
}
public async Task<IContentEntity?> QueryAsync(ISchemaEntity schema, DomainId id) public async Task<IContentEntity?> QueryAsync(ISchemaEntity schema, DomainId id)
{ {
Guard.NotNull(schema, nameof(schema)); Guard.NotNull(schema, nameof(schema));
@ -36,8 +31,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
return null; return null;
} }
contentEntity?.ParseData(schema.SchemaDef, DataConverter);
} }
return contentEntity; return contentEntity;

14
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByIds.cs

@ -20,11 +20,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
internal sealed class QueryByIds : OperationBase internal sealed class QueryByIds : OperationBase
{ {
public QueryByIds(DataConverter dataConverter)
: base(dataConverter)
{
}
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id, Status Status)>> QueryIdsAsync(DomainId appId, HashSet<DomainId> ids) public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id, Status Status)>> QueryIdsAsync(DomainId appId, HashSet<DomainId> ids)
{ {
if (ids == null || ids.Count == 0) if (ids == null || ids.Count == 0)
@ -59,15 +54,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
contentTotal = await Collection.Find(filter).CountDocumentsAsync(); contentTotal = await Collection.Find(filter).CountDocumentsAsync();
} }
var contentSchemas = schemas.ToDictionary(x => x.Id);
foreach (var content in contentEntities)
{
var schema = contentSchemas[content.SchemaId.Id];
content.ParseData(schema.SchemaDef, DataConverter);
}
} }
return ResultList.Create(contentTotal, contentEntities); return ResultList.Create(contentTotal, contentEntities);

21
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryByQuery.cs

@ -36,8 +36,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
public MongoContentEntity[] Joined { get; set; } public MongoContentEntity[] Joined { get; set; }
} }
public QueryByQuery(DataConverter dataConverter, IAppProvider appProvider) public QueryByQuery(IAppProvider appProvider)
: base(dataConverter)
{ {
this.appProvider = appProvider; this.appProvider = appProvider;
} }
@ -76,7 +75,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
return new List<(DomainId SchemaId, DomainId Id, Status Status)>(); return new List<(DomainId SchemaId, DomainId Id, Status Status)>();
} }
var filter = BuildFilter(appId, schemaId, filterNode.AdjustToModel(appId, schema.SchemaDef)); var filter = BuildFilter(appId, schemaId, filterNode.AdjustToModel(appId));
var contentItems = await Collection.FindStatusAsync(filter); var contentItems = await Collection.FindStatusAsync(filter);
@ -99,7 +98,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
try try
{ {
var query = q.Query.AdjustToModel(app.Id, null); var query = q.Query.AdjustToModel(app.Id);
var filter = CreateFilter(app.Id, schemas.Select(x => x.Id), query, q.Reference); var filter = CreateFilter(app.Id, schemas.Select(x => x.Id), query, q.Reference);
@ -112,13 +111,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
contentTotal = await Collection.Find(filter).CountDocumentsAsync(); contentTotal = await Collection.Find(filter).CountDocumentsAsync();
} }
var contentSchemas = schemas.ToDictionary(x => x.Id);
foreach (var entity in contentEntities)
{
entity.ParseData(contentSchemas[entity.IndexedSchemaId].SchemaDef, DataConverter);
}
} }
return ResultList.Create<IContentEntity>(contentTotal, contentEntities); return ResultList.Create<IContentEntity>(contentTotal, contentEntities);
@ -141,7 +133,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
try try
{ {
var query = q.Query.AdjustToModel(app.Id, schema.SchemaDef); var query = q.Query.AdjustToModel(app.Id);
var filter = CreateFilter(schema.AppId.Id, Enumerable.Repeat(schema.Id, 1), query, q.Reference); var filter = CreateFilter(schema.AppId.Id, Enumerable.Repeat(schema.Id, 1), query, q.Reference);
@ -154,11 +146,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
contentTotal = await Collection.Find(filter).CountDocumentsAsync(); contentTotal = await Collection.Find(filter).CountDocumentsAsync();
} }
foreach (var entity in contentEntities)
{
entity.ParseData(schema.SchemaDef, DataConverter);
}
} }
return ResultList.Create<IContentEntity>(contentTotal, contentEntities); return ResultList.Create<IContentEntity>(contentTotal, contentEntities);

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

@ -31,8 +31,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
public HashSet<DomainId>? ReferencedIds { get; set; } public HashSet<DomainId>? ReferencedIds { get; set; }
} }
public QueryReferences(DataConverter dataConverter, QueryByIds queryByIds) public QueryReferences(QueryByIds queryByIds)
: base(dataConverter)
{ {
this.queryByIds = queryByIds; this.queryByIds = queryByIds;
} }

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

@ -15,11 +15,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
internal sealed class QueryReferrers : OperationBase internal sealed class QueryReferrers : OperationBase
{ {
public QueryReferrers(DataConverter dataConverter)
: base(dataConverter)
{
}
protected override Task PrepareAsync(CancellationToken ct = default) protected override Task PrepareAsync(CancellationToken ct = default)
{ {
var index = var index =

8
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryScheduled.cs

@ -18,11 +18,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
internal sealed class QueryScheduled : OperationBase internal sealed class QueryScheduled : OperationBase
{ {
public QueryScheduled(DataConverter dataConverter)
: base(dataConverter)
{
}
protected override Task PrepareAsync(CancellationToken ct = default) protected override Task PrepareAsync(CancellationToken ct = default)
{ {
var index = var index =
@ -37,8 +32,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{ {
Guard.NotNull(callback, nameof(callback)); Guard.NotNull(callback, nameof(callback));
return Collection.Find(x => x.ScheduledAt < now && x.IsDeleted != true) return Collection.Find(x => x.ScheduledAt < now && x.IsDeleted != true).Not(x => x.Data)
.Not(x => x.DataByIds)
.ForEachAsync(c => .ForEachAsync(c =>
{ {
callback(c); callback(c);

4
backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs

@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
{ {
SchemaId = postsId, SchemaId = postsId,
Data = Data =
new NamedContentData() new ContentData()
.AddField("title", .AddField("title",
new ContentFieldData() new ContentFieldData()
.AddValue("My first post with Squidex")) .AddValue("My first post with Squidex"))
@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
{ {
SchemaId = pagesId, SchemaId = pagesId,
Data = Data =
new NamedContentData() new ContentData()
.AddField("title", .AddField("title",
new ContentFieldData() new ContentFieldData()
.AddValue("About Me")) .AddValue("About Me"))

2
backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs

@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
{ {
ContentId = postsId.Id, ContentId = postsId.Id,
Data = Data =
new NamedContentData() new ContentData()
.AddField("firstName", .AddField("firstName",
new ContentFieldData() new ContentFieldData()
.AddValue("John")) .AddValue("John"))

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

@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
return true; return true;
} }
private void ReplaceAssetUrl(NamedContentData data) private void ReplaceAssetUrl(ContentData data)
{ {
foreach (var field in data.Values) foreach (var field in data.Values)
{ {

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateJob.cs

@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Commands
public BulkUpdateType Type { get; set; } public BulkUpdateType Type { get; set; }
public NamedContentData? Data { get; set; } public ContentData? Data { get; set; }
public string? Schema { get; set; } public string? Schema { get; set; }

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentDataCommand.cs

@ -15,6 +15,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.Commands
public bool OptimizeValidation { get; set; } public bool OptimizeValidation { get; set; }
public NamedContentData Data { get; set; } public ContentData Data { get; set; }
} }
} }

4
backend/src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs

@ -30,9 +30,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
public RefToken LastModifiedBy { get; set; } public RefToken LastModifiedBy { get; set; }
public NamedContentData Data { get; set; } public ContentData Data { get; set; }
public NamedContentData? ReferenceData { get; set; } public ContentData? ReferenceData { get; set; }
public ScheduleJob? ScheduleJob { get; set; } public ScheduleJob? ScheduleJob { get; set; }

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

@ -114,7 +114,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
IJsonValue? GetValue(NamedContentData? data, RootField field) IJsonValue? GetValue(ContentData? data, RootField field)
{ {
if (data != null && data.TryGetValue(field.Name, out var fieldValue) && fieldValue != null) if (data != null && data.TryGetValue(field.Name, out var fieldValue) && fieldValue != null)
{ {

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

@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
return Task.FromResult(Status.Draft); return Task.FromResult(Status.Draft);
} }
public Task<bool> CanPublishOnCreateAsync(ISchemaEntity schema, NamedContentData data, ClaimsPrincipal user) public Task<bool> CanPublishOnCreateAsync(ISchemaEntity schema, ContentData data, ClaimsPrincipal user)
{ {
return Task.FromResult(true); return Task.FromResult(true);
} }

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

@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
} }
[IgnoreDataMember] [IgnoreDataMember]
public NamedContentData Data public ContentData Data
{ {
get { return NewVersion?.Data ?? CurrentVersion.Data; } get { return NewVersion?.Data ?? CurrentVersion.Data; }
} }

4
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs

@ -264,7 +264,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
} }
} }
private async Task<object> UpdateAsync(ContentUpdateCommand command, Func<NamedContentData, NamedContentData> newDataFunc, bool partial) private async Task<object> UpdateAsync(ContentUpdateCommand command, Func<ContentData, ContentData> newDataFunc, bool partial)
{ {
var currentData = Snapshot.Data; var currentData = Snapshot.Data;
@ -337,7 +337,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
Raise(command, new ContentDraftDeleted()); Raise(command, new ContentDraftDeleted());
} }
public void Update(ContentCommand command, NamedContentData data) public void Update(ContentCommand command, ContentData data)
{ {
Raise(command, new ContentUpdated { Data = data }); Raise(command, new ContentUpdated { Data = data });
} }

14
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentOperationContext.cs

@ -111,14 +111,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
return contentWorkflow.GetInitialStatusAsync(schema); return contentWorkflow.GetInitialStatusAsync(schema);
} }
public Task GenerateDefaultValuesAsync(NamedContentData data) public Task GenerateDefaultValuesAsync(ContentData data)
{ {
data.GenerateDefaultValues(schema.SchemaDef, Partition()); data.GenerateDefaultValues(schema.SchemaDef, Partition());
return Task.CompletedTask; return Task.CompletedTask;
} }
public async Task ValidateInputAsync(NamedContentData data, bool publish) public async Task ValidateInputAsync(ContentData data, bool publish)
{ {
var validator = var validator =
new ContentValidator(Partition(), new ContentValidator(Partition(),
@ -129,7 +129,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
CheckErrors(validator); CheckErrors(validator);
} }
public async Task ValidateInputPartialAsync(NamedContentData data) public async Task ValidateInputPartialAsync(ContentData data)
{ {
var validator = var validator =
new ContentValidator(Partition(), new ContentValidator(Partition(),
@ -140,7 +140,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
CheckErrors(validator); CheckErrors(validator);
} }
public async Task ValidateContentAsync(NamedContentData data) public async Task ValidateContentAsync(ContentData data)
{ {
var validator = var validator =
new ContentValidator(Partition(), new ContentValidator(Partition(),
@ -151,7 +151,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
CheckErrors(validator); CheckErrors(validator);
} }
public async Task ValidateContentAndInputAsync(NamedContentData data) public async Task ValidateContentAndInputAsync(ContentData data)
{ {
var validator = var validator =
new ContentValidator(Partition(), new ContentValidator(Partition(),
@ -163,7 +163,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
CheckErrors(validator); CheckErrors(validator);
} }
public Task ValidateOnPublishAsync(NamedContentData data) public Task ValidateOnPublishAsync(ContentData data)
{ {
if (!schema.SchemaDef.Properties.ValidateOnPublish) if (!schema.SchemaDef.Properties.ValidateOnPublish)
{ {
@ -186,7 +186,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
return !string.IsNullOrWhiteSpace(GetScript(script)); return !string.IsNullOrWhiteSpace(GetScript(script));
} }
public async Task<NamedContentData> ExecuteScriptAndTransformAsync(Func<SchemaScripts, string> script, ScriptVars context) public async Task<ContentData> ExecuteScriptAndTransformAsync(Func<SchemaScripts, string> script, ScriptVars context)
{ {
Enrich(context); Enrich(context);

6
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentVersion.cs

@ -14,9 +14,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
{ {
public Status Status { get; } public Status Status { get; }
public NamedContentData Data { get; } public ContentData Data { get; }
public ContentVersion(Status status, NamedContentData data) public ContentVersion(Status status, ContentData data)
{ {
Guard.NotNull(data, nameof(data)); Guard.NotNull(data, nameof(data));
@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
return new ContentVersion(status, Data); return new ContentVersion(status, Data);
} }
public ContentVersion WithData(NamedContentData data) public ContentVersion WithData(ContentData data)
{ {
return new ContentVersion(Status, data); return new ContentVersion(Status, data);
} }

4
backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs

@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
return workflow.TryGetTransition(status, next, out var transition) && IsTrue(transition, content.Data, user); return workflow.TryGetTransition(status, next, out var transition) && IsTrue(transition, content.Data, user);
} }
public async Task<bool> CanPublishOnCreateAsync(ISchemaEntity schema, NamedContentData data, ClaimsPrincipal user) public async Task<bool> CanPublishOnCreateAsync(ISchemaEntity schema, ContentData data, ClaimsPrincipal user)
{ {
var workflow = await GetWorkflowAsync(schema.AppId.Id, schema.Id); var workflow = await GetWorkflowAsync(schema.AppId.Id, schema.Id);
@ -102,7 +102,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
return result.ToArray(); return result.ToArray();
} }
private bool IsTrue(WorkflowCondition condition, NamedContentData data, ClaimsPrincipal user) private bool IsTrue(WorkflowCondition condition, ContentData data, ClaimsPrincipal user)
{ {
if (condition?.Roles != null && user != null) if (condition?.Roles != null && user != null)
{ {

4
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentActions.cs

@ -442,11 +442,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
} }
} }
private static NamedContentData GetContentData(IResolveFieldContext c) private static ContentData GetContentData(IResolveFieldContext c)
{ {
var source = c.GetArgument<IDictionary<string, object>>("data"); var source = c.GetArgument<IDictionary<string, object>>("data");
return source.ToNamedContentData((IComplexGraphType)c.FieldDefinition.Arguments.Find("data").Flatten()); return source.ToContentData((IComplexGraphType)c.FieldDefinition.Arguments.Find("data").Flatten());
} }
private static IFieldResolver ResolveAsync<T>(NamedId<DomainId> appId, NamedId<DomainId> schemaId, Func<IResolveFieldContext, ContentCommand> action) private static IFieldResolver ResolveAsync<T>(NamedId<DomainId> appId, NamedId<DomainId> schemaId, Func<IResolveFieldContext, ContentCommand> action)

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs

@ -12,7 +12,7 @@ using Squidex.Domain.Apps.Entities.Schemas;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
public sealed class ContentDataGraphType : ObjectGraphType<NamedContentData> public sealed class ContentDataGraphType : ObjectGraphType<ContentData>
{ {
public ContentDataGraphType(ISchemaEntity schema, string schemaName, string schemaType, IGraphModel model) public ContentDataGraphType(ISchemaEntity schema, string schemaName, string schemaType, IGraphModel model)
{ {

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentResolvers.cs

@ -62,7 +62,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{ {
var fieldName = field.Name; var fieldName = field.Name;
return new FuncFieldResolver<NamedContentData, IReadOnlyDictionary<string, IJsonValue>?>(c => return new FuncFieldResolver<ContentData, IReadOnlyDictionary<string, IJsonValue>?>(c =>
{ {
return c.Source?.GetOrDefault(fieldName); return c.Source?.GetOrDefault(fieldName);
}); });

4
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/Converters.cs

@ -14,9 +14,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{ {
public static class Converters public static class Converters
{ {
public static NamedContentData ToNamedContentData(this IDictionary<string, object> source, IComplexGraphType type) public static ContentData ToContentData(this IDictionary<string, object> source, IComplexGraphType type)
{ {
var result = new NamedContentData(); var result = new ContentData();
foreach (var field in type.Fields) foreach (var field in type.Fields)
{ {

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

@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
Status Status { get; } Status Status { get; }
NamedContentData Data { get; } ContentData Data { get; }
ScheduleJob? ScheduleJob { get; } ScheduleJob? ScheduleJob { get; }
} }

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

@ -16,7 +16,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
Task<Status> GetInitialStatusAsync(ISchemaEntity schema); Task<Status> GetInitialStatusAsync(ISchemaEntity schema);
Task<bool> CanPublishOnCreateAsync(ISchemaEntity schema, NamedContentData data, ClaimsPrincipal user); Task<bool> CanPublishOnCreateAsync(ISchemaEntity schema, ContentData data, ClaimsPrincipal user);
Task<bool> CanMoveToAsync(IContentEntity content, Status status, Status next, ClaimsPrincipal user); Task<bool> CanMoveToAsync(IContentEntity content, Status status, Status next, ClaimsPrincipal user);

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

@ -30,6 +30,6 @@ namespace Squidex.Domain.Apps.Entities.Contents
StatusInfo[]? NextStatuses { get; } StatusInfo[]? NextStatuses { get; }
NamedContentData? ReferenceData { get; } ContentData? ReferenceData { get; }
} }
} }

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

@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
foreach (var content in group) foreach (var content in group)
{ {
content.Data = content.Data.ConvertName2Name(schema.SchemaDef, converters); content.Data = content.Data.Convert(schema.SchemaDef, converters);
} }
} }
} }

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

@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
{ {
foreach (var content in contents) foreach (var content in contents)
{ {
content.ReferenceData ??= new NamedContentData(); content.ReferenceData ??= new ContentData();
var fieldReference = content.ReferenceData.GetOrAdd(field.Name, _ => new ContentFieldData())!; var fieldReference = content.ReferenceData.GetOrAdd(field.Name, _ => new ContentFieldData())!;

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

@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
{ {
foreach (var content in contents) foreach (var content in contents)
{ {
content.ReferenceData ??= new NamedContentData(); content.ReferenceData ??= new ContentData();
var fieldReference = content.ReferenceData.GetOrAdd(field.Name, _ => new ContentFieldData())!; var fieldReference = content.ReferenceData.GetOrAdd(field.Name, _ => new ContentFieldData())!;

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

@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
var schemaId = NamedId.Of(createSchema.SchemaId, createSchema.Name); var schemaId = NamedId.Of(createSchema.SchemaId, createSchema.Name);
var data = new NamedContentData(); var data = new ContentData();
var contentId = schemaId.Id; var contentId = schemaId.Id;
var content = new CreateContent var content = new CreateContent

55
backend/src/Squidex.Domain.Apps.Entities/Contents/Text/Extensions.cs

@ -6,26 +6,18 @@
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using GeoJSON.Net; using GeoJSON.Net;
using GeoJSON.Net.Geometry;
using Microsoft.Extensions.ObjectPool;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.ObjectPool;
namespace Squidex.Domain.Apps.Entities.Contents.Text namespace Squidex.Domain.Apps.Entities.Contents.Text
{ {
public static class Extensions public static class Extensions
{ {
private static readonly ObjectPool<StringBuilder> StringBuilderPool = public static Dictionary<string, GeoJSONObject>? ToGeo(this ContentData data, IJsonSerializer jsonSerializer)
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
private static readonly ObjectPool<MemoryStream> MemoryStreamPool =
new DefaultObjectPool<MemoryStream>(new DefaultPooledObjectPolicy<MemoryStream>());
public static Dictionary<string, GeoJSONObject>? ToGeo(this NamedContentData data, IJsonSerializer jsonSerializer)
{ {
Dictionary<string, GeoJSONObject>? result = null; Dictionary<string, GeoJSONObject>? result = null;
@ -35,9 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
{ {
foreach (var (key, jsonValue) in value) foreach (var (key, jsonValue) in value)
{ {
var geoJson = GetGeoJson(jsonSerializer, jsonValue); if (GeoJsonValue.TryParse(jsonValue, jsonSerializer, out var geoJson) == GeoJsonParseResult.Success)
if (geoJson != null)
{ {
result ??= new Dictionary<string, GeoJSONObject>(); result ??= new Dictionary<string, GeoJSONObject>();
result[$"{field}.{key}"] = geoJson; result[$"{field}.{key}"] = geoJson;
@ -49,40 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
return result; return result;
} }
private static GeoJSONObject? GetGeoJson(IJsonSerializer jsonSerializer, IJsonValue value) public static Dictionary<string, string>? ToTexts(this ContentData data)
{
if (value is JsonObject geoObject)
{
var stream = MemoryStreamPool.Get();
try
{
stream.Position = 0;
jsonSerializer.Serialize(geoObject, stream, true);
stream.Position = 0;
return jsonSerializer.Deserialize<GeoJSONObject>(stream, null, true);
}
catch
{
if (geoObject.TryGetValue<JsonNumber>("latitude", out var lat) &&
geoObject.TryGetValue<JsonNumber>("longitude", out var lon))
{
return new Point(new Position(lat.Value, lon.Value));
}
}
finally
{
MemoryStreamPool.Return(stream);
}
}
return null;
}
public static Dictionary<string, string>? ToTexts(this NamedContentData data)
{ {
Dictionary<string, string>? result = null; Dictionary<string, string>? result = null;
@ -115,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
{ {
foreach (var (_, sb) in languages) foreach (var (_, sb) in languages)
{ {
StringBuilderPool.Return(sb); DefaultPools.StringBuilder.Return(sb);
} }
} }
} }
@ -151,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
{ {
if (!languages.TryGetValue(language, out var sb)) if (!languages.TryGetValue(language, out var sb))
{ {
sb = StringBuilderPool.Get(); sb = DefaultPools.StringBuilder.Get();
languages[language] = sb; languages[language] = sb;
} }

4
backend/src/Squidex.Domain.Apps.Entities/Contents/Text/TextIndexingProcess.cs

@ -112,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
} }
} }
private void Create(ContentEvent @event, NamedContentData data) private void Create(ContentEvent @event, ContentData data)
{ {
var uniqueId = DomainId.Combine(@event.AppId, @event.ContentId); var uniqueId = DomainId.Combine(@event.AppId, @event.ContentId);
@ -172,7 +172,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
} }
} }
private void Update(ContentEvent @event, NamedContentData data) private void Update(ContentEvent @event, ContentData data)
{ {
var uniqueId = DomainId.Combine(@event.AppId, @event.ContentId); var uniqueId = DomainId.Combine(@event.AppId, @event.ContentId);

2
backend/src/Squidex.Domain.Apps.Events/Contents/ContentCreated.cs

@ -15,6 +15,6 @@ namespace Squidex.Domain.Apps.Events.Contents
{ {
public Status Status { get; set; } public Status Status { get; set; }
public NamedContentData Data { get; set; } public ContentData Data { get; set; }
} }
} }

2
backend/src/Squidex.Domain.Apps.Events/Contents/ContentDraftCreated.cs

@ -13,7 +13,7 @@ namespace Squidex.Domain.Apps.Events.Contents
[EventType(nameof(ContentDraftCreated))] [EventType(nameof(ContentDraftCreated))]
public sealed class ContentDraftCreated : ContentEvent public sealed class ContentDraftCreated : ContentEvent
{ {
public NamedContentData? MigratedData { get; set; } public ContentData? MigratedData { get; set; }
public Status Status { get; set; } public Status Status { get; set; }
} }

2
backend/src/Squidex.Domain.Apps.Events/Contents/ContentUpdated.cs

@ -13,7 +13,7 @@ namespace Squidex.Domain.Apps.Events.Contents
[EventType(nameof(ContentUpdated))] [EventType(nameof(ContentUpdated))]
public sealed class ContentUpdated : ContentEvent public sealed class ContentUpdated : ContentEvent
{ {
public NamedContentData Data { get; set; } public ContentData Data { get; set; }
public bool NewVersion { get; set; } public bool NewVersion { get; set; }
} }

10
backend/src/Squidex.Infrastructure.Azure/EventSourcing/StreamPosition.cs

@ -5,16 +5,12 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Text; using Squidex.Infrastructure.ObjectPool;
using Microsoft.Extensions.ObjectPool;
namespace Squidex.Infrastructure.EventSourcing namespace Squidex.Infrastructure.EventSourcing
{ {
internal sealed class StreamPosition internal sealed class StreamPosition
{ {
private static readonly ObjectPool<StringBuilder> StringBuilderPool =
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
public static readonly StreamPosition Empty = new StreamPosition(0, -1, -1); public static readonly StreamPosition Empty = new StreamPosition(0, -1, -1);
public long Timestamp { get; } public long Timestamp { get; }
@ -38,7 +34,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static implicit operator string(StreamPosition position) public static implicit operator string(StreamPosition position)
{ {
var sb = StringBuilderPool.Get(); var sb = DefaultPools.StringBuilder.Get();
try try
{ {
sb.Append(position.Timestamp); sb.Append(position.Timestamp);
@ -51,7 +47,7 @@ namespace Squidex.Infrastructure.EventSourcing
} }
finally finally
{ {
StringBuilderPool.Return(sb); DefaultPools.StringBuilder.Return(sb);
} }
} }

10
backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/StreamPosition.cs

@ -5,17 +5,13 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Text;
using Microsoft.Extensions.ObjectPool;
using MongoDB.Bson; using MongoDB.Bson;
using Squidex.Infrastructure.ObjectPool;
namespace Squidex.Infrastructure.EventSourcing namespace Squidex.Infrastructure.EventSourcing
{ {
internal sealed class StreamPosition internal sealed class StreamPosition
{ {
private static readonly ObjectPool<StringBuilder> StringBuilderPool =
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
public static readonly StreamPosition Empty = new StreamPosition(new BsonTimestamp(0, 0), -1, -1); public static readonly StreamPosition Empty = new StreamPosition(new BsonTimestamp(0, 0), -1, -1);
public BsonTimestamp Timestamp { get; } public BsonTimestamp Timestamp { get; }
@ -38,7 +34,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static implicit operator string(StreamPosition position) public static implicit operator string(StreamPosition position)
{ {
var sb = StringBuilderPool.Get(); var sb = DefaultPools.StringBuilder.Get();
try try
{ {
sb.Append(position.Timestamp.Timestamp); sb.Append(position.Timestamp.Timestamp);
@ -53,7 +49,7 @@ namespace Squidex.Infrastructure.EventSourcing
} }
finally finally
{ {
StringBuilderPool.Return(sb); DefaultPools.StringBuilder.Return(sb);
} }
} }

5
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonHelper.cs

@ -11,6 +11,7 @@ namespace Squidex.Infrastructure.MongoDb
{ {
private const string TypeBson = "§type"; private const string TypeBson = "§type";
private const string TypeJson = "$type"; private const string TypeJson = "$type";
private const string DotReplacement = "_§§_";
public static string UnescapeBson(this string value) public static string UnescapeBson(this string value)
{ {
@ -19,7 +20,7 @@ namespace Squidex.Infrastructure.MongoDb
return TypeJson; return TypeJson;
} }
return ReplaceFirstCharacter(value, '§', '$'); return ReplaceFirstCharacter(value, '§', '$').Replace(DotReplacement, ".");
} }
public static string EscapeJson(this string value) public static string EscapeJson(this string value)
@ -29,7 +30,7 @@ namespace Squidex.Infrastructure.MongoDb
return TypeBson; return TypeBson;
} }
return ReplaceFirstCharacter(value, '$', '§'); return ReplaceFirstCharacter(value, '$', '§').Replace(".", DotReplacement);
} }
private static string ReplaceFirstCharacter(string value, char toReplace, char replacement) private static string ReplaceFirstCharacter(string value, char toReplace, char replacement)

10
backend/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs

@ -47,19 +47,19 @@ namespace Squidex.Infrastructure.EventSourcing
var eventData = storedEvent.Data; var eventData = storedEvent.Data;
var payloadType = typeNameRegistry.GetType(eventData.Type); var payloadType = typeNameRegistry.GetType(eventData.Type);
var payloadObj = serializer.Deserialize<IEvent>(eventData.Payload, payloadType); var payloadValue = serializer.Deserialize<IEvent>(eventData.Payload, payloadType);
if (payloadObj is IMigrated<IEvent> migratedEvent) if (payloadValue is IMigrated<IEvent> migratedEvent)
{ {
payloadObj = migratedEvent.Migrate(); payloadValue = migratedEvent.Migrate();
if (ReferenceEquals(migratedEvent, payloadObj)) if (ReferenceEquals(migratedEvent, payloadValue))
{ {
Debug.WriteLine("Migration should return new event."); Debug.WriteLine("Migration should return new event.");
} }
} }
var envelope = new Envelope<IEvent>(payloadObj, eventData.Headers); var envelope = new Envelope<IEvent>(payloadValue, eventData.Headers);
envelope.SetEventPosition(storedEvent.EventPosition); envelope.SetEventPosition(storedEvent.EventPosition);
envelope.SetEventStreamNumber(storedEvent.EventStreamNumber); envelope.SetEventStreamNumber(storedEvent.EventStreamNumber);

22
backend/src/Squidex.Infrastructure/ObjectPool/DefaultPools.cs

@ -0,0 +1,22 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.IO;
using System.Text;
using Microsoft.Extensions.ObjectPool;
namespace Squidex.Infrastructure.ObjectPool
{
public static class DefaultPools
{
public static readonly ObjectPool<MemoryStream> MemoryStream =
new DefaultObjectPool<MemoryStream>(new MemoryStreamPooledObjectPolicy());
public static readonly ObjectPool<StringBuilder> StringBuilder =
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
}
}

36
backend/src/Squidex.Infrastructure/ObjectPool/MemoryStreamPooledObjectPolicy.cs

@ -0,0 +1,36 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.IO;
using Microsoft.Extensions.ObjectPool;
namespace Squidex.Infrastructure.ObjectPool
{
public sealed class MemoryStreamPooledObjectPolicy : PooledObjectPolicy<MemoryStream>
{
public int InitialCapacity { get; set; } = 100;
public int MaximumRetainedCapacity { get; set; } = 4 * 1024;
public override MemoryStream Create()
{
return new MemoryStream(InitialCapacity);
}
public override bool Return(MemoryStream obj)
{
if (obj.Capacity > MaximumRetainedCapacity)
{
return false;
}
obj.Position = 0;
return true;
}
}
}

8
backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -8,11 +8,6 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols> <DebugSymbols>True</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Remove="NewFolder\**" />
<EmbeddedResource Remove="NewFolder\**" />
<None Remove="NewFolder\**" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="GeoJSON.Net" Version="1.2.19" /> <PackageReference Include="GeoJSON.Net" Version="1.2.19" />
<PackageReference Include="MailKit" Version="2.10.1" /> <PackageReference Include="MailKit" Version="2.10.1" />
@ -50,4 +45,7 @@
<ItemGroup> <ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" /> <AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Pool\" />
</ItemGroup>
</Project> </Project>

1
backend/src/Squidex.Infrastructure/States/ISnapshotStore.cs

@ -6,6 +6,7 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

8
backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -433,7 +433,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ProducesResponseType(typeof(ContentsDto), 201)] [ProducesResponseType(typeof(ContentsDto), 201)]
[ApiPermissionOrAnonymous(Permissions.AppContentsCreate)] [ApiPermissionOrAnonymous(Permissions.AppContentsCreate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PostContent(string app, string name, [FromBody] NamedContentData request, [FromQuery] bool publish = false, [FromQuery] DomainId? id = null) public async Task<IActionResult> PostContent(string app, string name, [FromBody] ContentData request, [FromQuery] bool publish = false, [FromQuery] DomainId? id = null)
{ {
var command = new CreateContent { Data = request.ToCleaned(), Publish = publish }; var command = new CreateContent { Data = request.ToCleaned(), Publish = publish };
@ -530,7 +530,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ProducesResponseType(typeof(ContentsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ContentsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppContentsUpsert)] [ApiPermissionOrAnonymous(Permissions.AppContentsUpsert)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PostContent(string app, string name, DomainId id, [FromBody] NamedContentData request, [FromQuery] bool publish = false) public async Task<IActionResult> PostContent(string app, string name, DomainId id, [FromBody] ContentData request, [FromQuery] bool publish = false)
{ {
var command = new UpsertContent { ContentId = id, Data = request.ToCleaned(), Publish = publish }; var command = new UpsertContent { ContentId = id, Data = request.ToCleaned(), Publish = publish };
@ -559,7 +559,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ProducesResponseType(typeof(ContentsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ContentsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppContentsUpdate)] [ApiPermissionOrAnonymous(Permissions.AppContentsUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutContent(string app, string name, DomainId id, [FromBody] NamedContentData request) public async Task<IActionResult> PutContent(string app, string name, DomainId id, [FromBody] ContentData request)
{ {
var command = new UpdateContent { ContentId = id, Data = request.ToCleaned() }; var command = new UpdateContent { ContentId = id, Data = request.ToCleaned() };
@ -588,7 +588,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ProducesResponseType(typeof(ContentsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ContentsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppContentsUpdate)] [ApiPermissionOrAnonymous(Permissions.AppContentsUpdate)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PatchContent(string app, string name, DomainId id, [FromBody] NamedContentData request) public async Task<IActionResult> PatchContent(string app, string name, DomainId id, [FromBody] ContentData request)
{ {
var command = new PatchContent { ContentId = id, Data = request.ToCleaned() }; var command = new PatchContent { ContentId = id, Data = request.ToCleaned() };

2
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateJobDto.cs

@ -30,7 +30,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// <summary> /// <summary>
/// The data of the content when type is set to 'Upsert', 'Create', 'Update' or 'Patch. /// The data of the content when type is set to 'Upsert', 'Create', 'Update' or 'Patch.
/// </summary> /// </summary>
public NamedContentData? Data { get; set; } public ContentData? Data { get; set; }
/// <summary> /// <summary>
/// The new status when the type is set to 'ChangeStatus'. /// The new status when the type is set to 'ChangeStatus'.

2
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs

@ -46,7 +46,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// <summary> /// <summary>
/// The reference data for the frontend UI. /// The reference data for the frontend UI.
/// </summary> /// </summary>
public NamedContentData? ReferenceData { get; set; } public ContentData? ReferenceData { get; set; }
/// <summary> /// <summary>
/// The date and time when the content item has been created. /// The date and time when the content item has been created.

2
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs

@ -19,7 +19,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// The data to import. /// The data to import.
/// </summary> /// </summary>
[LocalizedRequired] [LocalizedRequired]
public List<NamedContentData> Datas { get; set; } public List<ContentData> Datas { get; set; }
/// <summary> /// <summary>
/// True to automatically publish the content. /// True to automatically publish the content.

1
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs

@ -69,7 +69,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
SimpleMapper.Map(field, SimpleMapper.Map(field,
new NestedFieldDto new NestedFieldDto
{ {
FieldId = field.Id,
Properties = properties Properties = properties
}); });

88
backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs

@ -17,7 +17,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
public void Should_remove_null_values_from_name_model_when_cleaning() public void Should_remove_null_values_from_name_model_when_cleaning()
{ {
var input = var input =
new NamedContentData() new ContentData()
.AddField("field1", null) .AddField("field1", null)
.AddField("field2", .AddField("field2",
new ContentFieldData() new ContentFieldData()
@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
var actual = input.ToCleaned(); var actual = input.ToCleaned();
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("field2", .AddField("field2",
new ContentFieldData() new ContentFieldData()
.AddValue("en", 2)); .AddValue("en", 2));
@ -35,33 +35,11 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
} }
[Fact]
public void Should_remove_null_values_from_id_model_when_cleaning()
{
var input =
new IdContentData()
.AddField(1, null)
.AddField(2,
new ContentFieldData()
.AddValue("en", 2)
.AddValue("it", null));
var actual = input.ToCleaned();
var expected =
new IdContentData()
.AddField(2,
new ContentFieldData()
.AddValue("en", 2));
Assert.Equal(expected, actual);
}
[Fact] [Fact]
public void Should_return_same_content_if_merging_same_references() public void Should_return_same_content_if_merging_same_references()
{ {
var source = var source =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1)) .AddValue("iv", 1))
@ -78,7 +56,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
public void Should_merge_two_name_models() public void Should_merge_two_name_models()
{ {
var lhs = var lhs =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1)) .AddValue("iv", 1))
@ -88,7 +66,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
.AddValue("it", 2)); .AddValue("it", 2));
var rhs = var rhs =
new NamedContentData() new ContentData()
.AddField("field2", .AddField("field2",
new ContentFieldData() new ContentFieldData()
.AddValue("it", 3) .AddValue("it", 3)
@ -98,7 +76,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
.AddValue("iv", 4)); .AddValue("iv", 4));
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1)) .AddValue("iv", 1))
@ -118,55 +96,11 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
Assert.NotSame(expected, lhs); Assert.NotSame(expected, lhs);
} }
[Fact]
public void Should_merge_two_id_models()
{
var lhs =
new IdContentData()
.AddField(1,
new ContentFieldData()
.AddValue("iv", 1))
.AddField(2,
new ContentFieldData()
.AddValue("de", 2)
.AddValue("it", 2));
var rhs =
new IdContentData()
.AddField(2,
new ContentFieldData()
.AddValue("it", 3)
.AddValue("en", 3))
.AddField(3,
new ContentFieldData()
.AddValue("iv", 4));
var expected =
new IdContentData()
.AddField(1,
new ContentFieldData()
.AddValue("iv", 1))
.AddField(2,
new ContentFieldData()
.AddValue("it", 2)
.AddValue("de", 2)
.AddValue("en", 3))
.AddField(3,
new ContentFieldData()
.AddValue("iv", 4));
var actual = lhs.MergeInto(rhs);
Assert.Equal(expected, actual);
Assert.NotSame(expected, rhs);
Assert.NotSame(expected, lhs);
}
[Fact] [Fact]
public void Should_be_equal_when_data_have_same_structure() public void Should_be_equal_when_data_have_same_structure()
{ {
var lhs = var lhs =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 2)) .AddValue("iv", 2))
@ -175,7 +109,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
.AddValue("iv", 2)); .AddValue("iv", 2));
var rhs = var rhs =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 2)) .AddValue("iv", 2))
@ -192,7 +126,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
public void Should_not_be_equal_when_data_have_not_same_structure() public void Should_not_be_equal_when_data_have_not_same_structure()
{ {
var lhs = var lhs =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 2)) .AddValue("iv", 2))
@ -201,7 +135,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
.AddValue("iv", 2)); .AddValue("iv", 2));
var rhs = var rhs =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("en", 2)) .AddValue("en", 2))
@ -233,7 +167,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
[Fact] [Fact]
public void Should_clone_named_value_and_also_children() public void Should_clone_named_value_and_also_children()
{ {
var source = new NamedContentData var source = new ContentData
{ {
["field1"] = new ContentFieldData(), ["field1"] = new ContentFieldData(),
["field2"] = new ContentFieldData() ["field2"] = new ContentFieldData()

4
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs

@ -16,8 +16,8 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
public class ContentConversionFlatTests public class ContentConversionFlatTests
{ {
private readonly NamedContentData source = private readonly ContentData source =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("de", 1) .AddValue("de", 1)

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

@ -8,7 +8,6 @@
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json.Objects;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ConvertContent namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
@ -35,54 +34,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
.UpdateField(3, f => f.Hide()); .UpdateField(3, f => f.Hide());
} }
[Fact]
public void Should_convert_name_to_id()
{
var input =
new NamedContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("en", "EN"))
.AddField("field2",
new ContentFieldData()
.AddValue("iv", 1))
.AddField("array",
new ContentFieldData()
.AddValue("iv",
JsonValue.Array(
JsonValue.Object()
.Add("nested1", 100)
.Add("nested2", 200)
.Add("invalid", 300))))
.AddField("invalid",
new ContentFieldData()
.AddValue("iv", 2));
var hideRoot = FieldConverters.ExcludeHidden;
var hideNested = FieldConverters.ForValues(ValueConverters.ForNested(ValueConverters.ExcludeHidden));
var actual = input.ConvertName2IdCloned(schema, hideRoot, hideNested);
var expected =
new IdContentData()
.AddField(1,
new ContentFieldData()
.AddValue("en", "EN"))
.AddField(7,
new ContentFieldData()
.AddValue("iv",
JsonValue.Array(
JsonValue.Object()
.Add("72", 200))));
Assert.Equal(expected, actual);
}
[Fact] [Fact]
public void Should_convert_name_to_name() public void Should_convert_name_to_name()
{ {
var input = var input =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("en", "EN")) .AddValue("en", "EN"))
@ -93,10 +49,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 2)); .AddValue("iv", 2));
var actual = input.ConvertName2Name(schema, (data, field) => field.Name == "field2" ? null : data); var actual = input.Convert(schema, (data, field) => field.Name == "field2" ? null : data);
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("field1", .AddField("field1",
new ContentFieldData() new ContentFieldData()
.AddValue("en", "EN")); .AddValue("en", "EN"));
@ -104,49 +60,6 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
} }
[Fact]
public void Should_convert_id_to_name()
{
var input =
new IdContentData()
.AddField(1,
new ContentFieldData()
.AddValue("en", "EN"))
.AddField(2,
new ContentFieldData()
.AddValue("iv", 1))
.AddField(7,
new ContentFieldData()
.AddValue("iv",
JsonValue.Array(
JsonValue.Object()
.Add("71", 100)
.Add("72", 200)
.Add("799", 300))))
.AddField(99,
new ContentFieldData()
.AddValue("iv", 2));
var hideRoot = FieldConverters.ExcludeHidden;
var hideNested = FieldConverters.ForValues(ValueConverters.ForNested(ValueConverters.ExcludeHidden));
var actual = input.ConvertId2Name(schema, hideRoot, hideNested);
var expected =
new NamedContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("en", "EN"))
.AddField("array",
new ContentFieldData()
.AddValue("iv",
JsonValue.Array(
JsonValue.Object()
.Add("nested2", 200))));
Assert.Equal(expected, actual);
}
[Fact] [Fact]
public void Should_be_equal_fields_when_they_have_same_value() public void Should_be_equal_fields_when_they_have_same_value()
{ {

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

@ -37,24 +37,6 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
[Fact]
public void Should_convert_for_value_conversion()
{
var field = Fields.Json(1, "json", Partitioning.Invariant);
var source =
new ContentFieldData()
.AddJsonValue(JsonValue.Object());
var result = FieldConverters.ForValues(ValueConverters.EncodeJson(TestUtils.DefaultSerializer))(source, field);
var expected =
new ContentFieldData()
.AddValue("iv", "e30=");
Assert.Equal(expected, result);
}
[Fact] [Fact]
public void Should_return_same_values_when_excluding_changed_types_if_all_values_are_valid() public void Should_return_same_values_when_excluding_changed_types_if_all_values_are_valid()
{ {

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

@ -21,9 +21,12 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app"); private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly DomainId id1 = DomainId.NewGuid(); private readonly DomainId id1 = DomainId.NewGuid();
private readonly DomainId id2 = DomainId.NewGuid(); private readonly DomainId id2 = DomainId.NewGuid();
private readonly RootField<StringFieldProperties> stringField = Fields.String(1, "1", Partitioning.Invariant);
private readonly RootField<JsonFieldProperties> jsonField = Fields.Json(1, "1", Partitioning.Invariant); private readonly RootField<StringFieldProperties> stringField
private readonly RootField<NumberFieldProperties> numberField = Fields.Number(1, "1", Partitioning.Invariant); = Fields.String(1, "1", Partitioning.Invariant);
private readonly RootField<NumberFieldProperties> numberField
= Fields.Number(1, "1", Partitioning.Invariant);
public ValueConvertersTests() public ValueConvertersTests()
{ {
@ -31,66 +34,6 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
.ReturnsLazily(ctx => $"url/to/{ctx.GetArgument<string>(1)}"); .ReturnsLazily(ctx => $"url/to/{ctx.GetArgument<string>(1)}");
} }
[Fact]
public void Should_encode_json_value()
{
var source = JsonValue.Object();
var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Equal(JsonValue.Create("e30="), result);
}
[Fact]
public void Should_return_same_value_if_encoding_null_value()
{
var source = JsonValue.Null;
var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Same(source, result);
}
[Fact]
public void Should_return_same_value_if_encoding_non_json_field()
{
var source = JsonValue.Create("NO-JSON");
var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, stringField);
Assert.Same(source, result);
}
[Fact]
public void Should_decode_json_values()
{
var source = JsonValue.Create("e30=");
var result = ValueConverters.DecodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Equal(JsonValue.Object(), result);
}
[Fact]
public void Should_return_same_value_if_decoding_null_value()
{
var source = JsonValue.Null;
var result = ValueConverters.DecodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Same(source, result);
}
[Fact]
public void Should_return_same_value_if_decoding_non_json_field()
{
var source = JsonValue.Null;
var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, stringField);
Assert.Same(source, result);
}
[Fact] [Fact]
public void Should_return_null_if_field_hidden() public void Should_return_null_if_field_hidden()
{ {

4
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs

@ -41,7 +41,7 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues
public void Should_enrich_with_default_values() public void Should_enrich_with_default_values()
{ {
var data = var data =
new NamedContentData() new ContentData()
.AddField("my-string", .AddField("my-string",
new ContentFieldData() new ContentFieldData()
.AddValue("de", "de-string")) .AddValue("de", "de-string"))
@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues
public void Should_also_enrich_with_default_values_when_string_is_empty() public void Should_also_enrich_with_default_values_when_string_is_empty()
{ {
var data = var data =
new NamedContentData() new ContentData()
.AddField("my-string", .AddField("my-string",
new ContentFieldData() new ContentFieldData()
.AddValue("de", string.Empty)) .AddValue("de", string.Empty))

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

@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var id2 = DomainId.NewGuid(); var id2 = DomainId.NewGuid();
var input = var input =
new NamedContentData() new ContentData()
.AddField("assets", .AddField("assets",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1.ToString(), id2.ToString()))); .AddJsonValue(JsonValue.Array(id1.ToString(), id2.ToString())));
@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var id2 = DomainId.NewGuid(); var id2 = DomainId.NewGuid();
var input = var input =
new NamedContentData() new ContentData()
.AddField("assets", .AddField("assets",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1.ToString(), id2.ToString()))); .AddJsonValue(JsonValue.Array(id1.ToString(), id2.ToString())));
@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var id2 = DomainId.NewGuid(); var id2 = DomainId.NewGuid();
var source = var source =
new NamedContentData() new ContentData()
.AddField("references", .AddField("references",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1, id2))) .AddJsonValue(JsonValue.Array(id1, id2)))
@ -91,7 +91,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
.Add("nested", JsonValue.Array(id1, id2))))); .Add("nested", JsonValue.Array(id1, id2)))));
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("references", .AddField("references",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array(id2))) .AddJsonValue(JsonValue.Array(id2)))
@ -110,7 +110,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var converter = FieldConverters.ForValues(cleaner, cleanNested); var converter = FieldConverters.ForValues(cleaner, cleanNested);
var actual = source.ConvertName2Name(schema, converter); var actual = source.Convert(schema, converter);
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
} }

6
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceFormattingTests.cs

@ -63,7 +63,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
[Fact] [Fact]
public void Should_return_default_value_if_no_value_found() public void Should_return_default_value_if_no_value_found()
{ {
var data = new NamedContentData(); var data = new ContentData();
var schema = CreateNoRefSchema(); var schema = CreateNoRefSchema();
@ -85,9 +85,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
.AddString(3, "non-ref", Partitioning.Invariant); .AddString(3, "non-ref", Partitioning.Invariant);
} }
private static NamedContentData CreateData() private static ContentData CreateData()
{ {
return new NamedContentData() return new ContentData()
.AddField("ref1", .AddField("ref1",
new ContentFieldData() new ContentFieldData()
.AddValue("en", "EN") .AddValue("en", "EN")

24
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs

@ -508,7 +508,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("country", .AddField("country",
new ContentFieldData() new ContentFieldData()
.AddValue("zh-TW", "Berlin")) .AddValue("zh-TW", "Berlin"))
@ -531,7 +531,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", "Berlin")) .AddValue("iv", "Berlin"))
@ -554,7 +554,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", "Berlin")) .AddValue("iv", "Berlin"))
@ -577,7 +577,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array())) .AddJsonValue(JsonValue.Array()))
@ -600,7 +600,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("name", "Berlin"))) .AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
@ -623,7 +623,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", "Berlin")) .AddValue("iv", "Berlin"))
@ -646,7 +646,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array("Berlin"))) .AddJsonValue(JsonValue.Array("Berlin")))
@ -669,7 +669,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("name", "Berlin"))) .AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
@ -692,7 +692,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("name", "Berlin"))) .AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
@ -715,7 +715,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array(1, 2, 3))) .AddJsonValue(JsonValue.Array(1, 2, 3)))
@ -738,7 +738,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("name", "Berlin"))) .AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
@ -792,7 +792,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("time", .AddField("time",
new ContentFieldData() new ContentFieldData()
.AddValue(JsonValue.Create("2020-06-01T10:10:20Z"))) .AddValue(JsonValue.Create("2020-06-01T10:10:20Z")))

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

@ -146,7 +146,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("city", .AddField("city",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array())) .AddJsonValue(JsonValue.Array()))
@ -285,7 +285,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent var @event = new EnrichedContentEvent
{ {
Data = Data =
new NamedContentData() new ContentData()
.AddField("categories", .AddField("categories",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array("ref1", "ref2", "ref3"))) .AddJsonValue(JsonValue.Array("ref1", "ref2", "ref3")))

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

@ -19,10 +19,10 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
[Fact] [Fact]
public void Should_update_data_when_setting_field() public void Should_update_data_when_setting_field()
{ {
var original = new NamedContentData(); var original = new ContentData();
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1.0)); .AddValue("iv", 1.0));
@ -35,10 +35,10 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
[Fact] [Fact]
public void Should_update_data_when_setting_lazy_field() public void Should_update_data_when_setting_lazy_field()
{ {
var original = new NamedContentData(); var original = new ContentData();
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1.0)); .AddValue("iv", 1.0));
@ -51,10 +51,10 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
[Fact] [Fact]
public void Should_update_data_defining_property_for_content() public void Should_update_data_defining_property_for_content()
{ {
var original = new NamedContentData(); var original = new ContentData();
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1.0)); .AddValue("iv", 1.0));
@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
[Fact] [Fact]
public void Should_throw_exception_when_assigning_non_object_as_field() public void Should_throw_exception_when_assigning_non_object_as_field()
{ {
var original = new NamedContentData(); var original = new ContentData();
Assert.Throws<JavaScriptException>(() => ExecuteScript(original, @"data.number = 1")); Assert.Throws<JavaScriptException>(() => ExecuteScript(original, @"data.number = 1"));
} }
@ -76,12 +76,12 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_update_data_when_deleting_field() public void Should_update_data_when_deleting_field()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1.0)); .AddValue("iv", 1.0));
var expected = new NamedContentData(); var expected = new ContentData();
var result = ExecuteScript(original, @"delete data.number"); var result = ExecuteScript(original, @"delete data.number");
@ -92,13 +92,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_update_data_when_setting_field_value_with_string() public void Should_update_data_when_setting_field_value_with_string()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("string", .AddField("string",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", "1")); .AddValue("iv", "1"));
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("string", .AddField("string",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", "1new")); .AddValue("iv", "1new"));
@ -112,13 +112,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_update_data_when_setting_field_value_with_number() public void Should_update_data_when_setting_field_value_with_number()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1.0)); .AddValue("iv", 1.0));
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 3.0)); .AddValue("iv", 3.0));
@ -132,13 +132,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_update_data_when_setting_field_value_with_boolean() public void Should_update_data_when_setting_field_value_with_boolean()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("boolean", .AddField("boolean",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", false)); .AddValue("iv", false));
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("boolean", .AddField("boolean",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", true)); .AddValue("iv", true));
@ -152,13 +152,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_update_data_when_setting_field_value_with_array() public void Should_update_data_when_setting_field_value_with_array()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array(1.0, 2.0))); .AddJsonValue(JsonValue.Array(1.0, 2.0)));
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array(1.0, 4.0, 5.0))); .AddJsonValue(JsonValue.Array(1.0, 4.0, 5.0)));
@ -172,13 +172,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_update_data_when_setting_field_value_with_object() public void Should_update_data_when_setting_field_value_with_object()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("lat", 1.0))); .AddJsonValue(JsonValue.Object().Add("lat", 1.0)));
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("lat", 1.0).Add("lon", 4.0))); .AddJsonValue(JsonValue.Object().Add("lat", 1.0).Add("lon", 4.0)));
@ -192,12 +192,12 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_throw_when_defining_property_for_field() public void Should_throw_when_defining_property_for_field()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData()); new ContentFieldData());
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("number", .AddField("number",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1.0)); .AddValue("iv", 1.0));
@ -211,13 +211,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_update_data_when_deleting_field_value() public void Should_update_data_when_deleting_field_value()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("string", .AddField("string",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", "hello")); .AddValue("iv", "hello"));
var expected = var expected =
new NamedContentData() new ContentData()
.AddField("string", .AddField("string",
new ContentFieldData()); new ContentFieldData());
@ -230,7 +230,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_be_able_to_iterate_over_fields() public void Should_be_able_to_iterate_over_fields()
{ {
var content = var content =
new NamedContentData() new ContentData()
.AddField("f1", .AddField("f1",
new ContentFieldData() new ContentFieldData()
.AddValue("v11", "1") .AddValue("v11", "1")
@ -262,7 +262,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_throw_exceptions_when_changing_objects() public void Should_throw_exceptions_when_changing_objects()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("obj", .AddField("obj",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("readonly", 1))); .AddJsonValue(JsonValue.Object().Add("readonly", 1)));
@ -275,7 +275,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_not_throw_exceptions_when_changing_arrays() public void Should_not_throw_exceptions_when_changing_arrays()
{ {
var original = var original =
new NamedContentData() new ContentData()
.AddField("obj", .AddField("obj",
new ContentFieldData() new ContentFieldData()
.AddJsonValue(JsonValue.Array())); .AddJsonValue(JsonValue.Array()));
@ -286,10 +286,10 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
[Fact] [Fact]
public void Should_null_propagate_unknown_fields() public void Should_null_propagate_unknown_fields()
{ {
ExecuteScript(new NamedContentData(), @"data.string.iv = 'hello'"); ExecuteScript(new ContentData(), @"data.string.iv = 'hello'");
} }
private static NamedContentData ExecuteScript(NamedContentData original, string script) private static ContentData ExecuteScript(ContentData original, string script)
{ {
var engine = new Engine(o => o.Strict()); var engine = new Engine(o => o.Strict());

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save