Browse Source

Feature/geo json (#619)

* Reduce allocations in visitors.

* Geojson.

* Build fixes (Tests pending).

* More fixes.

* Geojson support for geolocation fields.
pull/616/head
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
14ef257aaf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 36
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs
  2. 33
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs
  3. 1
      backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
  4. 77
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs
  5. 5
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationContext.cs
  6. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs
  7. 7
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentOperationContext.cs
  8. 23
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs
  9. 2
      backend/src/Squidex.Infrastructure/Json/IJsonSerializer.cs
  10. 4
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs
  11. 2
      backend/src/Squidex/Config/Domain/SerializationServices.cs
  12. 4
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs
  13. 2
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs
  14. 49
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs
  15. 2
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs
  16. 13
      backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs
  17. 3
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/MongoDb/AssetsQueryFixture.cs
  18. 1
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetEnricherTests.cs
  19. 3
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryParserTests.cs
  20. 1
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs
  21. 1
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs
  22. 5
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs
  23. 5
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs
  24. 3
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryParserTests.cs
  25. 4
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ConvertDataTests.cs
  26. 1
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/CachingTextIndexerStateTests.cs
  27. 15
      backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/AExtensions.cs
  28. 69
      backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/JsonHelper.cs

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

@ -13,6 +13,7 @@ using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ConvertContent namespace Squidex.Domain.Apps.Core.ConvertContent
@ -28,32 +29,35 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
return field.IsForApi() ? data : null; return field.IsForApi() ? data : null;
}; };
public static readonly FieldConverter ExcludeChangedTypes = (data, field) => public static FieldConverter ExcludeChangedTypes(IJsonSerializer jsonSerializer)
{ {
foreach (var value in data.Values) return (data, field) =>
{ {
if (value.Type == JsonValueType.Null) foreach (var value in data.Values)
{ {
continue; if (value.Type == JsonValueType.Null)
} {
continue;
}
try try
{ {
var (_, error) = JsonValueConverter.ConvertValue(field, value); var (_, error) = JsonValueConverter.ConvertValue(field, value, jsonSerializer);
if (error != null) if (error != null)
{
return null;
}
}
catch
{ {
return null; return null;
} }
} }
catch
{
return null;
}
}
return data; return data;
}; };
}
public static FieldConverter ResolveInvariant(LanguagesConfig languages) public static FieldConverter ResolveInvariant(LanguagesConfig languages)
{ {

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

@ -28,29 +28,32 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
return field.IsForApi() ? value : null; return field.IsForApi() ? value : null;
}; };
public static readonly ValueConverter ExcludeChangedTypes = (value, field, parent) => public static ValueConverter ExcludeChangedTypes(IJsonSerializer jsonSerializer)
{ {
if (value.Type == JsonValueType.Null) return (value, field, parent) =>
{ {
return value; if (value.Type == JsonValueType.Null)
} {
return value;
}
try try
{ {
var (_, error) = JsonValueConverter.ConvertValue(field, value); var (_, error) = JsonValueConverter.ConvertValue(field, value, jsonSerializer);
if (error != null) if (error != null)
{
return null;
}
}
catch
{ {
return null; return null;
} }
}
catch
{
return null;
}
return value; return value;
}; };
}
public static ValueConverter DecodeJson(IJsonSerializer jsonSerializer) public static ValueConverter DecodeJson(IJsonSerializer jsonSerializer)
{ {

1
backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj

@ -18,6 +18,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Fluid.Core.Squidex" Version="1.0.0-beta" /> <PackageReference Include="Fluid.Core.Squidex" Version="1.0.0-beta" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.29" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.29" />
<PackageReference Include="GeoJSON.Net" Version="1.2.19" />
<PackageReference Include="Markdig" Version="0.22.1" /> <PackageReference Include="Markdig" Version="0.22.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="Microsoft.OData.Core" Version="7.8.1" /> <PackageReference Include="Microsoft.OData.Core" Version="7.8.1" />

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

@ -5,11 +5,14 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using GeoJSON.Net;
using GeoJSON.Net.Geometry;
using NodaTime.Text; using NodaTime.Text;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Translations;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
@ -23,10 +26,13 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
public readonly struct Args public readonly struct Args
{ {
public readonly IJsonValue Value; public readonly IJsonValue Value;
public readonly IJsonSerializer JsonSerializer;
public Args(IJsonValue value) public Args(IJsonValue value, IJsonSerializer jsonSerializer)
{ {
Value = value; Value = value;
JsonSerializer = jsonSerializer;
} }
} }
@ -34,12 +40,14 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
{ {
} }
public static (object? Result, JsonError? Error) ConvertValue(IField field, IJsonValue value) public static (object? Result, JsonError? Error) ConvertValue(IField field, IJsonValue value, IJsonSerializer jsonSerializer)
{ {
Guard.NotNull(field, nameof(field)); Guard.NotNull(field, nameof(field));
Guard.NotNull(value, nameof(value)); Guard.NotNull(value, nameof(value));
return field.Accept(Instance, new Args(value)); var args = new Args(value, jsonSerializer);
return field.Accept(Instance, args);
} }
public (object? Result, JsonError? Error) Visit(IArrayField field, Args args) public (object? Result, JsonError? Error) Visit(IArrayField field, Args args)
@ -116,46 +124,55 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
public (object? Result, JsonError? Error) Visit(IField<GeolocationFieldProperties> field, Args args) public (object? Result, JsonError? Error) Visit(IField<GeolocationFieldProperties> field, Args args)
{ {
if (args.Value is JsonObject geolocation) if (args.Value is JsonObject geoObject)
{ {
foreach (var propertyName in geolocation.Keys) try
{ {
if (!string.Equals(propertyName, "latitude", StringComparison.OrdinalIgnoreCase) && using (var stream = new MemoryStream())
!string.Equals(propertyName, "longitude", StringComparison.OrdinalIgnoreCase))
{ {
return (null, new JsonError(T.Get("contents.invalidGeolocationMoreProperties"))); args.JsonSerializer.Serialize(args.Value, stream, true);
stream.Position = 0;
var geoJson = args.JsonSerializer.Deserialize<IGeoJSONObject>(stream);
return (geoJson, null);
} }
} }
catch
if (geolocation.TryGetValue("latitude", out var latValue) && latValue is JsonNumber latNumber)
{ {
var lat = latNumber.Value; if (geoObject.TryGetValue("latitude", out var latValue) && latValue is JsonNumber latNumber)
{
var lat = latNumber.Value;
if (!lat.IsBetween(-90, 90)) if (!lat.IsBetween(-90, 90))
{
return (null, new JsonError(T.Get("contents.invalidGeolocationLatitude")));
}
}
else
{ {
return (null, new JsonError(T.Get("contents.invalidGeolocationLatitude"))); return (null, new JsonError(T.Get("contents.invalidGeolocation")));
} }
}
else
{
return (null, new JsonError(T.Get("contents.invalidGeolocation")));
}
if (geolocation.TryGetValue("longitude", out var lonValue) && lonValue is JsonNumber lonNumber) if (geoObject.TryGetValue("longitude", out var lonValue) && lonValue is JsonNumber lonNumber)
{ {
var lon = lonNumber.Value; var lon = lonNumber.Value;
if (!lon.IsBetween(-180, 180)) if (!lon.IsBetween(-180, 180))
{
return (null, new JsonError(T.Get("contents.invalidGeolocationLongitude")));
}
}
else
{ {
return (null, new JsonError(T.Get("contents.invalidGeolocationLongitude"))); return (null, new JsonError(T.Get("contents.invalidGeolocation")));
} }
}
else
{
return (null, new JsonError(T.Get("contents.invalidGeolocation")));
}
return (args.Value, null); var position = new Position(latNumber.Value, lonNumber.Value);
return (position, null);
}
} }
return (null, new JsonError(T.Get("contents.invalidGeolocation"))); return (null, new JsonError(T.Get("contents.invalidGeolocation")));

5
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationContext.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Core.ValidateContent namespace Squidex.Domain.Apps.Core.ValidateContent
{ {
@ -16,17 +17,21 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
{ {
public ImmutableQueue<string> Path { get; private set; } = ImmutableQueue<string>.Empty; public ImmutableQueue<string> Path { get; private set; } = ImmutableQueue<string>.Empty;
public IJsonSerializer JsonSerializer { get; private set; }
public DomainId ContentId { get; } public DomainId ContentId { get; }
public bool IsOptional { get; private set; } public bool IsOptional { get; private set; }
public ValidationContext( public ValidationContext(
IJsonSerializer jsonSerializer,
NamedId<DomainId> appId, NamedId<DomainId> appId,
NamedId<DomainId> schemaId, NamedId<DomainId> schemaId,
Schema schema, Schema schema,
DomainId contentId) DomainId contentId)
: base(appId, schemaId, schema) : base(appId, schemaId, schema)
{ {
JsonSerializer = jsonSerializer;
ContentId = contentId; ContentId = contentId;
} }

2
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs

@ -41,7 +41,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
} }
else else
{ {
var (json, error) = JsonValueConverter.ConvertValue(field, jsonValue); var (json, error) = JsonValueConverter.ConvertValue(field, jsonValue, context.JsonSerializer);
if (error != null) if (error != null)
{ {

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

@ -20,6 +20,7 @@ using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
using Squidex.Log; using Squidex.Log;
@ -40,6 +41,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
private readonly IEnumerable<IValidatorsFactory> validators; private readonly IEnumerable<IValidatorsFactory> validators;
private readonly IContentWorkflow contentWorkflow; private readonly IContentWorkflow contentWorkflow;
private readonly IContentRepository contentRepository; private readonly IContentRepository contentRepository;
private readonly IJsonSerializer jsonSerializer;
private ISchemaEntity schema; private ISchemaEntity schema;
private IAppEntity app; private IAppEntity app;
private ContentCommand command; private ContentCommand command;
@ -54,6 +56,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
IEnumerable<IValidatorsFactory> validators, IEnumerable<IValidatorsFactory> validators,
IContentWorkflow contentWorkflow, IContentWorkflow contentWorkflow,
IContentRepository contentRepository, IContentRepository contentRepository,
IJsonSerializer jsonSerializer,
IScriptEngine scriptEngine, IScriptEngine scriptEngine,
ISemanticLog log) ISemanticLog log)
{ {
@ -61,6 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
Guard.NotDefault(validators, nameof(validators)); Guard.NotDefault(validators, nameof(validators));
Guard.NotDefault(contentWorkflow, nameof(contentWorkflow)); Guard.NotDefault(contentWorkflow, nameof(contentWorkflow));
Guard.NotDefault(contentRepository, nameof(contentRepository)); Guard.NotDefault(contentRepository, nameof(contentRepository));
Guard.NotDefault(jsonSerializer, nameof(jsonSerializer));
Guard.NotDefault(scriptEngine, nameof(scriptEngine)); Guard.NotDefault(scriptEngine, nameof(scriptEngine));
Guard.NotDefault(log, nameof(log)); Guard.NotDefault(log, nameof(log));
@ -68,6 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
this.validators = validators; this.validators = validators;
this.contentWorkflow = contentWorkflow; this.contentWorkflow = contentWorkflow;
this.contentRepository = contentRepository; this.contentRepository = contentRepository;
this.jsonSerializer = jsonSerializer;
this.scriptEngine = scriptEngine; this.scriptEngine = scriptEngine;
this.log = log; this.log = log;
@ -98,7 +103,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
this.schema = schema; this.schema = schema;
validationContext = new ValidationContext(appId, schemaId, schema.SchemaDef, command.ContentId).Optimized(optimized); validationContext = new ValidationContext(jsonSerializer, appId, schemaId, schema.SchemaDef, command.ContentId).Optimized(optimized);
} }
public Task<Status> GetInitialStatusAsync() public Task<Status> GetInitialStatusAsync()

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

@ -15,6 +15,7 @@ using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Tasks; using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
@ -24,16 +25,28 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
private readonly IUrlGenerator urlGenerator; private readonly IUrlGenerator urlGenerator;
private readonly IAssetRepository assetRepository; private readonly IAssetRepository assetRepository;
private readonly IContentRepository contentRepository; private readonly IContentRepository contentRepository;
private readonly FieldConverter excludedChangedField;
private readonly FieldConverter excludedChangedValue;
private readonly FieldConverter excludedHiddenField;
private readonly FieldConverter excludedHiddenValue;
public ConvertData(IUrlGenerator urlGenerator, IAssetRepository assetRepository, IContentRepository contentRepository) public ConvertData(IUrlGenerator urlGenerator, IJsonSerializer jsonSerializer,
IAssetRepository assetRepository, IContentRepository contentRepository)
{ {
Guard.NotNull(urlGenerator, nameof(urlGenerator)); Guard.NotNull(urlGenerator, nameof(urlGenerator));
Guard.NotNull(jsonSerializer, nameof(jsonSerializer));
Guard.NotNull(assetRepository, nameof(assetRepository)); Guard.NotNull(assetRepository, nameof(assetRepository));
Guard.NotNull(contentRepository, nameof(contentRepository)); Guard.NotNull(contentRepository, nameof(contentRepository));
this.urlGenerator = urlGenerator; this.urlGenerator = urlGenerator;
this.assetRepository = assetRepository; this.assetRepository = assetRepository;
this.contentRepository = contentRepository; this.contentRepository = contentRepository;
excludedChangedField = FieldConverters.ExcludeChangedTypes(jsonSerializer);
excludedChangedValue = FieldConverters.ForValues(ValueConverters.ForNested(ValueConverters.ExcludeChangedTypes(jsonSerializer)));
excludedHiddenField = FieldConverters.ExcludeHidden;
excludedHiddenValue = FieldConverters.ForValues(ValueConverters.ForNested(ValueConverters.ExcludeHidden));
} }
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas) public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas)
@ -102,12 +115,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
{ {
if (!context.IsFrontendClient) if (!context.IsFrontendClient)
{ {
yield return FieldConverters.ExcludeHidden; yield return excludedHiddenField;
yield return FieldConverters.ForValues(ValueConverters.ForNested(ValueConverters.ExcludeHidden)); yield return excludedHiddenValue;
} }
yield return FieldConverters.ExcludeChangedTypes; yield return excludedChangedField;
yield return FieldConverters.ForValues(ValueConverters.ForNested(ValueConverters.ExcludeChangedTypes)); yield return excludedChangedValue;
if (cleanReferences != null) if (cleanReferences != null)
{ {

2
backend/src/Squidex.Infrastructure/Json/IJsonSerializer.cs

@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.Json
{ {
string Serialize<T>(T value, bool intented = false); string Serialize<T>(T value, bool intented = false);
void Serialize<T>(T value, Stream stream); void Serialize<T>(T value, Stream stream, bool leaveOpen = false);
T Deserialize<T>(string value, Type? actualType = null); T Deserialize<T>(string value, Type? actualType = null);

4
backend/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs

@ -30,9 +30,9 @@ namespace Squidex.Infrastructure.Json.Newtonsoft
return JsonConvert.SerializeObject(value, intented ? Formatting.Indented : Formatting.None, settings); return JsonConvert.SerializeObject(value, intented ? Formatting.Indented : Formatting.None, settings);
} }
public void Serialize<T>(T value, Stream stream) public void Serialize<T>(T value, Stream stream, bool leaveOpen = false)
{ {
using (var writer = new StreamWriter(stream)) using (var writer = new StreamWriter(stream, leaveOpen: leaveOpen))
{ {
serializer.Serialize(writer, value); serializer.Serialize(writer, value);

2
backend/src/Squidex/Config/Domain/SerializationServices.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using GeoJSON.Net.Converters;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Migrations; using Migrations;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -40,6 +41,7 @@ namespace Squidex.Config.Domain
new EnvelopeHeadersConverter(), new EnvelopeHeadersConverter(),
new FilterConverter(), new FilterConverter(),
new InstantConverter(), new InstantConverter(),
new GeoJsonConverter(),
new JsonValueConverter(), new JsonValueConverter(),
new LanguageConverter(), new LanguageConverter(),
new LanguagesConfigConverter(), new LanguagesConfigConverter(),

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

@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
.AddValue("en", null) .AddValue("en", null)
.AddValue("de", 1); .AddValue("de", 1);
var result = FieldConverters.ExcludeChangedTypes(source, field); var result = FieldConverters.ExcludeChangedTypes(TestUtils.DefaultSerializer)(source, field);
Assert.Same(source, result); Assert.Same(source, result);
} }
@ -80,7 +80,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
.AddValue("en", "EN") .AddValue("en", "EN")
.AddValue("de", 0); .AddValue("de", 0);
var result = FieldConverters.ExcludeChangedTypes(source, field); var result = FieldConverters.ExcludeChangedTypes(TestUtils.DefaultSerializer)(source, field);
Assert.Null(result); Assert.Null(result);
} }

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

@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var source = JsonValue.Create("invalid"); var source = JsonValue.Create("invalid");
var result = ValueConverters.ExcludeChangedTypes(source, numberField); var result = ValueConverters.ExcludeChangedTypes(TestUtils.DefaultSerializer)(source, numberField);
Assert.Null(result); Assert.Null(result);
} }

49
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs

@ -38,13 +38,17 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
} }
[Fact] [Fact]
public async Task Should_not_add_error_if_geolocation_is_valid() public async Task Should_not_add_error_if_geolocation_is_valid_geojson()
{ {
var sut = Field(new GeolocationFieldProperties()); var sut = Field(new GeolocationFieldProperties());
var geolocation = JsonValue.Object() var geolocation = JsonValue.Object()
.Add("latitude", 0) .Add("coordinates",
.Add("longitude", 0); JsonValue.Array(
JsonValue.Create(12),
JsonValue.Create(45)
))
.Add("type", "Point");
await sut.ValidateAsync(geolocation, errors); await sut.ValidateAsync(geolocation, errors);
@ -52,49 +56,35 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
} }
[Fact] [Fact]
public async Task Should_add_error_if_geolocation_has_invalid_latitude() public async Task Should_not_add_error_if_geolocation_is_valid()
{ {
var sut = Field(new GeolocationFieldProperties { IsRequired = true }); var sut = Field(new GeolocationFieldProperties());
var geolocation = JsonValue.Object() await sut.ValidateAsync(CreateValue(0, 0), errors);
.Add("latitude", 200)
.Add("longitude", 0);
await sut.ValidateAsync(geolocation, errors); Assert.Empty(errors);
errors.Should().BeEquivalentTo(
new[] { "Latitude must be between -90 and 90." });
} }
[Fact] [Fact]
public async Task Should_add_error_if_geolocation_has_invalid_longitude() public async Task Should_add_error_if_geolocation_has_invalid_latitude()
{ {
var sut = Field(new GeolocationFieldProperties { IsRequired = true }); var sut = Field(new GeolocationFieldProperties { IsRequired = true });
var geolocation = JsonValue.Object() await sut.ValidateAsync(CreateValue(200, 0), errors);
.Add("latitude", 0)
.Add("longitude", 200);
await sut.ValidateAsync(geolocation, errors);
errors.Should().BeEquivalentTo( errors.Should().BeEquivalentTo(
new[] { "Longitude must be between -180 and 180." }); new[] { "Latitude must be between -90 and 90." });
} }
[Fact] [Fact]
public async Task Should_add_error_if_geolocation_has_too_many_properties() public async Task Should_add_error_if_geolocation_has_invalid_longitude()
{ {
var sut = Field(new GeolocationFieldProperties { IsRequired = true }); var sut = Field(new GeolocationFieldProperties { IsRequired = true });
var geolocation = JsonValue.Object() await sut.ValidateAsync(CreateValue(0, 200), errors);
.Add("invalid", 0)
.Add("latitude", 0)
.Add("longitude", 0);
await sut.ValidateAsync(geolocation, errors);
errors.Should().BeEquivalentTo( errors.Should().BeEquivalentTo(
new[] { "Geolocation can only have latitude and longitude property." }); new[] { "Longitude must be between -180 and 180." });
} }
[Fact] [Fact]
@ -108,6 +98,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
new[] { "Field is required." }); new[] { "Field is required." });
} }
private static JsonObject CreateValue(double lat, double lon)
{
return JsonValue.Object().Add("latitude", lat).Add("longitude", lon);
}
private static RootField<GeolocationFieldProperties> Field(GeolocationFieldProperties properties) private static RootField<GeolocationFieldProperties> Field(GeolocationFieldProperties properties)
{ {
return Fields.Geolocation(1, "my-geolocation", Partitioning.Invariant, properties); return Fields.Geolocation(1, "my-geolocation", Partitioning.Invariant, properties);

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

@ -11,6 +11,7 @@ using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Core.ValidateContent.Validators; using Squidex.Domain.Apps.Core.ValidateContent.Validators;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -127,6 +128,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
ValidationAction action = ValidationAction.Upsert) ValidationAction action = ValidationAction.Upsert)
{ {
var context = new ValidationContext( var context = new ValidationContext(
TestUtils.DefaultSerializer,
AppId, AppId,
SchemaId, SchemaId,
schema ?? new Schema(SchemaId.Name), schema ?? new Schema(SchemaId.Name),

13
backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs

@ -8,6 +8,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using GeoJSON.Net.Converters;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using Squidex.Domain.Apps.Core.Apps.Json; using Squidex.Domain.Apps.Core.Apps.Json;
@ -30,7 +31,9 @@ namespace Squidex.Domain.Apps.Core.TestHelpers
{ {
public static readonly IJsonSerializer DefaultSerializer = CreateSerializer(); public static readonly IJsonSerializer DefaultSerializer = CreateSerializer();
public static IJsonSerializer CreateSerializer(TypeNameHandling typeNameHandling = TypeNameHandling.Auto) public static readonly JsonSerializerSettings DefaultSerializerSettings = CreateSerializerSettings();
public static JsonSerializerSettings CreateSerializerSettings(TypeNameHandling typeNameHandling = TypeNameHandling.Auto)
{ {
var typeNameRegistry = var typeNameRegistry =
new TypeNameRegistry() new TypeNameRegistry()
@ -51,6 +54,7 @@ namespace Squidex.Domain.Apps.Core.TestHelpers
new DomainIdConverter(), new DomainIdConverter(),
new EnvelopeHeadersConverter(), new EnvelopeHeadersConverter(),
new FilterConverter(), new FilterConverter(),
new GeoJsonConverter(),
new InstantConverter(), new InstantConverter(),
new JsonValueConverter(), new JsonValueConverter(),
new LanguageConverter(), new LanguageConverter(),
@ -73,6 +77,13 @@ namespace Squidex.Domain.Apps.Core.TestHelpers
TypeNameHandling = typeNameHandling TypeNameHandling = typeNameHandling
}; };
return serializerSettings;
}
public static IJsonSerializer CreateSerializer(TypeNameHandling typeNameHandling = TypeNameHandling.Auto)
{
var serializerSettings = CreateSerializerSettings(typeNameHandling);
return new NewtonsoftJsonSerializer(serializerSettings); return new NewtonsoftJsonSerializer(serializerSettings);
} }

3
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/MongoDb/AssetsQueryFixture.cs

@ -12,6 +12,7 @@ using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Assets; using Squidex.Domain.Apps.Core.Assets;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.MongoDb.Assets; using Squidex.Domain.Apps.Entities.MongoDb.Assets;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
@ -117,7 +118,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.MongoDb
private static void SetupJson() private static void SetupJson()
{ {
var jsonSerializer = JsonSerializer.Create(JsonHelper.DefaultSettings()); var jsonSerializer = JsonSerializer.Create(TestUtils.DefaultSerializerSettings);
BsonJsonConvention.Register(jsonSerializer); BsonJsonConvention.Register(jsonSerializer);
} }

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

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.Tags; using Squidex.Domain.Apps.Core.Tags;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Caching;

3
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/AssetQueryParserTests.cs

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Squidex.Domain.Apps.Core.Tags; using Squidex.Domain.Apps.Core.Tags;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Validation; using Squidex.Infrastructure.Validation;
@ -30,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries
var options = Options.Create(new AssetOptions { DefaultPageSize = 30 }); var options = Options.Create(new AssetOptions { DefaultPageSize = 30 });
sut = new AssetQueryParser(JsonHelper.DefaultSerializer, tagService, options); sut = new AssetQueryParser(TestUtils.DefaultSerializer, tagService, options);
} }
[Fact] [Fact]

1
backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Shared.Users; using Squidex.Shared.Users;

1
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs

@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Triggers; using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;

5
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs

@ -13,6 +13,7 @@ using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
@ -107,10 +108,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var validators = Enumerable.Repeat(new DefaultValidatorsFactory(), 1); var validators = Enumerable.Repeat(new DefaultValidatorsFactory(), 1);
var context = new ContentOperationContext(appProvider, var context = new ContentOperationContext(
appProvider,
validators, validators,
contentWorkflow, contentWorkflow,
contentRepository, contentRepository,
TestUtils.DefaultSerializer,
scriptEngine, A.Fake<ISemanticLog>()); scriptEngine, A.Fake<ISemanticLog>());
sut = new ContentDomainObject(Store, A.Dummy<ISemanticLog>(), context); sut = new ContentDomainObject(Store, A.Dummy<ISemanticLog>(), context);

5
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs

@ -16,6 +16,7 @@ using Newtonsoft.Json;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Domain.Apps.Entities.Contents.Text;
@ -63,7 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
mongoDatabase, mongoDatabase,
CreateAppProvider(), CreateAppProvider(),
CreateTextIndexer(), CreateTextIndexer(),
JsonHelper.DefaultSerializer); TestUtils.DefaultSerializer);
Task.Run(async () => Task.Run(async () =>
{ {
@ -161,7 +162,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
private static void SetupJson() private static void SetupJson()
{ {
var jsonSerializer = JsonSerializer.Create(JsonHelper.DefaultSettings()); var jsonSerializer = JsonSerializer.Create(TestUtils.DefaultSerializerSettings);
BsonJsonConvention.Register(jsonSerializer); BsonJsonConvention.Register(jsonSerializer);
} }

3
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryParserTests.cs

@ -10,6 +10,7 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -42,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
var cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); var cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
sut = new ContentQueryParser(cache, JsonHelper.DefaultSerializer, options); sut = new ContentQueryParser(cache, TestUtils.DefaultSerializer, options);
} }
[Fact] [Fact]

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

@ -12,6 +12,7 @@ using FakeItEasy;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Contents.Queries.Steps; using Squidex.Domain.Apps.Entities.Contents.Queries.Steps;
using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Contents.Repositories;
@ -20,6 +21,7 @@ using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
using Xunit; using Xunit;
using TestUtils = Squidex.Domain.Apps.Core.TestHelpers.TestUtils;
namespace Squidex.Domain.Apps.Entities.Contents.Queries namespace Squidex.Domain.Apps.Entities.Contents.Queries
{ {
@ -46,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
schema = Mocks.Schema(appId, schemaId, schemaDef); schema = Mocks.Schema(appId, schemaId, schemaDef);
schemaProvider = x => Task.FromResult(schema); schemaProvider = x => Task.FromResult(schema);
sut = new ConvertData(urlGenerator, assetRepository, contentRepository); sut = new ConvertData(urlGenerator, TestUtils.DefaultSerializer, assetRepository, contentRepository);
} }
[Fact] [Fact]

1
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/CachingTextIndexerStateTests.cs

@ -8,6 +8,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.TestHelpers;
using Squidex.Domain.Apps.Entities.Contents.Text.State; using Squidex.Domain.Apps.Entities.Contents.Text.State;
using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure; using Squidex.Infrastructure;

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

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

69
backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/JsonHelper.cs

@ -1,69 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
using Squidex.Infrastructure.Queries.Json;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.TestHelpers
{
public static class JsonHelper
{
public static readonly IJsonSerializer DefaultSerializer = CreateSerializer();
public static IJsonSerializer CreateSerializer(TypeNameRegistry? typeNameRegistry = null)
{
var serializerSettings = DefaultSettings(typeNameRegistry);
return new NewtonsoftJsonSerializer(serializerSettings);
}
public static JsonSerializerSettings DefaultSettings(TypeNameRegistry? typeNameRegistry = null)
{
return new JsonSerializerSettings
{
SerializationBinder = new TypeNameSerializationBinder(typeNameRegistry ?? new TypeNameRegistry()),
ContractResolver = new ConverterContractResolver(
new ClaimsPrincipalConverter(),
new InstantConverter(),
new EnvelopeHeadersConverter(),
new FilterConverter(),
new JsonValueConverter(),
new LanguageConverter(),
new NamedDomainIdConverter(),
new NamedGuidIdConverter(),
new NamedLongIdConverter(),
new NamedStringIdConverter(),
new PropertyPathConverter(),
new RefTokenConverter(),
new StringEnumConverter()),
TypeNameHandling = TypeNameHandling.Auto
};
}
public static T SerializeAndDeserialize<T>(this T value)
{
return DefaultSerializer.Deserialize<Tuple<T>>(DefaultSerializer.Serialize(Tuple.Create(value))).Item1;
}
public static T Deserialize<T>(string value)
{
return DefaultSerializer.Deserialize<Tuple<T>>($"{{ \"Item1\": \"{value}\" }}").Item1;
}
public static T Deserialize<T>(object value)
{
return DefaultSerializer.Deserialize<Tuple<T>>($"{{ \"Item1\": {value} }}").Item1;
}
}
}
Loading…
Cancel
Save