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 @@ -61,6 +61,10 @@ internal sealed class ExecuteHttpRequestSpan(
return new ExecuteHttpRequestSpan(activity, httpContext, kind, enricher, options);
}

public bool IsBatch { get; private set; }

public void MarkAsBatch() => IsBatch = true;

public void SetSingleRequestDetails(GraphQLRequest request)
{
Activity.SetTag(GraphQL.Http.Request.Type, GraphQL.Http.Request.Types.Single);
Expand Down Expand Up @@ -122,13 +126,13 @@ public void SetBatchRequestDetails(IReadOnlyList<GraphQLRequest> batch)
if (request.DocumentId is not null
&& (options.RequestDetails & RequestDetails.Id) == RequestDetails.Id)
{
Activity.SetTag(GraphQL.Http.Request.BatchRequest.QueryId(i), request.DocumentId.Value);
Activity.SetTag(GraphQL.Http.Request.BatchRequest.QueryId(i), request.DocumentId.Value.Value);
}

if (request.DocumentHash is not null
&& (options.RequestDetails & RequestDetails.Hash) == RequestDetails.Hash)
{
Activity.SetTag(GraphQL.Http.Request.BatchRequest.QueryHash(i), request.DocumentHash.Value);
Activity.SetTag(GraphQL.Http.Request.BatchRequest.QueryHash(i), request.DocumentHash.Value.Value);
}

if (request.Document is not null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ public override IDisposable ExecuteRequest(RequestContext context)
return EmptyScope;
}

// A GraphQL over HTTP request reuses the transport span as the root request
// span instead of emitting a second one. A transport that has no span of its
// own, like a WebSocket session, falls through to a dedicated request span.
if (httpContext.Features.Get<ExecuteHttpRequestSpan>() is { } httpRequestSpan)
if (httpContext.Features.Get<ExecuteHttpRequestSpan>() is { IsBatch: false } httpRequestSpan)
{
httpContextActivity = httpRequestSpan.Activity;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,14 @@ public override void StartSingleRequest(HttpContext context, GraphQLRequest requ

public override void StartBatchRequest(HttpContext context, IReadOnlyList<GraphQLRequest> batch)
{
if (options.IncludeRequestDetails
&& context.Features.Get<ExecuteHttpRequestSpan>() is { } span)
if (context.Features.Get<ExecuteHttpRequestSpan>() is not { } span)
{
return;
}

span.MarkAsBatch();

if (options.IncludeRequestDetails)
{
span.SetBatchRequestDetails(batch);
}
Expand All @@ -55,8 +61,14 @@ public override void StartOperationBatchRequest(
GraphQLRequest request,
IReadOnlyList<string> operations)
{
if (options.IncludeRequestDetails
&& context.Features.Get<ExecuteHttpRequestSpan>() is { } span)
if (context.Features.Get<ExecuteHttpRequestSpan>() is not { } span)
{
return;
}

span.MarkAsBatch();
Comment thread
tobias-tengler marked this conversation as resolved.

if (options.IncludeRequestDetails)
{
span.SetOperationBatchRequestDetails(request, operations);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,63 @@ public async Task Http_Get_PersistedOperationEndpoint_GetHeroName_Default()
}
}

[Fact]
public async Task Http_Post_BatchRequest_Should_Give_Every_Item_Its_Own_Request_Span_Default()
{
using (CaptureActivities(out var activities))
{
// arrange
using var server = CreateInstrumentedServer();
using var client = server.CreateClient();
client.BaseAddress = new Uri("http://localhost:5000");

const string batch =
"""
[{"query":"query A { hero { name } }"},
{"query":"query B { human(id: \"1000\") { name } }"}]
""";

// act
using var content = new StringContent(batch, Encoding.UTF8, "application/json");
using var response = await client.PostAsync(
"/graphql",
content,
TestContext.Current.CancellationToken);
await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);

// assert
activities.MatchSnapshot(Postfix([NET11_0]));
}
}

[Fact]
public async Task Http_Post_OperationBatchRequest_Should_Give_Every_Item_Its_Own_Request_Span_Default()
{
using (CaptureActivities(out var activities))
{
// arrange
using var server = CreateInstrumentedServer();
using var client = server.CreateClient();
client.BaseAddress = new Uri("http://localhost:5000");

const string request =
"""
{"query":"query A { hero { name } } query B { human(id: \"1000\") { name } }"}
""";

// act
using var content = new StringContent(request, Encoding.UTF8, "application/json");
using var response = await client.PostAsync(
"/graphql?batchOperations=[A,B]",
content,
TestContext.Current.CancellationToken);
await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);

// assert
activities.MatchSnapshot(Postfix([NET11_0]));
}
}

[Fact]
public async Task Http_Post_Variables_Are_Not_Automatically_Added_To_Activities()
{
Expand Down
Loading