Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Elastic.Transport to 0.5.5 #8421

Merged
merged 5 commits into from
Nov 21, 2024
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
46 changes: 38 additions & 8 deletions docs/migration-guide.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -292,13 +292,43 @@ As a last resort, the low-level client `Elastic.Transport` can be used to create

[source,csharp]
----
public class MyRequestParameters : RequestParameters
{
public bool Pretty
{
get => Q<bool>("pretty");
init => Q("pretty", value);
}
}

// ...

var body = """
{
"name": "my-api-key",
"expiration": "1d",
"...": "..."
}
""";

var response = await client.Transport.RequestAsync<StringResponse>(HttpMethod.POST, "/_security/api_key", PostData.String(body));
{
"name": "my-api-key",
"expiration": "1d",
"...": "..."
}
""";

MyRequestParameters requestParameters = new()
{
Pretty = true
};

var pathAndQuery = requestParameters.CreatePathWithQueryStrings("/_security/api_key",
client.ElasticsearchClientSettings);
var endpointPath = new EndpointPath(Elastic.Transport.HttpMethod.POST, pathAndQuery);

// Or, if the path does not contain query parameters:
// new EndpointPath(Elastic.Transport.HttpMethod.POST, "my_path")

var response = await client.Transport
.RequestAsync<StringResponse>(
endpointPath,
PostData.String(body),
null,
null,
cancellationToken: default)
.ConfigureAwait(false);
----
9 changes: 5 additions & 4 deletions docs/usage/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ If you're new to {es}, make sure also to read {ref}/getting-started.html[Elastic

NOTE: This is still a work in progress, more sections will be added in the near future.

include::recommendations.asciidoc[]
include::aggregations.asciidoc[]
include::esql.asciidoc[]
include::examples.asciidoc[]
include::query.asciidoc[]
include::mappings.asciidoc[]
include::aggregations.asciidoc[]
include::esql.asciidoc[]
include::query.asciidoc[]
include::recommendations.asciidoc[]
include::transport.asciidoc[]
47 changes: 47 additions & 0 deletions docs/usage/transport.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[[transport]]
== Transport example

This page demonstrates how to use the low level transport to send requests.

[source,csharp]
----
public class MyRequestParameters : RequestParameters
{
public bool Pretty
{
get => Q<bool>("pretty");
init => Q("pretty", value);
}
}

// ...

var body = """
{
"name": "my-api-key",
"expiration": "1d",
"...": "..."
}
""";

MyRequestParameters requestParameters = new()
{
Pretty = true
};

var pathAndQuery = requestParameters.CreatePathWithQueryStrings("/_security/api_key",
client.ElasticsearchClientSettings);
var endpointPath = new EndpointPath(Elastic.Transport.HttpMethod.POST, pathAndQuery);

// Or, if the path does not contain query parameters:
// new EndpointPath(Elastic.Transport.HttpMethod.POST, "my_path")

var response = await client.Transport
.RequestAsync<StringResponse>(
endpointPath,
PostData.String(body),
null,
null,
cancellationToken: default)
.ConfigureAwait(false);
----
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public class ApiVersionMetaHeaderProducer : MetaHeaderProducer

public override string HeaderName => "Elastic-Api-Version";

public override string ProduceHeaderValue(RequestData requestData, bool isAsync) => _apiVersion;
public override string ProduceHeaderValue(BoundConfiguration boundConfiguration, bool isAsync) => _apiVersion;

public ApiVersionMetaHeaderProducer(VersionInfo version)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<Nullable>annotations</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Elastic.Transport" Version="0.5.2" />
<PackageReference Include="Elastic.Transport" Version="0.5.5" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Tests" Key="$(ExposedPublicKey)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<Nullable>annotations</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Elastic.Transport" Version="0.5.2" />
<PackageReference Include="Elastic.Transport" Version="0.5.5" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Tests" Key="$(ExposedPublicKey)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Elastic.Clients.Elasticsearch.Esql;

internal sealed class EsqlResponseBuilder : TypedResponseBuilder<EsqlQueryResponse>
{
protected override EsqlQueryResponse? Build(ApiCallDetails apiCallDetails, RequestData requestData,
protected override EsqlQueryResponse? Build(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration,
Stream responseStream,
string contentType, long contentLength)
{
Expand All @@ -38,7 +38,7 @@ static byte[] BytesFromStream(Stream stream)
}
}

protected override async Task<EsqlQueryResponse?> BuildAsync(ApiCallDetails apiCallDetails, RequestData requestData,
protected override async Task<EsqlQueryResponse?> BuildAsync(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration,
Stream responseStream,
string contentType, long contentLength, CancellationToken cancellationToken = default)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.Json;
Expand Down Expand Up @@ -165,11 +166,11 @@ private ValueTask<TResponse> DoRequestCoreAsync<TRequest, TResponse, TRequestPar
ValueTask<TResponse> SendRequest()
{
var (endpointPath, resolvedRouteValues, postData) = PrepareRequest<TRequest, TRequestParameters>(request);
var openTelemetryData = PrepareOpenTelemetryData<TRequest, TRequestParameters>(request, resolvedRouteValues);
var openTelemetryDataMutator = GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(request, resolvedRouteValues);

return isAsync
? new ValueTask<TResponse>(_transport.RequestAsync<TResponse>(endpointPath, postData, in openTelemetryData, request.RequestConfig, cancellationToken))
: new ValueTask<TResponse>(_transport.Request<TResponse>(endpointPath, postData, in openTelemetryData, request.RequestConfig));
? new ValueTask<TResponse>(_transport.RequestAsync<TResponse>(endpointPath, postData, openTelemetryDataMutator, request.RequestConfig, cancellationToken))
: new ValueTask<TResponse>(_transport.Request<TResponse>(endpointPath, postData, openTelemetryDataMutator, request.RequestConfig));
}

async ValueTask<TResponse> SendRequestWithProductCheck()
Expand Down Expand Up @@ -211,19 +212,19 @@ async ValueTask<TResponse> SendRequestWithProductCheckCore()
// Send request

var (endpointPath, resolvedRouteValues, postData) = PrepareRequest<TRequest, TRequestParameters>(request);
var openTelemetryData = PrepareOpenTelemetryData<TRequest, TRequestParameters>(request, resolvedRouteValues);
var openTelemetryDataMutator = GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(request, resolvedRouteValues);

TResponse response;

if (isAsync)
{
response = await _transport
.RequestAsync<TResponse>(endpointPath, postData, in openTelemetryData, requestConfig, cancellationToken)
.RequestAsync<TResponse>(endpointPath, postData, openTelemetryDataMutator, requestConfig, cancellationToken)
.ConfigureAwait(false);
}
else
{
response = _transport.Request<TResponse>(endpointPath, postData, in openTelemetryData, requestConfig);
response = _transport.Request<TResponse>(endpointPath, postData, openTelemetryDataMutator, requestConfig);
}

// Evaluate product check result
Expand Down Expand Up @@ -252,39 +253,41 @@ async ValueTask<TResponse> SendRequestWithProductCheckCore()
}
}

private static OpenTelemetryData PrepareOpenTelemetryData<TRequest, TRequestParameters>(TRequest request, Dictionary<string, string> resolvedRouteValues)
private static Action<Activity>? GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(TRequest request, Dictionary<string, string>? resolvedRouteValues)
where TRequest : Request<TRequestParameters>
where TRequestParameters : RequestParameters, new()
{
// If there are no subscribed listeners, we avoid some work and allocations
if (!Elastic.Transport.Diagnostics.OpenTelemetry.ElasticTransportActivitySourceHasListeners)
return default;
return null;

// We fall back to a general operation name in cases where the derived request fails to override the property
var operationName = !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : request.HttpMethod.GetStringValue();
return OpenTelemetryDataMutator;

// TODO: Optimisation: We should consider caching these, either for cases where resolvedRouteValues is null, or
// caching per combination of route values.
// We should benchmark this first to assess the impact for common workloads.
// The former is likely going to save some short-lived allocations, but only for requests to endpoints without required path parts.
// The latter may bloat the cache as some combinations of path parts may rarely re-occur.
var attributes = new Dictionary<string, object>
void OpenTelemetryDataMutator(Activity activity)
{
[OpenTelemetry.SemanticConventions.DbOperation] = !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : "unknown",
[$"{OpenTelemetrySpanAttributePrefix}schema_url"] = OpenTelemetrySchemaVersion
};
// We fall back to a general operation name in cases where the derived request fails to override the property
var operationName = !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : request.HttpMethod.GetStringValue();

// TODO: Optimisation: We should consider caching these, either for cases where resolvedRouteValues is null, or
// caching per combination of route values.
// We should benchmark this first to assess the impact for common workloads.
// The former is likely going to save some short-lived allocations, but only for requests to endpoints without required path parts.
// The latter may bloat the cache as some combinations of path parts may rarely re-occur.

activity.DisplayName = operationName;

activity.SetTag(OpenTelemetry.SemanticConventions.DbOperation, !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : "unknown");
activity.SetTag($"{OpenTelemetrySpanAttributePrefix}schema_url", OpenTelemetrySchemaVersion);

if (resolvedRouteValues is null)
return;

if (resolvedRouteValues is not null)
{
foreach (var value in resolvedRouteValues)
{
if (!string.IsNullOrEmpty(value.Key) && !string.IsNullOrEmpty(value.Value))
attributes.Add($"{OpenTelemetrySpanAttributePrefix}path_parts.{value.Key}", value.Value);
activity.SetTag($"{OpenTelemetrySpanAttributePrefix}path_parts.{value.Key}", value.Value);
}
}

var openTelemetryData = new OpenTelemetryData { SpanName = operationName, SpanAttributes = attributes };
return openTelemetryData;
}

private (EndpointPath endpointPath, Dictionary<string, string>? resolvedRouteValues, PostData data) PrepareRequest<TRequest, TRequestParameters>(TRequest request)
Expand Down
2 changes: 1 addition & 1 deletion src/Playground/Playground.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<ItemGroup>
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Elastic.Transport" Version="0.5.2" />
<PackageReference Include="Elastic.Transport" Version="0.5.5" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>

Expand Down
6 changes: 3 additions & 3 deletions tests/Tests.Core/Client/FixedResponseClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static ElasticsearchClient Create(
object response,
int statusCode = 200,
Func<ElasticsearchClientSettings, ElasticsearchClientSettings> modifySettings = null,
string contentType = RequestData.DefaultContentType,
string contentType = BoundConfiguration.DefaultContentType,
Exception exception = null
)
{
Expand All @@ -29,7 +29,7 @@ public static ElasticsearchClientSettings CreateConnectionSettings(
object response,
int statusCode = 200,
Func<ElasticsearchClientSettings, ElasticsearchClientSettings> modifySettings = null,
string contentType = RequestData.DefaultContentType,
string contentType = BoundConfiguration.DefaultContentType,
Exception exception = null,
Serializer serializer = null
)
Expand All @@ -46,7 +46,7 @@ public static ElasticsearchClientSettings CreateConnectionSettings(
break;
default:
{
responseBytes = contentType == RequestData.DefaultContentType
responseBytes = contentType == BoundConfiguration.DefaultContentType
? serializer.SerializeToBytes(response,
TestClient.Default.ElasticsearchClientSettings.MemoryStreamFactory)
: Encoding.UTF8.GetBytes(response.ToString());
Expand Down
16 changes: 8 additions & 8 deletions tests/Tests/ClientConcepts/OpenTelemetry/OpenTelemetryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,34 @@ public async Task BasicOpenTelemetryTest()

client.Ping();

VerifyActivity(oTelActivity, "ping");
VerifyActivity(oTelActivity, "ping", "HEAD");

await client.PingAsync();

VerifyActivity(oTelActivity, "ping");
VerifyActivity(oTelActivity, "ping", "HEAD");

await client.SearchAsync<Project>(s => s.Index("test").Query(q => q.MatchAll(m => { })));

VerifyActivity(oTelActivity, "search", "http://localhost:9200/test/_search?pretty=true&error_trace=true");
VerifyActivity(oTelActivity, "search", "POST", "http://localhost:9200/test/_search?pretty=true&error_trace=true");

static void VerifyActivity(Activity oTelActivity, string operation, string url = null)
static void VerifyActivity(Activity oTelActivity, string displayName, string operation, string url = null)
{
oTelActivity.Should().NotBeNull();

oTelActivity.Kind.Should().Be(ActivityKind.Client);

oTelActivity.DisplayName.Should().Be(operation);
oTelActivity.OperationName.Should().Be(operation);
oTelActivity.DisplayName.Should().Be(displayName);

oTelActivity.Tags.Should().Contain(n => n.Key == "elastic.transport.product.name" && n.Value == "elasticsearch-net");
oTelActivity.Tags.Should().Contain(n => n.Key == "db.system" && n.Value == "elasticsearch");
oTelActivity.Tags.Should().Contain(n => n.Key == "db.operation" && n.Value == operation);
oTelActivity.Tags.Should().Contain(n => n.Key == "db.operation" && n.Value == displayName);
oTelActivity.Tags.Should().Contain(n => n.Key == "db.user" && n.Value == "elastic");
oTelActivity.Tags.Should().Contain(n => n.Key == "url.full" && n.Value == (url ?? "http://localhost:9200/?pretty=true&error_trace=true"));
oTelActivity.Tags.Should().Contain(n => n.Key == "server.address" && n.Value == "localhost");
oTelActivity.Tags.Should().Contain(n => n.Key == "http.request.method" && n.Value == (operation == "ping" ? "HEAD" : "POST"));
oTelActivity.Tags.Should().Contain(n => n.Key == "http.request.method" && n.Value == (displayName == "ping" ? "HEAD" : "POST"));

switch (operation)
switch (displayName)
{
case "search":
oTelActivity.Tags.Should().Contain(n => n.Key == "db.elasticsearch.path_parts.index" && n.Value == "test");
Expand Down
Loading