Skip to content

Commit 56a3ac0

Browse files
authored
Use a custom convertor for GeoOrientation to tolerate alternative options in Elasticsearch #3776 (#3779)
1 parent ea4f212 commit 56a3ac0

File tree

3 files changed

+173
-7
lines changed

3 files changed

+173
-7
lines changed

src/Nest/CommonAbstractions/SerializationBehavior/JsonFormatters/NestFormatterResolver.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ internal sealed class InnerResolver : IJsonFormatterResolver
4646
new TimeSpanToStringFormatter(),
4747
new NullableTimeSpanToStringFormatter(),
4848
new JsonNetCompatibleUriFormatter(),
49+
new GeoOrientationFormatter(),
50+
new NullableGeoOrientationFormatter(),
4951
}, new IJsonFormatterResolver[0]),
5052
BuiltinResolver.Instance, // Builtin primitives
5153
ElasticsearchNetEnumResolver.Instance, // Specialized Enum handling
Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,93 @@
1-
using System.Runtime.Serialization;
2-
using Elasticsearch.Net;
3-
1+
using Elasticsearch.Net;
42

53
namespace Nest
64
{
7-
[StringEnum]
85
public enum GeoOrientation
96
{
10-
[EnumMember(Value = "cw")]
117
ClockWise,
12-
13-
[EnumMember(Value = "ccw")]
148
CounterClockWise
159
}
10+
11+
internal class GeoOrientationFormatter : IJsonFormatter<GeoOrientation>
12+
{
13+
public void Serialize(ref JsonWriter writer, GeoOrientation value, IJsonFormatterResolver formatterResolver)
14+
{
15+
switch (value)
16+
{
17+
case GeoOrientation.ClockWise:
18+
writer.WriteString("cw");
19+
break;
20+
case GeoOrientation.CounterClockWise:
21+
writer.WriteString("ccw");
22+
break;
23+
}
24+
}
25+
26+
public GeoOrientation Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
27+
{
28+
if (reader.ReadIsNull())
29+
{
30+
// Default, complies with the OGC standard
31+
return GeoOrientation.CounterClockWise;
32+
}
33+
34+
var enumString = reader.ReadString();
35+
switch (enumString.ToUpperInvariant())
36+
{
37+
case "LEFT":
38+
case "CW":
39+
case "CLOCKWISE":
40+
return GeoOrientation.ClockWise;
41+
}
42+
43+
// Default, complies with the OGC standard
44+
return GeoOrientation.CounterClockWise;
45+
}
46+
}
47+
48+
internal class NullableGeoOrientationFormatter : IJsonFormatter<GeoOrientation?>
49+
{
50+
public void Serialize(ref JsonWriter writer, GeoOrientation? value, IJsonFormatterResolver formatterResolver)
51+
{
52+
if (!value.HasValue)
53+
{
54+
writer.WriteNull();
55+
return;
56+
}
57+
58+
switch (value)
59+
{
60+
case GeoOrientation.ClockWise:
61+
writer.WriteString("cw");
62+
break;
63+
case GeoOrientation.CounterClockWise:
64+
writer.WriteString("ccw");
65+
break;
66+
}
67+
}
68+
69+
public GeoOrientation? Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
70+
{
71+
if (reader.ReadIsNull())
72+
{
73+
return null;
74+
}
75+
76+
var enumString = reader.ReadString();
77+
78+
switch (enumString.ToUpperInvariant())
79+
{
80+
case "LEFT":
81+
case "CW":
82+
case "CLOCKWISE":
83+
return GeoOrientation.ClockWise;
84+
case "RIGHT":
85+
case "CCW":
86+
case "COUNTERCLOCKWISE":
87+
return GeoOrientation.CounterClockWise;
88+
default:
89+
return null;
90+
}
91+
}
92+
}
1693
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Linq;
3+
using Elasticsearch.Net;
4+
using FluentAssertions;
5+
using Nest;
6+
using Tests.Core.Extensions;
7+
using Tests.Core.ManagedElasticsearch.Clusters;
8+
using Tests.Domain;
9+
using Tests.Framework;
10+
using Tests.Framework.Integration;
11+
12+
namespace Tests.Mapping.Types.Core.GeoShape
13+
{
14+
public class GeoShapeClusterMetadataApiTests : ApiIntegrationTestBase<WritableCluster, PutMappingResponse, IPutMappingRequest, PutMappingDescriptor<Project>,
15+
PutMappingRequest<Project>>
16+
{
17+
public GeoShapeClusterMetadataApiTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
18+
19+
protected override void IntegrationSetup(IElasticClient client, CallUniqueValues values)
20+
{
21+
foreach (var index in values.Values) client.Indices.Create(index, CreateIndexSettings).ShouldBeValid();
22+
var indices = Infer.Indices(values.Values.Select(i => (IndexName)i));
23+
client.Cluster.Health(null, f => f.WaitForStatus(WaitForStatus.Yellow).Index(indices))
24+
.ShouldBeValid();
25+
}
26+
27+
protected virtual ICreateIndexRequest CreateIndexSettings(CreateIndexDescriptor create) => create;
28+
29+
protected override bool ExpectIsValid => true;
30+
protected override int ExpectStatusCode => 200;
31+
32+
protected override Func<PutMappingDescriptor<Project>, IPutMappingRequest> Fluent => f => f
33+
.Index(CallIsolatedValue)
34+
.Properties(FluentProperties);
35+
36+
private static Func<PropertiesDescriptor<Project>, IPromise<IProperties>> FluentProperties => f => f
37+
.GeoShape(s => s
38+
.Name(p => p.Location)
39+
.Tree(GeoTree.Quadtree)
40+
.Orientation(GeoOrientation.ClockWise)
41+
.Strategy(GeoStrategy.Recursive)
42+
.TreeLevels(3)
43+
.PointsOnly()
44+
.DistanceErrorPercentage(1.0)
45+
.Coerce()
46+
);
47+
48+
private static IProperties InitializerProperties => new Properties
49+
{
50+
{
51+
"location", new GeoShapeProperty
52+
{
53+
Tree = GeoTree.Quadtree,
54+
Orientation = GeoOrientation.ClockWise,
55+
Strategy = GeoStrategy.Recursive,
56+
TreeLevels = 3,
57+
PointsOnly = true,
58+
DistanceErrorPercentage = 1.0,
59+
Coerce = true
60+
}
61+
}
62+
};
63+
64+
protected override HttpMethod HttpMethod => HttpMethod.PUT;
65+
66+
protected override PutMappingRequest<Project> Initializer => new PutMappingRequest<Project>(CallIsolatedValue)
67+
{
68+
Properties = InitializerProperties
69+
};
70+
71+
protected override string UrlPath => $"/{CallIsolatedValue}/_mapping";
72+
73+
protected override LazyResponses ClientUsage() => Calls(
74+
(client, f) => client.Map(f),
75+
(client, f) => client.MapAsync(f),
76+
(client, r) => client.Map(r),
77+
(client, r) => client.MapAsync(r)
78+
);
79+
80+
protected override void ExpectResponse(PutMappingResponse response)
81+
{
82+
// Ensure metadata can be deserialised
83+
var metadata = Client.Cluster.State(CallIsolatedValue, r => r.Metric(ClusterStateMetric.Metadata));
84+
metadata.IsValid.Should().BeTrue();
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)