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);
ruleJob.Data = jsonSerializer.Deserialize<NamedContentData>(json);
ruleJob.Data = jsonSerializer.Deserialize<ContentData>(json);
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)
{
if (value is NamedContentData data)
if (value is ContentData data)
{
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;

27
backend/src/Migrations/MigrationPath.cs

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

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

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

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

@ -18,7 +18,7 @@ namespace Migrations.OldEvents
[Obsolete("New Event introduced")]
public sealed class ContentUpdateProposed : ContentEvent, IMigrated<IEvent>
{
public NamedContentData Data { get; set; }
public ContentData Data { get; set; }
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
{
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); }
}
protected ContentData(IEqualityComparer<T> comparer)
: base(comparer)
public ContentData()
: base(StringComparer.Ordinal)
{
}
protected ContentData(ContentData<T> source, IEqualityComparer<T> comparer)
: base(source, comparer)
public ContentData(ContentData source)
: base(source, StringComparer.Ordinal)
{
}
protected ContentData(int capacity, IEqualityComparer<T> comparer)
: base(capacity, comparer)
public ContentData(int capacity)
: 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));
@ -66,9 +75,21 @@ namespace Squidex.Domain.Apps.Core.Contents
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)
{
@ -91,10 +112,10 @@ namespace Squidex.Domain.Apps.Core.Contents
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));
}
@ -103,5 +124,22 @@ namespace Squidex.Domain.Apps.Core.Contents
{
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 NamedContentData Data { get; set; }
public ContentData Data { get; set; }
public NamedContentData? DataOld { get; set; }
public ContentData? DataOld { 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.
// ==========================================================================
using System.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ConvertContent
{
public 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));
var result = new NamedContentData(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);
var result = new ContentData(content.Count);
foreach (var (fieldName, data) in content)
{
@ -75,40 +42,6 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
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)
{
if (converters != null)
@ -126,43 +59,5 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
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 Dictionary<string, object?> ToFlatten(this NamedContentData content)
public static Dictionary<string, object?> ToFlatten(this ContentData content)
{
var result = new Dictionary<string, object?>();
@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
return result;
}
public static FlatContentData ToFlatten(this NamedContentData content, string fallback)
public static FlatContentData ToFlatten(this ContentData content, string fallback)
{
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
{
var (_, error) = JsonValueConverter.ConvertValue(field, value, jsonSerializer);
if (error != null)
if (!JsonValueValidator.IsValid(field, value, jsonSerializer))
{
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.Collections.Generic;
using System.Linq;
using System.Text;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure;
@ -39,9 +38,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
try
{
var (_, error) = JsonValueConverter.ConvertValue(field, value, jsonSerializer);
if (error != null)
if (!JsonValueValidator.IsValid(field, value, jsonSerializer))
{
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)
{
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 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);

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

@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Core.DefaultValues
this.partitionResolver = partitionResolver;
}
public void Enrich(NamedContentData data)
public void Enrich(ContentData 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 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));
@ -27,14 +27,14 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
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));
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(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(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)
{
@ -82,7 +82,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
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(partitioning, nameof(partitioning));
@ -97,7 +97,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
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));

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];
if (current is NamedContentData data)
if (current is ContentData data)
{
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
{
private readonly NamedContentData contentData;
private readonly ContentData contentData;
private HashSet<string> fieldsToDelete;
private Dictionary<string, PropertyDescriptor> fieldProperties;
private bool isChanged;
public override bool Extensible => true;
public ContentDataObject(Engine engine, NamedContentData contentData)
public ContentDataObject(Engine engine, ContentData contentData)
: base(engine)
{
this.contentData = contentData;
@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper
isChanged = true;
}
public bool TryUpdate(out NamedContentData result)
public bool TryUpdate(out ContentData result)
{
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:
result = status.ToString();
return true;
case NamedContentData content:
case ContentData content:
result = new ContentDataObject(engine, content);
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<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);

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.NotNullOrEmpty(script, nameof(script));
using (var cts = new CancellationTokenSource(TimeoutExecution))
{
var tcs = new TaskCompletionSource<NamedContentData>();
var tcs = new TaskCompletionSource<ContentData>();
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);
}
public NamedContentData? Data
public ContentData? Data
{
get => GetValue<NamedContentData?>();
get => GetValue<ContentData?>();
set => SetValue(value);
}
public NamedContentData? DataOld
public ContentData? DataOld
{
get => GetValue<NamedContentData?>();
get => GetValue<ContentData?>();
set
{
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 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(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(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)
{

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<JsonNull>(x => FluidValue.Create(null));
memberAccessStrategy.Register<NamedContentData, object?>(
memberAccessStrategy.Register<ContentData, object?>(
(value, name) => value.GetOrDefault(name));
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));
}
public Task ValidateInputPartialAsync(NamedContentData data)
public Task ValidateInputPartialAsync(ContentData data)
{
Guard.NotNull(data, nameof(data));
@ -62,7 +62,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
return validator.ValidateAsync(data, context, AddError);
}
public Task ValidateInputAsync(NamedContentData data)
public Task ValidateInputAsync(ContentData data)
{
Guard.NotNull(data, nameof(data));
@ -71,7 +71,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
return validator.ValidateAsync(data, context, AddError);
}
public Task ValidateContentAsync(NamedContentData data)
public Task ValidateContentAsync(ContentData 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.IO;
using GeoJSON.Net;
using GeoJSON.Net.Geometry;
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;
using Squidex.Infrastructure.Translations;
using Squidex.Infrastructure.Validation;
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)
{
if (args.Value is JsonObject geoObject)
{
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")));
}
var result = GeoJsonValue.TryParse(args.Value, args.JsonSerializer, out var value);
if (geoObject.TryGetValue<JsonNumber>("longitude", out var lon))
{
if (!lon.Value.IsBetween(-180, 180))
{
return (null, new JsonError(T.Get("contents.invalidGeolocationLongitude")));
}
}
else
{
return (null, new JsonError(T.Get("contents.invalidGeolocation")));
}
var position = new Point(new Position(lat.Value, lon.Value));
return (position, null);
}
switch (result)
{
case GeoJsonParseResult.InvalidLatitude:
return (null, new JsonError(T.Get("contents.invalidGeolocationLatitude")));
case GeoJsonParseResult.InvalidLongitude:
return (null, new JsonError(T.Get("contents.invalidGeolocationLongitude")));
case GeoJsonParseResult.InvalidValue:
return (null, new JsonError(T.Get("contents.invalidGeolocation")));
default:
return (value, null);
}
return (null, new JsonError(T.Get("contents.invalidGeolocation")));
}
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)
{
if (item is JsonNull)
{
result.Add(null);
}
else if (item is JsonString s)
if (item is JsonString s && !string.IsNullOrWhiteSpace(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.Sort = query.Sort
.Select(x =>
new SortNode(
x.Path.ToFirstPascalCase(),
x.Order))
.ToList();
if (query.Sort != null)
{
query.Sort = query.Sort.Select(x => new SortNode(x.Path.ToFirstPascalCase(), x.Order)).ToList();
}
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 string name;
public MongoContentCollection(string name, IMongoDatabase database, IAppProvider appProvider, DataConverter dataConverter)
public MongoContentCollection(string name, IMongoDatabase database, IAppProvider appProvider)
: base(database)
{
this.name = name;
queryAsStream = new QueryAsStream(dataConverter, appProvider);
queryBdId = new QueryById(dataConverter);
queryByIds = new QueryByIds(dataConverter);
queryByQuery = new QueryByQuery(dataConverter, appProvider);
queryReferences = new QueryReferences(dataConverter, queryByIds);
queryReferrers = new QueryReferrers(dataConverter);
queryScheduled = new QueryScheduled(dataConverter);
queryAsStream = new QueryAsStream();
queryBdId = new QueryById();
queryByIds = new QueryByIds();
queryByQuery = new QueryByQuery(appProvider);
queryReferences = new QueryReferences(queryByIds);
queryReferrers = new QueryReferrers();
queryScheduled = new QueryScheduled();
}
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.Linq;
using MongoDB.Bson.Serialization.Attributes;
using NodaTime;
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.MongoDb.Contents.Operations;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
@ -22,8 +18,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
[BsonIgnoreExtraElements]
public sealed class MongoContentEntity : IContentEntity, IVersionedEntity<DomainId>
{
private NamedContentData data;
[BsonId]
[BsonElement("_id")]
public DomainId DocumentId { get; set; }
@ -63,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
[BsonIgnoreIfNull]
[BsonElement("do")]
[BsonJson]
public IdContentData DataByIds { get; set; }
public ContentData Data { get; set; }
[BsonIgnoreIfNull]
[BsonElement("sa")]
@ -97,27 +91,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
[BsonElement("mb")]
public RefToken LastModifiedBy { get; set; }
[BsonIgnore]
public NamedContentData Data
{
get { return data; }
}
public DomainId UniqueId
{
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.Contents;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Hosting;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Queries;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
public partial class MongoContentRepository : IContentRepository, IInitializable
{
private readonly IAppProvider appProvider;
private readonly DataConverter converter;
private readonly MongoContentCollection collectionAll;
private readonly MongoContentCollection collectionPublished;
@ -36,22 +32,17 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
StatusSerializer.Register();
}
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider, IJsonSerializer serializer)
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider)
{
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(serializer, nameof(serializer));
this.appProvider = appProvider;
converter = new DataConverter(serializer);
collectionAll =
new MongoContentCollection(
"States_Contents_All2", database, appProvider, converter);
"States_Contents_All3", database, appProvider);
collectionPublished =
new MongoContentCollection(
"States_Contents_Published2", database, appProvider, converter);
"States_Contents_Published3", database, appProvider);
}
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 Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.DomainObject;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States;
@ -51,15 +50,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
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);
}
@ -76,25 +66,17 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
return;
}
var schema = await GetSchemaAsync(value.AppId.Id, value.SchemaId.Id);
if (schema == null)
{
return;
}
var saveDraft = UpsertDraftContentAsync(value, oldVersion, newVersion, schema);
var savePublic = UpsertOrDeletePublishedAsync(value, oldVersion, newVersion, schema);
await Task.WhenAll(saveDraft, savePublic);
await Task.WhenAll(
UpsertDraftContentAsync(value, oldVersion, newVersion),
UpsertOrDeletePublishedAsync(value, oldVersion, newVersion));
}
}
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)
{
await UpsertPublishedContentAsync(value, oldVersion, newVersion, schema);
await UpsertPublishedContentAsync(value, oldVersion, newVersion);
}
else
{
@ -109,7 +91,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
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
{
@ -123,12 +105,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
content.ScheduleJob = value.ScheduleJob;
content.NewStatus = value.NewStatus;
content.LoadData(value.Data, schema.SchemaDef, converter);
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
{
@ -142,16 +122,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
content.ScheduleJob = null;
content.NewStatus = null;
content.LoadData(value.CurrentVersion.Data, schema.SchemaDef, converter);
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.Collections.Generic;
using System.Linq;
using System.Reflection;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization;
using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Queries;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
public static class Adapt
{
private static readonly Dictionary<string, string> PropertyMap =
typeof(MongoContentEntity).GetProperties()
.ToDictionary(
ToElementName,
ToName,
StringComparer.OrdinalIgnoreCase);
private static Dictionary<string, PropertyPath> pathMap;
private static Dictionary<string, string> propertyMap;
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 x.Name;
return propertyMap;
}
}
public static Func<PropertyPath, PropertyPath> Path(Schema? schema)
public static IReadOnlyDictionary<string, PropertyPath> PathMap
{
return propertyNames =>
get
{
var result = new List<string>(propertyNames);
if (result.Count > 1 && schema != null)
if (pathMap == null)
{
var rootEdmName = result[1].UnescapeEdmField();
var rootField = schema.FieldsByName[rootEdmName];
pathMap = PropertyMap.ToDictionary(x => x.Key, x => (PropertyPath)x.Value);
}
result[1] = rootField.Id.ToString();
return pathMap;
}
}
if (rootField is IArrayField arrayField && result.Count > 3)
{
var nestedEdmName = result[3].UnescapeEdmField();
var nestedField = arrayField.FieldsByName[nestedEdmName];
public static PropertyPath MapPath(PropertyPath path)
{
if (path.Count == 1 && PathMap.TryGetValue(path[0], out var mappedPath))
{
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)
{
if (result[0].Equals("Data", StringComparison.CurrentCultureIgnoreCase))
{
result[0] = "do";
}
else
{
result[0] = PropertyMap[propertyNames[0]];
}
}
for (var i = 1; i < path.Count; i++)
{
result[i] = result[i].UnescapeEdmField().EscapeJson();
}
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)
{
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;
}
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.Adapt(filter, pathConverter, appId);
return AdaptionVisitor.AdaptFilter(filter, 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 readonly Func<PropertyPath, PropertyPath> PathConverter;
public readonly DomainId AppId;
public Args(Func<PropertyPath, PropertyPath> pathConverter, DomainId appId)
public Args(DomainId appId)
{
PathConverter = pathConverter;
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);
}
@ -74,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
}
else
{
path = args.PathConverter(path);
path = Adapt.MapPath(path);
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 DataConverter DataConverter { get; }
protected OperationBase(DataConverter dataConverter)
{
DataConverter = dataConverter;
}
public Task PrepareAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default)
{
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
{
private readonly IAppProvider appProvider;
public QueryAsStream(DataConverter converter, IAppProvider appProvider)
: base(converter)
{
this.appProvider = appProvider;
}
protected override async Task PrepareAsync(CancellationToken ct = default)
{
var indexBySchema =
@ -48,14 +40,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
foreach (var entity in cursor.Current)
{
var schema = await appProvider.GetSchemaAsync(appId, entity.SchemaId.Id, false);
if (schema != null)
{
entity.ParseData(schema.SchemaDef, DataConverter);
yield return entity;
}
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
{
public QueryById(DataConverter dataConverter)
: base(dataConverter)
{
}
public async Task<IContentEntity?> QueryAsync(ISchemaEntity schema, DomainId id)
{
Guard.NotNull(schema, nameof(schema));
@ -36,8 +31,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
return null;
}
contentEntity?.ParseData(schema.SchemaDef, DataConverter);
}
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
{
public QueryByIds(DataConverter dataConverter)
: base(dataConverter)
{
}
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id, Status Status)>> QueryIdsAsync(DomainId appId, HashSet<DomainId> ids)
{
if (ids == null || ids.Count == 0)
@ -59,15 +54,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
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);

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 QueryByQuery(DataConverter dataConverter, IAppProvider appProvider)
: base(dataConverter)
public QueryByQuery(IAppProvider 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)>();
}
var filter = BuildFilter(appId, schemaId, filterNode.AdjustToModel(appId, schema.SchemaDef));
var filter = BuildFilter(appId, schemaId, filterNode.AdjustToModel(appId));
var contentItems = await Collection.FindStatusAsync(filter);
@ -99,7 +98,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
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);
@ -112,13 +111,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
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);
@ -141,7 +133,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
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);
@ -154,11 +146,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
contentTotal = await Collection.Find(filter).CountDocumentsAsync();
}
foreach (var entity in contentEntities)
{
entity.ParseData(schema.SchemaDef, DataConverter);
}
}
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 QueryReferences(DataConverter dataConverter, QueryByIds queryByIds)
: base(dataConverter)
public QueryReferences(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
{
public QueryReferrers(DataConverter dataConverter)
: base(dataConverter)
{
}
protected override Task PrepareAsync(CancellationToken ct = default)
{
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
{
public QueryScheduled(DataConverter dataConverter)
: base(dataConverter)
{
}
protected override Task PrepareAsync(CancellationToken ct = default)
{
var index =
@ -37,8 +32,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
Guard.NotNull(callback, nameof(callback));
return Collection.Find(x => x.ScheduledAt < now && x.IsDeleted != true)
.Not(x => x.DataByIds)
return Collection.Find(x => x.ScheduledAt < now && x.IsDeleted != true).Not(x => x.Data)
.ForEachAsync(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,
Data =
new NamedContentData()
new ContentData()
.AddField("title",
new ContentFieldData()
.AddValue("My first post with Squidex"))
@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
{
SchemaId = pagesId,
Data =
new NamedContentData()
new ContentData()
.AddField("title",
new ContentFieldData()
.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,
Data =
new NamedContentData()
new ContentData()
.AddField("firstName",
new ContentFieldData()
.AddValue("John"))

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

@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
return true;
}
private void ReplaceAssetUrl(NamedContentData data)
private void ReplaceAssetUrl(ContentData data)
{
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 NamedContentData? Data { get; set; }
public ContentData? Data { 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 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 NamedContentData Data { get; set; }
public ContentData Data { get; set; }
public NamedContentData? ReferenceData { get; set; }
public ContentData? ReferenceData { 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();
IJsonValue? GetValue(NamedContentData? data, RootField field)
IJsonValue? GetValue(ContentData? data, RootField field)
{
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);
}
public Task<bool> CanPublishOnCreateAsync(ISchemaEntity schema, NamedContentData data, ClaimsPrincipal user)
public Task<bool> CanPublishOnCreateAsync(ISchemaEntity schema, ContentData data, ClaimsPrincipal user)
{
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]
public NamedContentData Data
public ContentData 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;
@ -337,7 +337,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
Raise(command, new ContentDraftDeleted());
}
public void Update(ContentCommand command, NamedContentData data)
public void Update(ContentCommand command, ContentData 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);
}
public Task GenerateDefaultValuesAsync(NamedContentData data)
public Task GenerateDefaultValuesAsync(ContentData data)
{
data.GenerateDefaultValues(schema.SchemaDef, Partition());
return Task.CompletedTask;
}
public async Task ValidateInputAsync(NamedContentData data, bool publish)
public async Task ValidateInputAsync(ContentData data, bool publish)
{
var validator =
new ContentValidator(Partition(),
@ -129,7 +129,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
CheckErrors(validator);
}
public async Task ValidateInputPartialAsync(NamedContentData data)
public async Task ValidateInputPartialAsync(ContentData data)
{
var validator =
new ContentValidator(Partition(),
@ -140,7 +140,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
CheckErrors(validator);
}
public async Task ValidateContentAsync(NamedContentData data)
public async Task ValidateContentAsync(ContentData data)
{
var validator =
new ContentValidator(Partition(),
@ -151,7 +151,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
CheckErrors(validator);
}
public async Task ValidateContentAndInputAsync(NamedContentData data)
public async Task ValidateContentAndInputAsync(ContentData data)
{
var validator =
new ContentValidator(Partition(),
@ -163,7 +163,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
CheckErrors(validator);
}
public Task ValidateOnPublishAsync(NamedContentData data)
public Task ValidateOnPublishAsync(ContentData data)
{
if (!schema.SchemaDef.Properties.ValidateOnPublish)
{
@ -186,7 +186,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
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);

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 NamedContentData Data { get; }
public ContentData Data { get; }
public ContentVersion(Status status, NamedContentData data)
public ContentVersion(Status status, ContentData data)
{
Guard.NotNull(data, nameof(data));
@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
return new ContentVersion(status, Data);
}
public ContentVersion WithData(NamedContentData data)
public ContentVersion WithData(ContentData 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);
}
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);
@ -102,7 +102,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
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)
{

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");
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)

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
{
public sealed class ContentDataGraphType : ObjectGraphType<NamedContentData>
public sealed class ContentDataGraphType : ObjectGraphType<ContentData>
{
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;
return new FuncFieldResolver<NamedContentData, IReadOnlyDictionary<string, IJsonValue>?>(c =>
return new FuncFieldResolver<ContentData, IReadOnlyDictionary<string, IJsonValue>?>(c =>
{
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 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)
{

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

@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
Status Status { get; }
NamedContentData Data { get; }
ContentData Data { 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<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);

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

@ -30,6 +30,6 @@ namespace Squidex.Domain.Apps.Entities.Contents
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)
{
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)
{
content.ReferenceData ??= new NamedContentData();
content.ReferenceData ??= new ContentData();
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)
{
content.ReferenceData ??= new NamedContentData();
content.ReferenceData ??= new ContentData();
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 data = new NamedContentData();
var data = new ContentData();
var contentId = schemaId.Id;
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.IO;
using System.Text;
using GeoJSON.Net;
using GeoJSON.Net.Geometry;
using Microsoft.Extensions.ObjectPool;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.ObjectPool;
namespace Squidex.Domain.Apps.Entities.Contents.Text
{
public static class Extensions
{
private static readonly ObjectPool<StringBuilder> StringBuilderPool =
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)
public static Dictionary<string, GeoJSONObject>? ToGeo(this ContentData data, IJsonSerializer jsonSerializer)
{
Dictionary<string, GeoJSONObject>? result = null;
@ -35,9 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
{
foreach (var (key, jsonValue) in value)
{
var geoJson = GetGeoJson(jsonSerializer, jsonValue);
if (geoJson != null)
if (GeoJsonValue.TryParse(jsonValue, jsonSerializer, out var geoJson) == GeoJsonParseResult.Success)
{
result ??= new Dictionary<string, GeoJSONObject>();
result[$"{field}.{key}"] = geoJson;
@ -49,40 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
return result;
}
private static GeoJSONObject? GetGeoJson(IJsonSerializer jsonSerializer, IJsonValue value)
{
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)
public static Dictionary<string, string>? ToTexts(this ContentData data)
{
Dictionary<string, string>? result = null;
@ -115,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text
{
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))
{
sb = StringBuilderPool.Get();
sb = DefaultPools.StringBuilder.Get();
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);
@ -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);

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 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))]
public sealed class ContentDraftCreated : ContentEvent
{
public NamedContentData? MigratedData { get; set; }
public ContentData? MigratedData { 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))]
public sealed class ContentUpdated : ContentEvent
{
public NamedContentData Data { get; set; }
public ContentData Data { 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.
// ==========================================================================
using System.Text;
using Microsoft.Extensions.ObjectPool;
using Squidex.Infrastructure.ObjectPool;
namespace Squidex.Infrastructure.EventSourcing
{
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 long Timestamp { get; }
@ -38,7 +34,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static implicit operator string(StreamPosition position)
{
var sb = StringBuilderPool.Get();
var sb = DefaultPools.StringBuilder.Get();
try
{
sb.Append(position.Timestamp);
@ -51,7 +47,7 @@ namespace Squidex.Infrastructure.EventSourcing
}
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.
// ==========================================================================
using System.Text;
using Microsoft.Extensions.ObjectPool;
using MongoDB.Bson;
using Squidex.Infrastructure.ObjectPool;
namespace Squidex.Infrastructure.EventSourcing
{
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 BsonTimestamp Timestamp { get; }
@ -38,7 +34,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static implicit operator string(StreamPosition position)
{
var sb = StringBuilderPool.Get();
var sb = DefaultPools.StringBuilder.Get();
try
{
sb.Append(position.Timestamp.Timestamp);
@ -53,7 +49,7 @@ namespace Squidex.Infrastructure.EventSourcing
}
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 TypeJson = "$type";
private const string DotReplacement = "_§§_";
public static string UnescapeBson(this string value)
{
@ -19,7 +20,7 @@ namespace Squidex.Infrastructure.MongoDb
return TypeJson;
}
return ReplaceFirstCharacter(value, '§', '$');
return ReplaceFirstCharacter(value, '§', '$').Replace(DotReplacement, ".");
}
public static string EscapeJson(this string value)
@ -29,7 +30,7 @@ namespace Squidex.Infrastructure.MongoDb
return TypeBson;
}
return ReplaceFirstCharacter(value, '$', '§');
return ReplaceFirstCharacter(value, '$', '§').Replace(".", DotReplacement);
}
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 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.");
}
}
var envelope = new Envelope<IEvent>(payloadObj, eventData.Headers);
var envelope = new Envelope<IEvent>(payloadValue, eventData.Headers);
envelope.SetEventPosition(storedEvent.EventPosition);
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>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Compile Remove="NewFolder\**" />
<EmbeddedResource Remove="NewFolder\**" />
<None Remove="NewFolder\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="GeoJSON.Net" Version="1.2.19" />
<PackageReference Include="MailKit" Version="2.10.1" />
@ -50,4 +45,7 @@
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
<ItemGroup>
<Folder Include="Pool\" />
</ItemGroup>
</Project>

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

@ -6,6 +6,7 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading;
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)]
[ApiPermissionOrAnonymous(Permissions.AppContentsCreate)]
[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 };
@ -530,7 +530,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ProducesResponseType(typeof(ContentsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppContentsUpsert)]
[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 };
@ -559,7 +559,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ProducesResponseType(typeof(ContentsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppContentsUpdate)]
[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() };
@ -588,7 +588,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ProducesResponseType(typeof(ContentsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(Permissions.AppContentsUpdate)]
[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() };

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

@ -30,7 +30,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// <summary>
/// The data of the content when type is set to 'Upsert', 'Create', 'Update' or 'Patch.
/// </summary>
public NamedContentData? Data { get; set; }
public ContentData? Data { get; set; }
/// <summary>
/// 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>
/// The reference data for the frontend UI.
/// </summary>
public NamedContentData? ReferenceData { get; set; }
public ContentData? ReferenceData { get; set; }
/// <summary>
/// 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.
/// </summary>
[LocalizedRequired]
public List<NamedContentData> Datas { get; set; }
public List<ContentData> Datas { get; set; }
/// <summary>
/// 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,
new NestedFieldDto
{
FieldId = field.Id,
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()
{
var input =
new NamedContentData()
new ContentData()
.AddField("field1", null)
.AddField("field2",
new ContentFieldData()
@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
var actual = input.ToCleaned();
var expected =
new NamedContentData()
new ContentData()
.AddField("field2",
new ContentFieldData()
.AddValue("en", 2));
@ -35,33 +35,11 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
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]
public void Should_return_same_content_if_merging_same_references()
{
var source =
new NamedContentData()
new ContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("iv", 1))
@ -78,7 +56,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
public void Should_merge_two_name_models()
{
var lhs =
new NamedContentData()
new ContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("iv", 1))
@ -88,7 +66,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
.AddValue("it", 2));
var rhs =
new NamedContentData()
new ContentData()
.AddField("field2",
new ContentFieldData()
.AddValue("it", 3)
@ -98,7 +76,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
.AddValue("iv", 4));
var expected =
new NamedContentData()
new ContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("iv", 1))
@ -118,55 +96,11 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
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]
public void Should_be_equal_when_data_have_same_structure()
{
var lhs =
new NamedContentData()
new ContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("iv", 2))
@ -175,7 +109,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
.AddValue("iv", 2));
var rhs =
new NamedContentData()
new ContentData()
.AddField("field1",
new ContentFieldData()
.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()
{
var lhs =
new NamedContentData()
new ContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("iv", 2))
@ -201,7 +135,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
.AddValue("iv", 2));
var rhs =
new NamedContentData()
new ContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("en", 2))
@ -233,7 +167,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
[Fact]
public void Should_clone_named_value_and_also_children()
{
var source = new NamedContentData
var source = new ContentData
{
["field1"] = 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
{
private readonly NamedContentData source =
new NamedContentData()
private readonly ContentData source =
new ContentData()
.AddField("field1",
new ContentFieldData()
.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.ConvertContent;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json.Objects;
using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
@ -35,54 +34,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
.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]
public void Should_convert_name_to_name()
{
var input =
new NamedContentData()
new ContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("en", "EN"))
@ -93,10 +49,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
new ContentFieldData()
.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 =
new NamedContentData()
new ContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("en", "EN"));
@ -104,49 +60,6 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
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]
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);
}
[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]
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 DomainId id1 = 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<NumberFieldProperties> numberField = Fields.Number(1, "1", Partitioning.Invariant);
private readonly RootField<StringFieldProperties> stringField
= Fields.String(1, "1", Partitioning.Invariant);
private readonly RootField<NumberFieldProperties> numberField
= Fields.Number(1, "1", Partitioning.Invariant);
public ValueConvertersTests()
{
@ -31,66 +34,6 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
.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]
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()
{
var data =
new NamedContentData()
new ContentData()
.AddField("my-string",
new ContentFieldData()
.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()
{
var data =
new NamedContentData()
new ContentData()
.AddField("my-string",
new ContentFieldData()
.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 input =
new NamedContentData()
new ContentData()
.AddField("assets",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1.ToString(), id2.ToString())));
@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var id2 = DomainId.NewGuid();
var input =
new NamedContentData()
new ContentData()
.AddField("assets",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1.ToString(), id2.ToString())));
@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var id2 = DomainId.NewGuid();
var source =
new NamedContentData()
new ContentData()
.AddField("references",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1, id2)))
@ -91,7 +91,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
.Add("nested", JsonValue.Array(id1, id2)))));
var expected =
new NamedContentData()
new ContentData()
.AddField("references",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id2)))
@ -110,7 +110,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
var converter = FieldConverters.ForValues(cleaner, cleanNested);
var actual = source.ConvertName2Name(schema, converter);
var actual = source.Convert(schema, converter);
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]
public void Should_return_default_value_if_no_value_found()
{
var data = new NamedContentData();
var data = new ContentData();
var schema = CreateNoRefSchema();
@ -85,9 +85,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
.AddString(3, "non-ref", Partitioning.Invariant);
}
private static NamedContentData CreateData()
private static ContentData CreateData()
{
return new NamedContentData()
return new ContentData()
.AddField("ref1",
new ContentFieldData()
.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
{
Data =
new NamedContentData()
new ContentData()
.AddField("country",
new ContentFieldData()
.AddValue("zh-TW", "Berlin"))
@ -531,7 +531,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddValue("iv", "Berlin"))
@ -554,7 +554,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddValue("iv", "Berlin"))
@ -577,7 +577,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddJsonValue(JsonValue.Array()))
@ -600,7 +600,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
@ -623,7 +623,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddValue("iv", "Berlin"))
@ -646,7 +646,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddJsonValue(JsonValue.Array("Berlin")))
@ -669,7 +669,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
@ -692,7 +692,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
@ -715,7 +715,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(1, 2, 3)))
@ -738,7 +738,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("name", "Berlin")))
@ -792,7 +792,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("time",
new ContentFieldData()
.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
{
Data =
new NamedContentData()
new ContentData()
.AddField("city",
new ContentFieldData()
.AddJsonValue(JsonValue.Array()))
@ -285,7 +285,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var @event = new EnrichedContentEvent
{
Data =
new NamedContentData()
new ContentData()
.AddField("categories",
new ContentFieldData()
.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]
public void Should_update_data_when_setting_field()
{
var original = new NamedContentData();
var original = new ContentData();
var expected =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", 1.0));
@ -35,10 +35,10 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
[Fact]
public void Should_update_data_when_setting_lazy_field()
{
var original = new NamedContentData();
var original = new ContentData();
var expected =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", 1.0));
@ -51,10 +51,10 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
[Fact]
public void Should_update_data_defining_property_for_content()
{
var original = new NamedContentData();
var original = new ContentData();
var expected =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", 1.0));
@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
[Fact]
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"));
}
@ -76,12 +76,12 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_update_data_when_deleting_field()
{
var original =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", 1.0));
var expected = new NamedContentData();
var expected = new ContentData();
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()
{
var original =
new NamedContentData()
new ContentData()
.AddField("string",
new ContentFieldData()
.AddValue("iv", "1"));
var expected =
new NamedContentData()
new ContentData()
.AddField("string",
new ContentFieldData()
.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()
{
var original =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", 1.0));
var expected =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.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()
{
var original =
new NamedContentData()
new ContentData()
.AddField("boolean",
new ContentFieldData()
.AddValue("iv", false));
var expected =
new NamedContentData()
new ContentData()
.AddField("boolean",
new ContentFieldData()
.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()
{
var original =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(1.0, 2.0)));
var expected =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.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()
{
var original =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.AddJsonValue(JsonValue.Object().Add("lat", 1.0)));
var expected =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.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()
{
var original =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData());
var expected =
new NamedContentData()
new ContentData()
.AddField("number",
new ContentFieldData()
.AddValue("iv", 1.0));
@ -211,13 +211,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_update_data_when_deleting_field_value()
{
var original =
new NamedContentData()
new ContentData()
.AddField("string",
new ContentFieldData()
.AddValue("iv", "hello"));
var expected =
new NamedContentData()
new ContentData()
.AddField("string",
new ContentFieldData());
@ -230,7 +230,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_be_able_to_iterate_over_fields()
{
var content =
new NamedContentData()
new ContentData()
.AddField("f1",
new ContentFieldData()
.AddValue("v11", "1")
@ -262,7 +262,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
public void Should_throw_exceptions_when_changing_objects()
{
var original =
new NamedContentData()
new ContentData()
.AddField("obj",
new ContentFieldData()
.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()
{
var original =
new NamedContentData()
new ContentData()
.AddField("obj",
new ContentFieldData()
.AddJsonValue(JsonValue.Array()));
@ -286,10 +286,10 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
[Fact]
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());

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

Loading…
Cancel
Save