From ffe8880c063a1c19390cbae457522a41a270fa99 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Mon, 19 Sep 2022 19:54:36 +0200 Subject: [PATCH] Fix/invalid geodata (#922) * Ignore invalid geodata. * Save file. --- .../Contents/GeoJsonValue.cs | 2 +- .../Text/MongoTextIndexBase.cs | 17 +++++- .../MongoDb/MongoDbErrorCodes.cs | 16 ++++++ .../Contents/Text/TextIndexerTestsBase.cs | 55 +++++++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoDbErrorCodes.cs diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonValue.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonValue.cs index 662b94982..6935b8c93 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonValue.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonValue.cs @@ -40,7 +40,7 @@ namespace Squidex.Domain.Apps.Core.Contents return GeoJsonParseResult.InvalidLongitude; } - geoJSON = new Point(new Coordinate(lat, lon)); + geoJSON = new Point(new Coordinate(lon, lat)); return GeoJsonParseResult.Success; } diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/MongoTextIndexBase.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/MongoTextIndexBase.cs index 58f94ecdb..39289ec1a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/MongoTextIndexBase.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/MongoTextIndexBase.cs @@ -75,7 +75,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Text await Collection.DeleteManyAsync(Filter.Eq(x => x.AppId, app.Id), ct); } - public virtual Task ExecuteAsync(IndexCommand[] commands, + public async virtual Task ExecuteAsync(IndexCommand[] commands, CancellationToken ct = default) { var writes = new List>>(commands.Length); @@ -87,10 +87,21 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Text if (writes.Count == 0) { - return Task.CompletedTask; + return; } - return Collection.BulkWriteAsync(writes, BulkUnordered, ct); + try + { + await Collection.BulkWriteAsync(writes, BulkUnordered, ct); + } + catch (MongoBulkWriteException ex) + { + // Ignore invalid geo data. + if (ex.WriteErrors.Any(error => error.Code != MongoDbErrorCodes.Errror16755_InvalidGeoData)) + { + throw; + } + } } public virtual async Task?> SearchAsync(IAppEntity app, GeoQuery query, SearchScope scope, diff --git a/backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoDbErrorCodes.cs b/backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoDbErrorCodes.cs new file mode 100644 index 000000000..68512d03f --- /dev/null +++ b/backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoDbErrorCodes.cs @@ -0,0 +1,16 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +#pragma warning disable SA1310 // Field names should not contain underscore + +namespace Squidex.Infrastructure.MongoDb +{ + public static class MongoDbErrorCodes + { + public const int Errror16755_InvalidGeoData = 16755; + } +} diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/TextIndexerTestsBase.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/TextIndexerTestsBase.cs index 3566d5054..d8111f36e 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/TextIndexerTestsBase.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/TextIndexerTestsBase.cs @@ -110,6 +110,29 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text await SearchGeo(expected: null, "other.iv", 51.48596429889613, 12.102629469505713); } + [Fact] + public async Task Should_search_by_geojson() + { + if (!SupportsGeo) + { + return; + } + + var field = Guid.NewGuid().ToString(); + + // Within search radius + await CreateGeoJsonAsync(ids1[0], field, 51.343391192211506, 12.401476788622826); + + // Outside of search radius + await CreateGeoJsonAsync(ids2[0], field, 51.30765141427311, 12.379631713912486); + + // Within search radius and correct field. + await SearchGeo(expected: ids1, $"{field}.iv", 51.34641682574934, 12.401965298137707); + + // Within search radius but incorrect field. + await SearchGeo(expected: null, "other.iv", 51.48596429889613, 12.102629469505713); + } + [Fact] public async Task Should_index_invariant_content_and_retrieve() { @@ -304,6 +327,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text await SearchText(expected: ids2, text: "V2_1"); } + [Fact] + public async Task Should_index_invalid_geodata() + { + await CreateGeoAsync(ids1[0], "field", 144.34, -200); + } + + [Fact] + public async Task Should_index_invalid_geojsondata() + { + await CreateGeoJsonAsync(ids1[0], "field", 144.34, -200); + } + protected Task CreateTextAsync(DomainId id, string language, string text) { var data = TextData(language, text); @@ -318,6 +353,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text return UpdateAsync(id, new ContentCreated { Data = data }); } + protected Task CreateGeoJsonAsync(DomainId id, string field, double latitude, double longitude) + { + var data = GeoJsonData(field, latitude, longitude); + + return UpdateAsync(id, new ContentCreated { Data = data }); + } + protected Task UpdateTextAsync(DomainId id, string language, string text) { var data = TextData(language, text); @@ -384,6 +426,19 @@ namespace Squidex.Domain.Apps.Entities.Contents.Text .AddInvariant(new JsonObject().Add("latitude", latitude).Add("longitude", longitude))); } + private static ContentData GeoJsonData(string field, double latitude, double longitude) + { + return new ContentData() + .AddField(field, + new ContentFieldData() + .AddInvariant(new JsonObject() + .Add("type", "Point") + .Add("coordinates", + new JsonArray() + .Add(longitude) + .Add(latitude)))); + } + protected async Task SearchGeo(List? expected, string field, double latitude, double longitude, SearchScope target = SearchScope.All) { var query = new GeoQuery(schemaId.Id, field, latitude, longitude, 1000, 1000);