Skip to content

Commit 194c152

Browse files
committed
allow access to HttpResponseHeaders
1 parent ccb8eb7 commit 194c152

File tree

3 files changed

+81
-7
lines changed

3 files changed

+81
-7
lines changed

src/GraphQL.Client/GraphQLHttpClient.cs

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Concurrent;
33
using System.Diagnostics;
44
using System.Net.Http;
5+
using System.Net.Http.Headers;
56
using System.Text;
67
using System.Threading;
78
using System.Threading.Tasks;
@@ -64,10 +65,12 @@ public GraphQLHttpClient(GraphQLHttpClientOptions options, HttpClient httpClient
6465
#region IGraphQLClient
6566

6667
/// <inheritdoc />
67-
public Task<GraphQLResponse<TResponse>> SendQueryAsync<TResponse>(GraphQLRequest request, CancellationToken cancellationToken = default) {
68-
return Options.UseWebSocketForQueriesAndMutations
69-
? this.graphQlHttpWebSocket.SendRequest<TResponse>(request, cancellationToken)
70-
: this.SendHttpPostRequestAsync<TResponse>(request, cancellationToken);
68+
public async Task<GraphQLResponse<TResponse>> SendQueryAsync<TResponse>(GraphQLRequest request, CancellationToken cancellationToken = default) {
69+
if(Options.UseWebSocketForQueriesAndMutations)
70+
return await this.graphQlHttpWebSocket.SendRequest<TResponse>(request, cancellationToken);
71+
72+
var response = await this.SendHttpPostRequestAsync<TResponse>(request, cancellationToken);
73+
return response.Response;
7174
}
7275

7376
/// <inheritdoc />
@@ -114,18 +117,64 @@ public IObservable<GraphQLResponse<TResponse>> CreateSubscriptionStream<TRespons
114117
/// <returns></returns>
115118
public Task InitializeWebsocketConnection() => graphQlHttpWebSocket.InitializeWebSocket();
116119

120+
/// <summary>
121+
/// Sends a query to the GraphQL server and deserializes the response. Provides access to the HTTP response headers. This method will never utilize the websocket connection!
122+
/// </summary>
123+
/// <typeparam name="TResponse"></typeparam>
124+
/// <param name="request"></param>
125+
/// <param name="cancellationToken"></param>
126+
/// <returns></returns>
127+
public Task<GraphQLHttpResponse<TResponse>> SendQueryHttpAsync<TResponse>(GraphQLRequest request, CancellationToken cancellationToken = default) {
128+
return this.SendHttpPostRequestAsync<TResponse>(request, cancellationToken);
129+
}
130+
/// <inheritdoc cref="SendQueryHttpAsync{TResponse}(GraphQL.GraphQLRequest,System.Threading.CancellationToken)"/>
131+
public Task<GraphQLHttpResponse<TResponse>> SendQueryHttpAsync<TResponse>(GraphQLRequest request,
132+
Func<TResponse> defineResponseType, CancellationToken cancellationToken = default)
133+
=> SendQueryHttpAsync<TResponse>(request, cancellationToken);
134+
/// <inheritdoc cref="SendQueryHttpAsync{TResponse}(GraphQL.GraphQLRequest,System.Threading.CancellationToken)"/>
135+
public Task<GraphQLHttpResponse<TResponse>> SendQueryHttpAsync<TResponse>(string query, object? variables = null,
136+
string? operationName = null, Func<TResponse> defineResponseType = null, CancellationToken cancellationToken = default) {
137+
return SendQueryHttpAsync<TResponse>(new GraphQLRequest(query, variables, operationName), cancellationToken: cancellationToken);
138+
}
139+
140+
/// <summary>
141+
/// Sends a mutation to the GraphQL server and deserializes the response. Provides access to the HTTP response headers. This method will never utilize the websocket connection!
142+
/// </summary>
143+
/// <typeparam name="TResponse"></typeparam>
144+
/// <param name="request"></param>
145+
/// <param name="cancellationToken"></param>
146+
/// <returns></returns>
147+
public Task<GraphQLHttpResponse<TResponse>> SendMutationHttpAsync<TResponse>(GraphQLRequest request,
148+
CancellationToken cancellationToken = default)
149+
=> SendQueryHttpAsync<TResponse>(request, cancellationToken);
150+
/// <inheritdoc cref="SendMutationHttpAsync{TResponse}(GraphQL.GraphQLRequest,System.Threading.CancellationToken)"/>
151+
public Task<GraphQLHttpResponse<TResponse>> SendMutationHttpAsync<TResponse>(GraphQLRequest request,
152+
Func<TResponse> defineResponseType, CancellationToken cancellationToken = default)
153+
=> SendQueryHttpAsync<TResponse>(request, cancellationToken);
154+
/// <inheritdoc cref="SendMutationHttpAsync{TResponse}(GraphQL.GraphQLRequest,System.Threading.CancellationToken)"/>
155+
public Task<GraphQLHttpResponse<TResponse>> SendMutationHttpAsync<TResponse>(string query, object? variables = null,
156+
string? operationName = null, Func<TResponse> defineResponseType = null, CancellationToken cancellationToken = default) {
157+
return SendQueryHttpAsync<TResponse>(new GraphQLRequest(query, variables, operationName), cancellationToken: cancellationToken);
158+
}
159+
117160
#region Private Methods
118161

119-
private async Task<GraphQLResponse<TResponse>> SendHttpPostRequestAsync<TResponse>(GraphQLRequest request, CancellationToken cancellationToken = default) {
162+
private async Task<GraphQLHttpResponse<TResponse>> SendHttpPostRequestAsync<TResponse>(GraphQLRequest request, CancellationToken cancellationToken = default) {
120163
var preprocessedRequest = await Options.PreprocessRequest(request, this);
121164
using var httpRequestMessage = this.GenerateHttpRequestMessage(preprocessedRequest);
122165
using var httpResponseMessage = await this.HttpClient.SendAsync(httpRequestMessage, cancellationToken);
123166
if (!httpResponseMessage.IsSuccessStatusCode) {
124167
throw new GraphQLHttpException(httpResponseMessage);
125168
}
169+
170+
var response = new GraphQLHttpResponse<TResponse> {
171+
ResponseHeaders = httpResponseMessage.Headers,
172+
StatusCode = httpResponseMessage.StatusCode
173+
};
126174

127175
var bodyStream = await httpResponseMessage.Content.ReadAsStreamAsync();
128-
return await JsonSerializer.DeserializeFromUtf8StreamAsync<TResponse>(bodyStream, cancellationToken);
176+
response.Response = await JsonSerializer.DeserializeFromUtf8StreamAsync<TResponse>(bodyStream, cancellationToken);
177+
return response;
129178
}
130179

131180
private HttpRequestMessage GenerateHttpRequestMessage(GraphQLRequest request) {
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1+
using System.Net;
2+
using System.Net.Http.Headers;
3+
14
namespace GraphQL.Client.Http {
25

3-
public class GraphQLHttpResponse<T> : GraphQLResponse<T> {
6+
public class GraphQLHttpResponse<T> {
7+
public GraphQLHttpResponse()
8+
{
9+
}
10+
11+
public GraphQLResponse<T> Response { get; set; }
12+
public HttpResponseHeaders ResponseHeaders { get; set; }
13+
public HttpStatusCode StatusCode { get; set; }
414
}
515
}

tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using System.Net;
13
using System.Net.Http;
24
using System.Threading;
35
using System.Threading.Tasks;
@@ -46,6 +48,19 @@ public async void QueryTheory(int id, string name) {
4648
Assert.Equal(name, response.Data.Human.Name);
4749
}
4850

51+
[Theory]
52+
[ClassData(typeof(StarWarsHumans))]
53+
public async void QueryHttpTheory(int id, string name) {
54+
var graphQLRequest = new GraphQLRequest($"{{ human(id: \"{id}\") {{ name }} }}");
55+
var httpResponse = await StarWarsClient.SendQueryHttpAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } });
56+
57+
httpResponse.Response.Errors.Should().BeNull();
58+
httpResponse.Response.Data.Human.Name.Should().Be(name);
59+
60+
httpResponse.StatusCode.Should().BeEquivalentTo(HttpStatusCode.OK);
61+
httpResponse.ResponseHeaders.Date.Should().BeCloseTo(DateTimeOffset.Now, TimeSpan.FromMinutes(1));
62+
}
63+
4964
[Theory]
5065
[ClassData(typeof(StarWarsHumans))]
5166
public async void QueryWithDynamicReturnTypeTheory(int id, string name) {

0 commit comments

Comments
 (0)