Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System.Text.Json.Serialization.Metadata;
using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.Serialization;
using Elastic.Documentation.Configuration;
Expand Down Expand Up @@ -65,18 +64,12 @@ SearchConfiguration searchConfiguration
? new BasicAuthentication(username, password)
: null!;

// Combine the contract's source-gen context with a reflection fallback so that
// internal package types (e.g. RuleQueryMatchCriteria from the Elasticsearch impl
// package) are still serializable when the ES client delegates to the source serializer.
var resolver = JsonTypeInfoResolver.Combine(
SourceGenerationContext.Default,
new DefaultJsonTypeInfoResolver()
);

_clientSettings = new ElasticsearchClientSettings(
_nodePool,
sourceSerializer: (_, settings) => new DefaultSourceSerializer(settings, resolver, null)
)
sourceSerializer: (_, settings) => new DefaultSourceSerializer(
settings,
ElasticsearchClientJsonResolver.Default,
static jsonOptions => jsonOptions.Converters.Add(RuleQueryMatchCriteriaJsonConverterFactory.Instance)))
.DefaultIndex(SearchIndex)
.Authentication(auth);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System.Text.Json.Serialization.Metadata;
using Elastic.Documentation.Serialization;
using InternalSearch = Elastic.Internal.Search;

namespace Elastic.Documentation.Search.Common;

/// <summary>
/// Combined JSON type info resolver for the shared Elasticsearch client: external search contract types,
/// docs-builder document metadata, and internal query-rule criteria from the Elasticsearch search package.
/// </summary>
internal static class ElasticsearchClientJsonResolver
{
public static IJsonTypeInfoResolver Default { get; } = Create();

private static IJsonTypeInfoResolver Create()
{
var combined = JsonTypeInfoResolver.Combine(
InternalSearch.SourceGenerationContext.Default,
SourceGenerationContext.Default);

return new RuleQueryMatchCriteriaTypeInfoResolver(combined);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Elastic.Documentation.Search.Common;

/// <summary>
/// Serializes <c>Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria</c>, which is internal to the
/// Elasticsearch search package and therefore not covered by public <see cref="JsonSerializerContext"/> types.
/// </summary>
internal sealed class RuleQueryMatchCriteriaJsonConverterFactory : JsonConverterFactory
{
public static readonly RuleQueryMatchCriteriaJsonConverterFactory Instance = new();

[DynamicDependency(
DynamicallyAccessedMemberTypes.All,
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria",
"Elastic.Internal.Search.Elasticsearch")]
private static readonly Type RuleQueryMatchCriteriaType = Type.GetType(
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria, Elastic.Internal.Search.Elasticsearch",
throwOnError: true)!;

public override bool CanConvert(Type typeToConvert) => typeToConvert == RuleQueryMatchCriteriaType;

public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
RuleQueryMatchCriteriaJsonConverter.Instance;
}

internal sealed class RuleQueryMatchCriteriaJsonConverter : JsonConverter<object>
{
public static readonly RuleQueryMatchCriteriaJsonConverter Instance = new();

public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
throw new NotSupportedException();

public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
{
if (value is null)
{
writer.WriteNullValue();
return;
}

var queryString = RuleQueryMatchCriteriaAccessors.GetQueryString(value);
writer.WriteStartObject();
writer.WriteString("query_string", queryString);
writer.WriteEndObject();
}
}

internal static class RuleQueryMatchCriteriaAccessors
{
[DynamicDependency(
DynamicallyAccessedMemberTypes.PublicProperties,
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria",
"Elastic.Internal.Search.Elasticsearch")]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
private static readonly Type RuleQueryMatchCriteriaType = Type.GetType(
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria, Elastic.Internal.Search.Elasticsearch",
throwOnError: true)!;

private static readonly PropertyInfo QueryStringProperty = RuleQueryMatchCriteriaType.GetProperty(
"QueryString",
BindingFlags.Public | BindingFlags.Instance)!;

[UnconditionalSuppressMessage(
"Trimming",
"IL2075",
Justification = "RuleQueryMatchCriteria.QueryString is preserved via DynamicDependency on RuleQueryMatchCriteriaType.")]
public static string GetQueryString(object target) => (string)QueryStringProperty.GetValue(target)!;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

namespace Elastic.Documentation.Search.Common;

/// <summary>
/// Supplies <see cref="JsonTypeInfo"/> for internal Elasticsearch search query types not declared on public contexts.
/// </summary>
internal sealed class RuleQueryMatchCriteriaTypeInfoResolver(IJsonTypeInfoResolver inner) : IJsonTypeInfoResolver
{
[DynamicDependency(
DynamicallyAccessedMemberTypes.All,
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria",
"Elastic.Internal.Search.Elasticsearch")]
private static readonly Type RuleQueryMatchCriteriaType = Type.GetType(
"Elastic.Internal.Search.Elasticsearch.RuleQueryMatchCriteria, Elastic.Internal.Search.Elasticsearch",
throwOnError: true)!;

private JsonTypeInfo? _ruleQueryMatchCriteriaTypeInfo;

public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options)
{
if (type == RuleQueryMatchCriteriaType)
{
return _ruleQueryMatchCriteriaTypeInfo ??= CreateRuleQueryMatchCriteriaTypeInfo(options);
}

return inner.GetTypeInfo(type, options);
}

[UnconditionalSuppressMessage(
"Trimming",
"IL2026",
Justification = "RuleQueryMatchCriteria is internal to Elastic.Internal.Search.Elasticsearch; serialization uses an UnsafeAccessor-based converter.")]
[UnconditionalSuppressMessage(
"AOT",
"IL3050",
Justification = "RuleQueryMatchCriteria is internal to Elastic.Internal.Search.Elasticsearch; serialization uses an UnsafeAccessor-based converter.")]
private static JsonTypeInfo CreateRuleQueryMatchCriteriaTypeInfo(JsonSerializerOptions options) =>
JsonTypeInfo.CreateJsonTypeInfo(RuleQueryMatchCriteriaType, options);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<AssemblyName>Elastic.Documentation.Search</AssemblyName>
<RootNamespace>Elastic.Documentation.Search</RootNamespace>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading