diff --git a/src/GraphQL.Client/GraphQLHttpClient.cs b/src/GraphQL.Client/GraphQLHttpClient.cs index c925f1af..18d656ea 100644 --- a/src/GraphQL.Client/GraphQLHttpClient.cs +++ b/src/GraphQL.Client/GraphQLHttpClient.cs @@ -89,57 +89,19 @@ public GraphQLHttpClient(string endPoint, IGraphQLWebsocketJsonSerializer serial #region IGraphQLClient - /// - public async Task> SendQueryAsync(GraphQLRequest request, CancellationToken cancellationToken = default) - { - return Options.UseWebSocketForQueriesAndMutations || Options.WebSocketEndPoint is not null && Options.EndPoint is null || Options.EndPoint.HasWebSocketScheme() - ? await GraphQlHttpWebSocket.SendRequestAsync(request, cancellationToken).ConfigureAwait(false) - : await SendAPQHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); - } - - /// - public Task> SendMutationAsync(GraphQLRequest request, - CancellationToken cancellationToken = default) - => SendQueryAsync(request, cancellationToken); - - /// - public IObservable> CreateSubscriptionStream(GraphQLRequest request) - => CreateSubscriptionStream(request, null); + private const int APQ_SUPPORTED_VERSION = 1; /// - public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action? exceptionHandler) - { - if (_disposed) - throw new ObjectDisposedException(nameof(GraphQLHttpClient)); - - var observable = GraphQlHttpWebSocket.CreateSubscriptionStream(request, exceptionHandler); - return observable; - } - - #endregion - - /// - public Task InitializeWebsocketConnection() => GraphQlHttpWebSocket.InitializeWebSocket(); - - /// - public Task SendPingAsync(object? payload) => GraphQlHttpWebSocket.SendPingAsync(payload); - - /// - public Task SendPongAsync(object? payload) => GraphQlHttpWebSocket.SendPongAsync(payload); - - #region Private Methods - - private async Task> SendAPQHttpRequestAsync(GraphQLRequest request, CancellationToken cancellationToken = default) + public async Task> SendQueryAsync(GraphQLRequest request, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); - var savedQuery = request.Query; + string? savedQuery = request.Query; bool useAPQ = false; if (request.Query != null && !_apqDisabledPerSession && Options.EnableAutomaticPersistedQueries(request)) { // https://www.apollographql.com/docs/react/api/link/persisted-queries/ - const int APQ_SUPPORTED_VERSION = 1; useAPQ = true; request.Extensions ??= new(); request.Extensions["persistedQuery"] = new Dictionary @@ -150,7 +112,7 @@ private async Task> SendAPQHttpRequestAsync(request, cancellationToken); + var response = await SendQueryInternalAsync(request, cancellationToken); if (useAPQ) { @@ -161,7 +123,7 @@ private async Task> SendAPQHttpRequestAsync(request, cancellationToken); + return await SendQueryInternalAsync(request, cancellationToken); } else { @@ -169,13 +131,49 @@ private async Task> SendAPQHttpRequestAsync(request, cancellationToken); + return await SendQueryInternalAsync(request, cancellationToken); } } return response; } + /// + public Task> SendMutationAsync(GraphQLRequest request, + CancellationToken cancellationToken = default) + => SendQueryAsync(request, cancellationToken); + + /// + public IObservable> CreateSubscriptionStream(GraphQLRequest request) + => CreateSubscriptionStream(request, null); + + /// + public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action? exceptionHandler) + { + if (_disposed) + throw new ObjectDisposedException(nameof(GraphQLHttpClient)); + + var observable = GraphQlHttpWebSocket.CreateSubscriptionStream(request, exceptionHandler); + return observable; + } + + #endregion + + /// + public Task InitializeWebsocketConnection() => GraphQlHttpWebSocket.InitializeWebSocket(); + + /// + public Task SendPingAsync(object? payload) => GraphQlHttpWebSocket.SendPingAsync(payload); + + /// + public Task SendPongAsync(object? payload) => GraphQlHttpWebSocket.SendPongAsync(payload); + + #region Private Methods + private async Task> SendQueryInternalAsync(GraphQLRequest request, CancellationToken cancellationToken = default) => + Options.UseWebSocketForQueriesAndMutations || Options.WebSocketEndPoint is not null && Options.EndPoint is null || Options.EndPoint.HasWebSocketScheme() + ? await GraphQlHttpWebSocket.SendRequestAsync(request, cancellationToken).ConfigureAwait(false) + : await SendHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); + private async Task> SendHttpRequestAsync(GraphQLRequest request, CancellationToken cancellationToken = default) { var preprocessedRequest = await Options.PreprocessRequest(request, this).ConfigureAwait(false); diff --git a/src/GraphQL.Client/GraphQLHttpClientOptions.cs b/src/GraphQL.Client/GraphQLHttpClientOptions.cs index e1804845..eda0d024 100644 --- a/src/GraphQL.Client/GraphQLHttpClientOptions.cs +++ b/src/GraphQL.Client/GraphQLHttpClientOptions.cs @@ -111,9 +111,9 @@ public static bool DefaultIsValidResponseToDeserialize(HttpResponseMessage r) /// A delegate which takes an and returns a boolean to disable any future persisted queries for that session. /// This defaults to disabling on PersistedQueryNotSupported or a 400 or 500 HTTP error. /// - public Func DisableAPQ { get; set; } = response => + public Func DisableAPQ { get; set; } = response => { - return ((int)response.StatusCode >= 400 && (int)response.StatusCode < 600) || - response.Errors?.Any(error => string.Equals(error.Message, "PersistedQueryNotSupported", StringComparison.CurrentCultureIgnoreCase)) == true; + return response.Errors?.Any(error => string.Equals(error.Message, "PersistedQueryNotSupported", StringComparison.CurrentCultureIgnoreCase)) == true + || response is IGraphQLHttpResponse httpResponse && (int)httpResponse.StatusCode >= 400 && (int)httpResponse.StatusCode < 600; }; } diff --git a/src/GraphQL.Primitives/GraphQLRequest.cs b/src/GraphQL.Primitives/GraphQLRequest.cs index c208a90f..d931c3ed 100644 --- a/src/GraphQL.Primitives/GraphQLRequest.cs +++ b/src/GraphQL.Primitives/GraphQLRequest.cs @@ -16,7 +16,7 @@ public class GraphQLRequest : Dictionary, IEquatable [StringSyntax("GraphQL")] - public string Query + public string? Query { get => TryGetValue(QUERY_KEY, out object value) ? (string)value : null; set => this[QUERY_KEY] = value;