diff --git a/src/ZeroQL.Runtime/GraphQLClient.cs b/src/ZeroQL.Runtime/GraphQLClient.cs index b11f7fe..40e6e9b 100644 --- a/src/ZeroQL.Runtime/GraphQLClient.cs +++ b/src/ZeroQL.Runtime/GraphQLClient.cs @@ -89,11 +89,11 @@ public async Task> Execute(result.Query, default, result.Errors, result.Extensions); + return new GraphQLResult(result.HttpResponseMessage, result.Query, default, result.Errors, result.Extensions); } var mappedData = queryMapper(variables, result.Data); - return new GraphQLResult(result.Query, mappedData, result.Errors, result.Extensions); + return new GraphQLResult(result.HttpResponseMessage, result.Query, mappedData, result.Errors, result.Extensions); } public void Dispose() diff --git a/src/ZeroQL.Runtime/GraphQLResult.cs b/src/ZeroQL.Runtime/GraphQLResult.cs index 44b8ec5..f78bc33 100644 --- a/src/ZeroQL.Runtime/GraphQLResult.cs +++ b/src/ZeroQL.Runtime/GraphQLResult.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Net.Http; namespace ZeroQL; @@ -14,6 +15,8 @@ public interface IGraphQLResult public GraphQueryError[]? Errors { get; } public Dictionary? Extensions { get; } + + public HttpResponseMessage HttpResponseMessage { get; set; } } public class GraphQLResult : IGraphQLResult @@ -23,12 +26,13 @@ public GraphQLResult() } - public GraphQLResult(string query, TData? data, GraphQueryError[]? errors, Dictionary? extensions) + public GraphQLResult(HttpResponseMessage responseMessage, string query, TData? data, GraphQueryError[]? errors, Dictionary? extensions) { Query = query; Data = data; Errors = errors; Extensions = extensions; + HttpResponseMessage = responseMessage; } public string Query { get; set; } @@ -38,6 +42,8 @@ public GraphQLResult(string query, TData? data, GraphQueryError[]? errors, Dicti public GraphQueryError[]? Errors { get; set; } public Dictionary? Extensions { get; set; } + + public HttpResponseMessage HttpResponseMessage { get; set; } } public record GraphQLResponse @@ -49,6 +55,8 @@ public record GraphQLResponse public GraphQueryError[]? Errors { get; set; } public Dictionary? Extensions { get; set; } + + public HttpResponseMessage HttpResponseMessage { get; set; } } public class GraphQueryError diff --git a/src/ZeroQL.Runtime/Internal/HttpResponseMessageExtension.cs b/src/ZeroQL.Runtime/Internal/HttpResponseMessageExtension.cs index 2aba7b9..19e1c10 100644 --- a/src/ZeroQL.Runtime/Internal/HttpResponseMessageExtension.cs +++ b/src/ZeroQL.Runtime/Internal/HttpResponseMessageExtension.cs @@ -15,16 +15,19 @@ public static async Task> ReadGraphQLResponse( CancellationToken cancellationToken) { const string responseContentTypeStartWith = "application/graphql-response"; - if (!response.IsSuccessStatusCode && !(response.Content.Headers.ContentType?.MediaType?.StartsWith(responseContentTypeStartWith) ?? false)) + var graphqlResponse = response.Content.Headers.ContentType?.MediaType?.StartsWith(responseContentTypeStartWith); + if (!response.IsSuccessStatusCode && !(graphqlResponse ?? false)) { var responseContent = await response.Content.ReadAsStringAsync(); return new GraphQLResponse { + HttpResponseMessage = response, Errors = [ new() { - Message = $"""HTTP request failed unexpectedly with status code {(int)response.StatusCode}. Look at the Extensions.ZeroQLError extension for more details""", + Message = + $"""HTTP request failed unexpectedly with status code {(int)response.StatusCode}. Look at the Extensions.ZeroQLError extension for more details""", } ], Extensions = new() @@ -39,7 +42,7 @@ public static async Task> ReadGraphQLResponse( } }; } - + #if DEBUG var responseJson = await response.Content.ReadAsStringAsync(); var qlResponse = @@ -62,11 +65,13 @@ public static async Task> ReadGraphQLResponse( { return new GraphQLResponse { + HttpResponseMessage = response, Errors = [ new() { - Message = """Failed to deserialize the response from the server. Look at the Extensions.ZeroQLError extension for more details""", + Message = + """Failed to deserialize the response from the server. Look at the Extensions.ZeroQLError extension for more details""", } ], Extensions = new() @@ -80,7 +85,10 @@ public static async Task> ReadGraphQLResponse( } }; } - - return qlResponse; - } + + return qlResponse with + { + HttpResponseMessage = response + }; + } } \ No newline at end of file diff --git a/src/ZeroQL.SourceGenerators/Resolver/GraphQLSourceResolver.cs b/src/ZeroQL.SourceGenerators/Resolver/GraphQLSourceResolver.cs index 31897fe..b668fbc 100644 --- a/src/ZeroQL.SourceGenerators/Resolver/GraphQLSourceResolver.cs +++ b/src/ZeroQL.SourceGenerators/Resolver/GraphQLSourceResolver.cs @@ -61,6 +61,7 @@ public static void Init() {{ return new GraphQLResult<{context.QueryTypeName}> {{ + HttpResponseMessage = qlResponse.HttpResponseMessage, Errors = new[] {{ new GraphQueryError {{ Message = ""Failed to deserialize response"" }} @@ -70,6 +71,7 @@ public static void Init() return new GraphQLResult<{context.QueryTypeName}> {{ + HttpResponseMessage = qlResponse.HttpResponseMessage, Query = qlResponse.Query, Data = qlResponse.Data, Errors = qlResponse.Errors?.Length > 0 ? qlResponse.Errors : null, diff --git a/src/ZeroQL.TestServer/Program.cs b/src/ZeroQL.TestServer/Program.cs index eff3fc5..8f82680 100644 --- a/src/ZeroQL.TestServer/Program.cs +++ b/src/ZeroQL.TestServer/Program.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Net; using HotChocolate.Language; using HotChocolate.Types.NodaTime; @@ -58,6 +59,14 @@ public static WebApplication CreateApp(ServerContext context, WebApplicationBuil var app = builder.Build(); + app.Use(async (context, next) => + { + var traceId = Guid.NewGuid().ToString(); + context.Response.Headers.TryAdd("trace-id", traceId); + + await next(); + }); + app.MapGraphQL(); return app; diff --git a/src/ZeroQL.Tests/Core/TestExtensions.cs b/src/ZeroQL.Tests/Core/TestExtensions.cs index 59e76b8..a0ef0ca 100644 --- a/src/ZeroQL.Tests/Core/TestExtensions.cs +++ b/src/ZeroQL.Tests/Core/TestExtensions.cs @@ -188,7 +188,7 @@ public static string Preview(this Location location) var character = span.StartLinePosition.Character; var source = sourceTree .ToString() - .Split('\r', '\n'); + .Split('\r'); var lineWithPreview = source[line].Insert(character, "^"); diff --git a/src/ZeroQL.Tests/SourceGeneration/FileUploadTests.UploadFileGenerates.verified.txt b/src/ZeroQL.Tests/SourceGeneration/FileUploadTests.UploadFileGenerates.verified.txt index b03ab4d..6c12186 100644 --- a/src/ZeroQL.Tests/SourceGeneration/FileUploadTests.UploadFileGenerates.verified.txt +++ b/src/ZeroQL.Tests/SourceGeneration/FileUploadTests.UploadFileGenerates.verified.txt @@ -65,6 +65,7 @@ namespace ZeroQL.TestApp { return new GraphQLResult { + HttpResponseMessage = qlResponse.HttpResponseMessage, Errors = new[] { new GraphQueryError { Message = "Failed to deserialize response" } @@ -74,6 +75,7 @@ namespace ZeroQL.TestApp return new GraphQLResult { + HttpResponseMessage = qlResponse.HttpResponseMessage, Query = qlResponse.Query, Data = qlResponse.Data, Errors = qlResponse.Errors?.Length > 0 ? qlResponse.Errors : null, diff --git a/src/ZeroQL.Tests/SourceGeneration/QueryTests.LambdaModuleInitializerGenerated.verified.txt b/src/ZeroQL.Tests/SourceGeneration/QueryTests.LambdaModuleInitializerGenerated.verified.txt index 9919579..da9dae3 100644 --- a/src/ZeroQL.Tests/SourceGeneration/QueryTests.LambdaModuleInitializerGenerated.verified.txt +++ b/src/ZeroQL.Tests/SourceGeneration/QueryTests.LambdaModuleInitializerGenerated.verified.txt @@ -49,6 +49,7 @@ namespace ZeroQL.TestApp { return new GraphQLResult { + HttpResponseMessage = qlResponse.HttpResponseMessage, Errors = new[] { new GraphQueryError { Message = "Failed to deserialize response" } @@ -58,6 +59,7 @@ namespace ZeroQL.TestApp return new GraphQLResult { + HttpResponseMessage = qlResponse.HttpResponseMessage, Query = qlResponse.Query, Data = qlResponse.Data, Errors = qlResponse.Errors?.Length > 0 ? qlResponse.Errors : null, diff --git a/src/ZeroQL.Tests/SourceGeneration/QueryTests.QueryPreviewGenerated.verified.txt b/src/ZeroQL.Tests/SourceGeneration/QueryTests.QueryPreviewGenerated.verified.txt index 7b6a2e6..b3e868e 100644 --- a/src/ZeroQL.Tests/SourceGeneration/QueryTests.QueryPreviewGenerated.verified.txt +++ b/src/ZeroQL.Tests/SourceGeneration/QueryTests.QueryPreviewGenerated.verified.txt @@ -1 +1,2 @@ - var response = await qlClient.^Query(static q => new { Me = q.Me(o => new { o.FirstName }) }); \ No newline at end of file + + var response = await qlClient^.Query(static q => new { Me = q.Me(o => new { o.FirstName }) }); \ No newline at end of file diff --git a/src/ZeroQL.Tests/SourceGeneration/QueryTests.SimpleQuery.verified.txt b/src/ZeroQL.Tests/SourceGeneration/QueryTests.SimpleQuery.verified.txt new file mode 100644 index 0000000..d37639c --- /dev/null +++ b/src/ZeroQL.Tests/SourceGeneration/QueryTests.SimpleQuery.verified.txt @@ -0,0 +1,4 @@ +{ + Query: query { me { firstName } }, + Data: Jon +} \ No newline at end of file diff --git a/src/ZeroQL.Tests/SourceGeneration/QueryTests.cs b/src/ZeroQL.Tests/SourceGeneration/QueryTests.cs index fdebe6e..cc57a0b 100644 --- a/src/ZeroQL.Tests/SourceGeneration/QueryTests.cs +++ b/src/ZeroQL.Tests/SourceGeneration/QueryTests.cs @@ -14,16 +14,21 @@ public async Task CompilationWorks() { await TestProject.Project.CompileToRealAssembly(); } + + [Fact] + public async Task ResponseHeadersAvailable() + { + var result = (IGraphQLResult)await TestProject.Project.Execute(); + + result.HttpResponseMessage.Headers.Should().ContainKey("trace-id"); + } [Fact] public async Task SimpleQuery() { - var graphqlQuery = @"query { me { firstName } }"; - var project = TestProject.Project; + var result = await TestProject.Project.Execute(); - var result = (GraphQLResult)await project.Validate(graphqlQuery); - - result.Data.Should().Be("Jon"); + await Verify(result); } [Fact] diff --git a/src/ZeroQL.Tests/VerifyModuleInitializer.cs b/src/ZeroQL.Tests/VerifyModuleInitializer.cs new file mode 100644 index 0000000..f0e7e20 --- /dev/null +++ b/src/ZeroQL.Tests/VerifyModuleInitializer.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; + +namespace ZeroQL.Tests; + +public static class VerifyModuleInitializer +{ + [ModuleInitializer] + public static void Init() => + VerifierSettings.IgnoreMember(nameof(GraphQLResult.HttpResponseMessage)); +} \ No newline at end of file