Skip to content

Commit bc1b00c

Browse files
Mpdreamzclaudecursoragent
authored
Fix AOT build failure after search NuGet refactor (#3433)
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 4856b14 commit bc1b00c

5 files changed

Lines changed: 154 additions & 11 deletions

File tree

src/services/search/Elastic.Documentation.Search/Common/ElasticsearchClientAccessor.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5-
using System.Text.Json.Serialization.Metadata;
65
using Elastic.Clients.Elasticsearch;
76
using Elastic.Clients.Elasticsearch.Serialization;
87
using Elastic.Documentation.Configuration;
@@ -65,18 +64,12 @@ SearchConfiguration searchConfiguration
6564
? new BasicAuthentication(username, password)
6665
: null!;
6766

68-
// Combine the contract's source-gen context with a reflection fallback so that
69-
// internal package types (e.g. RuleQueryMatchCriteria from the Elasticsearch impl
70-
// package) are still serializable when the ES client delegates to the source serializer.
71-
var resolver = JsonTypeInfoResolver.Combine(
72-
SourceGenerationContext.Default,
73-
new DefaultJsonTypeInfoResolver()
74-
);
75-
7667
_clientSettings = new ElasticsearchClientSettings(
7768
_nodePool,
78-
sourceSerializer: (_, settings) => new DefaultSourceSerializer(settings, resolver, null)
79-
)
69+
sourceSerializer: (_, settings) => new DefaultSourceSerializer(
70+
settings,
71+
ElasticsearchClientJsonResolver.Default,
72+
static jsonOptions => jsonOptions.Converters.Add(RuleQueryMatchCriteriaJsonConverterFactory.Instance)))
8073
.DefaultIndex(SearchIndex)
8174
.Authentication(auth);
8275

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System.Text.Json.Serialization.Metadata;
6+
using Elastic.Documentation.Serialization;
7+
using InternalSearch = Elastic.Internal.Search;
8+
9+
namespace Elastic.Documentation.Search.Common;
10+
11+
/// <summary>
12+
/// Combined JSON type info resolver for the shared Elasticsearch client: external search contract types,
13+
/// docs-builder document metadata, and internal query-rule criteria from the Elasticsearch search package.
14+
/// </summary>
15+
internal static class ElasticsearchClientJsonResolver
16+
{
17+
public static IJsonTypeInfoResolver Default { get; } = Create();
18+
19+
private static IJsonTypeInfoResolver Create()
20+
{
21+
var combined = JsonTypeInfoResolver.Combine(
22+
InternalSearch.SourceGenerationContext.Default,
23+
SourceGenerationContext.Default);
24+
25+
return new RuleQueryMatchCriteriaTypeInfoResolver(combined);
26+
}
27+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System.Diagnostics.CodeAnalysis;
6+
using System.Reflection;
7+
using System.Text.Json;
8+
using System.Text.Json.Serialization;
9+
10+
namespace Elastic.Documentation.Search.Common;
11+
12+
/// <summary>
13+
/// Serializes <c>Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria</c>, which is internal to the
14+
/// Elasticsearch search package and therefore not covered by public <see cref="JsonSerializerContext"/> types.
15+
/// </summary>
16+
internal sealed class RuleQueryMatchCriteriaJsonConverterFactory : JsonConverterFactory
17+
{
18+
public static readonly RuleQueryMatchCriteriaJsonConverterFactory Instance = new();
19+
20+
[DynamicDependency(
21+
DynamicallyAccessedMemberTypes.All,
22+
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria",
23+
"Elastic.Internal.Search.Elasticsearch")]
24+
private static readonly Type RuleQueryMatchCriteriaType = Type.GetType(
25+
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria, Elastic.Internal.Search.Elasticsearch",
26+
throwOnError: true)!;
27+
28+
public override bool CanConvert(Type typeToConvert) => typeToConvert == RuleQueryMatchCriteriaType;
29+
30+
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
31+
RuleQueryMatchCriteriaJsonConverter.Instance;
32+
}
33+
34+
internal sealed class RuleQueryMatchCriteriaJsonConverter : JsonConverter<object>
35+
{
36+
public static readonly RuleQueryMatchCriteriaJsonConverter Instance = new();
37+
38+
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
39+
throw new NotSupportedException();
40+
41+
public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
42+
{
43+
if (value is null)
44+
{
45+
writer.WriteNullValue();
46+
return;
47+
}
48+
49+
var queryString = RuleQueryMatchCriteriaAccessors.GetQueryString(value);
50+
writer.WriteStartObject();
51+
writer.WriteString("query_string", queryString);
52+
writer.WriteEndObject();
53+
}
54+
}
55+
56+
internal static class RuleQueryMatchCriteriaAccessors
57+
{
58+
[DynamicDependency(
59+
DynamicallyAccessedMemberTypes.PublicProperties,
60+
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria",
61+
"Elastic.Internal.Search.Elasticsearch")]
62+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
63+
private static readonly Type RuleQueryMatchCriteriaType = Type.GetType(
64+
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria, Elastic.Internal.Search.Elasticsearch",
65+
throwOnError: true)!;
66+
67+
private static readonly PropertyInfo QueryStringProperty = RuleQueryMatchCriteriaType.GetProperty(
68+
"QueryString",
69+
BindingFlags.Public | BindingFlags.Instance)!;
70+
71+
[UnconditionalSuppressMessage(
72+
"Trimming",
73+
"IL2075",
74+
Justification = "RuleQueryMatchCriteria.QueryString is preserved via DynamicDependency on RuleQueryMatchCriteriaType.")]
75+
public static string GetQueryString(object target) => (string)QueryStringProperty.GetValue(target)!;
76+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System.Diagnostics.CodeAnalysis;
6+
using System.Text.Json;
7+
using System.Text.Json.Serialization.Metadata;
8+
9+
namespace Elastic.Documentation.Search.Common;
10+
11+
/// <summary>
12+
/// Supplies <see cref="JsonTypeInfo"/> for internal Elasticsearch search query types not declared on public contexts.
13+
/// </summary>
14+
internal sealed class RuleQueryMatchCriteriaTypeInfoResolver(IJsonTypeInfoResolver inner) : IJsonTypeInfoResolver
15+
{
16+
[DynamicDependency(
17+
DynamicallyAccessedMemberTypes.All,
18+
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria",
19+
"Elastic.Internal.Search.Elasticsearch")]
20+
private static readonly Type RuleQueryMatchCriteriaType = Type.GetType(
21+
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria, Elastic.Internal.Search.Elasticsearch",
22+
throwOnError: true)!;
23+
24+
private JsonTypeInfo? _ruleQueryMatchCriteriaTypeInfo;
25+
26+
public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options)
27+
{
28+
if (type == RuleQueryMatchCriteriaType)
29+
{
30+
return _ruleQueryMatchCriteriaTypeInfo ??= CreateRuleQueryMatchCriteriaTypeInfo(options);
31+
}
32+
33+
return inner.GetTypeInfo(type, options);
34+
}
35+
36+
[UnconditionalSuppressMessage(
37+
"Trimming",
38+
"IL2026",
39+
Justification = "RuleQueryMatchCriteria is internal to Elastic.Internal.Search.Elasticsearch; serialization uses an UnsafeAccessor-based converter.")]
40+
[UnconditionalSuppressMessage(
41+
"AOT",
42+
"IL3050",
43+
Justification = "RuleQueryMatchCriteria is internal to Elastic.Internal.Search.Elasticsearch; serialization uses an UnsafeAccessor-based converter.")]
44+
private static JsonTypeInfo CreateRuleQueryMatchCriteriaTypeInfo(JsonSerializerOptions options) =>
45+
JsonTypeInfo.CreateJsonTypeInfo(RuleQueryMatchCriteriaType, options);
46+
}

src/services/search/Elastic.Documentation.Search/Elastic.Documentation.Search.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<Nullable>enable</Nullable>
77
<AssemblyName>Elastic.Documentation.Search</AssemblyName>
88
<RootNamespace>Elastic.Documentation.Search</RootNamespace>
9+
<IsAotCompatible>true</IsAotCompatible>
910
</PropertyGroup>
1011

1112
<ItemGroup>

0 commit comments

Comments
 (0)