Giter Club home page Giter Club logo

graphql-client's Introduction

GraphQL.Client

A GraphQL Client for .NET Standard over HTTP.

Provides the following packages:

Package Downloads Nuget Latest
GraphQL.Client Nuget Nuget
GraphQL.Client.Abstractions Nuget Nuget
GraphQL.Client.Abstractions.Websocket Nuget Nuget
GraphQL.Client.LocalExecution Nuget Nuget
GraphQL.Client.Serializer.Newtonsoft Nuget Nuget
GraphQL.Client.Serializer.SystemTextJson Nuget Nuget
GraphQL.Primitives Nuget Nuget

Specification

The Library will try to follow the following standards and documents:

Usage

Create a GraphQLHttpClient

// To use NewtonsoftJsonSerializer, add a reference to 
// NuGet package GraphQL.Client.Serializer.Newtonsoft
var graphQLClient = new GraphQLHttpClient(
    "https://api.example.com/graphql", 
    new NewtonsoftJsonSerializer());

Note

GraphQLHttpClient is meant to be used as a single long-lived instance per endpoint (i.e. register as singleton in a DI system), which should be reused for multiple requests.

Create a GraphQLRequest:

Simple Request:

var heroRequest = new GraphQLRequest {
    Query = """
    {
        hero {
            name
        }
    }
    """
};

OperationName and Variables Request:

var personAndFilmsRequest = new GraphQLRequest {
    Query ="""
    query PersonAndFilms($id: ID) {
        person(id: $id) {
            name
            filmConnection {
                films {
                    title
                }
            }
        }
    }
    """,
    OperationName = "PersonAndFilms",
    Variables = new {
        id = "cGVvcGxlOjE="
    }
};

Warning

Be careful when using byte[] in your variables object, as most JSON serializers will treat that as binary data.

If you really need to send a list of bytes with a byte[] as a source, then convert it to a List<byte> first, which will tell the serializer to output a list of numbers instead of a base64-encoded string.

Execute Query/Mutation

public class ResponseType 
{
    public PersonType Person { get; set; }
}

public class PersonType 
{
    public string Name { get; set; }
    public FilmConnectionType FilmConnection { get; set; }    
}

public class FilmConnectionType {
    public List<FilmContentType> Films { get; set; }    
}

public class FilmContentType {
    public string Title { get; set; }
}

var graphQLResponse = await graphQLClient.SendQueryAsync<ResponseType>(personAndFilmsRequest);

var personName = graphQLResponse.Data.Person.Name;

Using the extension method for anonymously typed responses (namespace GraphQL.Client.Abstractions) you could achieve the same result with the following code:

var graphQLResponse = await graphQLClient.SendQueryAsync(
    personAndFilmsRequest, 
    () => new { person = new PersonType()});
var personName = graphQLResponse.Data.person.Name;

Important

Note that the field in the GraphQL response which gets deserialized into the response object is the data field.

A common mistake is to try to directly use the PersonType class as response type (because thats the thing you actually want to query), but the returned response object contains a property person containing a PersonType object (like the ResponseType modelled above).

Use Subscriptions

public class UserJoinedSubscriptionResult {
    public ChatUser UserJoined { get; set; }

    public class ChatUser {
        public string DisplayName { get; set; }
        public string Id { get; set; }
    }
}

Create subscription

var userJoinedRequest = new GraphQLRequest {
    Query = @"
    subscription {
        userJoined{
            displayName
            id
        }
    }"
};

IObservable<GraphQLResponse<UserJoinedSubscriptionResult>> subscriptionStream 
    = client.CreateSubscriptionStream<UserJoinedSubscriptionResult>(userJoinedRequest);

var subscription = subscriptionStream.Subscribe(response => 
    {
        Console.WriteLine($"user '{response.Data.UserJoined.DisplayName}' joined")
    });

End Subscription

subscription.Dispose();

Syntax Highlighting for GraphQL strings in IDEs

.NET 7.0 introduced the StringSyntaxAttribute to have a unified way of telling what data is expected in a given string or ReadOnlySpan<char>. IDEs like Visual Studio and Rider can then use this to provide syntax highlighting and checking.

From v6.0.4 on all GraphQL string parameters in this library are decorated with the [StringSyntax("GraphQL")] attribute.

Currently, there is no native support for GraphQL formatting and syntax highlighting in Visual Studio, but the GraphQLTools Extension provides that for you.

For Rider, JetBrains provides a Plugin, too.

To leverage syntax highlighting in variable declarations, the GraphQLQuery value record type is provided:

GraphQLQuery query = new("""
                         query PersonAndFilms($id: ID) {
                             person(id: $id) {
                                 name
                                 filmConnection {
                                     films {
                                         title
                                     }
                                 }
                             }
                         }
                         """);
                         
var graphQLResponse = await graphQLClient.SendQueryAsync<ResponseType>(
    query, 
    "PersonAndFilms",
    new { id = "cGVvcGxlOjE=" });

Useful Links

Blazor WebAssembly Limitations

Blazor WebAssembly differs from other platforms as it does not support all features of other .NET runtime implementations. For instance, the following WebSocket options properties are not supported and will not be set:

graphql-client's People

Contributors

ben-voss avatar bjorg avatar deinok avatar dependabot[bot] avatar didimitrie avatar dwelch2344 avatar eilon avatar jasmin-mistry avatar jorisvaneijden avatar kimlukas avatar lol768 avatar maydayof avatar mdhalgara avatar meantub avatar mikocot avatar mjul avatar rose-a avatar simoncropp avatar stephanedelcroix avatar sungam3r avatar vivek4int avatar wirmar avatar zooeywm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-client's Issues

.NET Standard

Hey!
Just wanna check wich version .NET Standard? Is it 1.2 or?

Integration testing vs own GraphQL server

I would like to build an integration test for a GraphQL client/server "pair" using the way described in https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.1.

For this it would be handy if I could inject a HttpClient created by the WebApplicationFactory into the GraphQLHttpClient.

Would you mind exposing the internal GraphQLHttpClient(GraphQLHttpClientOptions options, HttpClient httpClient)? I guess this would provide exactly what I need...

Deserialization returns an empty GraphQLResponse in case of valid JSON but invalid GraphQLResponse

I've come across a situation where trying to call out to a GraphQL service (hosted internally) and using this library causes it to return an empty GraphQLResponse back to the consumer when the response from the service isn't a type of GraphQLResponse.

After cloning this repo and stepping thru the code, I realized that it isn't checking to see whether the response status code is 200 and then attempt to deserialize the response to GraphQLResponse. Would it be possible to make this adjustment to check the status code first and then perform appropriate de-serialization technique?

Here's a rough code changes I made to get this to work for me:

public async Task<GraphQLResponse> ReadHttpResponseMessageAsync(HttpResponseMessage httpResponseMessage) {
			using (var stream = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false))
			using (var streamReader = new StreamReader(stream))
			using (var jsonTextReader = new JsonTextReader(streamReader)) {
				var jsonSerializer = new JsonSerializer {
					ContractResolver = this.Options.JsonSerializerSettings.ContractResolver
				};
				if (!httpResponseMessage.IsSuccessStatusCode)
				{
					GraphQLResponse response;
					try
					{
						response = jsonSerializer.Deserialize<GraphQLResponse>(jsonTextReader);
					}
					catch (JsonReaderException)
					{
						throw new GraphQLHttpException(httpResponseMessage);
					}

					if (response == null || response.Data == null)
					{
						throw new GraphQLHttpException(httpResponseMessage);
					}

					return response;
				}

				try
				{
					return jsonSerializer.Deserialize<GraphQLResponse>(jsonTextReader);
				}
				catch (JsonReaderException)
				{
					throw new GraphQLHttpException(httpResponseMessage);
				}
			}
		}

Consider using IHttpClientFactory instead of HttpClient

Using a HttpClient without using static even when you're disposing the object, is considered a bad practise due to the connection that is in a n a TIME_WAIT state . Consider changing the implementation to use IHttpClientFactory as mentioned by Steve Gordon:
https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore

private readonly HttpClient httpClient;

Subscription closing?

Can we close a opened subscription?

var client = new GraphQLClient("http://localhost:5000/graphql");
var subscription = new GraphQLRequest
            {
                Query = @"
                subscription{
                  addressEvent{
                    type
                    isSigned
                    id
                    description
                    addressNumber
                    cncValues{
                      readValue
                      value
                    }
                  }
                }"
            };
response = await client.SendSubscribeAsync(subscription);
response.OnReceive += Response_OnReceive;

When I close the application, the server will continue to send subscription data to the client.

I don't know if it is a client or server issue but when subscription is aborted with, for example GraphQLPlayground, we can see that the client send a special close message to the websocket:

{"id":"1","type":"stop"}

Custom HttpClient for GraphQLClient

It would be very helpful to supply my own instance of HttpClient to the GraphQLClient constructor. Currently it allows to supply a HttpMessageHandler. But this way I can't specify authorization tokens.

Or is there another way to acomplish this?

Making GraphQLHttpClient easier to test

I am building a GraphQL api and using Microsoft.AspNetCore.Mvc.Testing to run a test server.
Ability to inject a HTTPClient into GraphQLHttpClient.cs would make testing much simpler.
it already has a constructor that takes an HttpClient, it just needs to be made public.

internal GraphQLHttpClient(GraphQLHttpClientOptions options,HttpClient httpClient) {
	if (options == null) { throw new ArgumentNullException(nameof(options)); }
	if (options.EndPoint == null) { throw new ArgumentNullException(nameof(options.EndPoint)); }
	if (options.JsonSerializerSettings == null) { throw new ArgumentNullException(nameof(options.JsonSerializerSettings)); }
	if (options.HttpMessageHandler == null) { throw new ArgumentNullException(nameof(options.HttpMessageHandler)); }
	if (options.MediaType == null) { throw new ArgumentNullException(nameof(options.MediaType)); }

	this.graphQLHttpHandler = new GraphQLHttpHandler(options,httpClient);
}

see graphql-dotnet/graphql-dotnet#800 for details

CamelCasePropertyNamesContractResolver causes incompatibility with Spec

ref: http://facebook.github.io/graphql/June2018/#sec-Names
ref: #58

However, using CamelCasePropertyNamesContractResolver on the entire
payload, also changes data field names to camelCase. Data fields (per
spec) can be any desired casing, so the current graphql-client library
won't work where data field names are not camel case.

So, the idea is force Query, OperationName and Variables to CamelCase without forcing the usage CamelCasePropertyNamesContractResolver that causes that the model of Variables to be forced to be CamelCase.

This needs further investigation, like:

  • Requests: It causes renaming of variables?
  • Response: Is the response also afected by this?
  • Serialization: Is there a way to fix this issues without depending on JSON implementation? Other Serialization formats cases

CC: @sys-dev

Application crash when CancellationToken.Cancel() on subscription

I create a subscription as follows and give the method a CancellationToken.

.SendSubscribeAsync(request, token).ConfigureAwait(false);

As soon as I trigger this (CancellationTokenSource.Cancel()), I get an UnhandledException and the program crashes:

image

AppDomain.CurrentDomain.UnhandledException += (sender, args) => CurrentDomainOnUnhandledException(args);

bei System.Threading.CancellationToken.ThrowOperationCanceledException()
bei System.Net.WebSockets.WebSocketConnectionStream.d__21.MoveNext()
--- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
bei System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
bei System.Net.WebSockets.WebSocketBase.WebSocketOperation.d__19.MoveNext()
--- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
bei System.Net.WebSockets.WebSocketBase.d__45.MoveNext()
--- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
bei GraphQL.Client.Http.GraphQLHttpSubscriptionResult.d__12.MoveNext()
--- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
bei System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.b__6_1(Object state)
bei System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
bei System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
bei System.Threading.ThreadPoolWorkQueue.Dispatch()
bei System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

How did you imagine that you catch these exceptions or how should I do it?

I also noticed that you did not pass the cancellationToken on SendGraphQLSubscriptionRequest().

return this.SendGraphQLSubscriptionRequest(webSocketRequest);

Why do you use GPL3?

Hi,

The GPL V3 is very difficult to use in projects for companies.
Can you please change the license to a without copyleft? For example the same license as dotnet core MIT?

Regards

Only working for .NET 4.5?

I tried to use this library while targeting .NET Framework 4.6.2, but i always get a System.MissingMethodException saying that
get_DefaultRequestHeaders() method was not found.
This works fine when targeting .NET 4.5.2. Is this a known issue, or am i doing something wrong?

Here is my code snippet which causes the issue:

graphQLClient.DefaultRequestHeaders.Authorization =
     new AuthenticationHeaderValue("Basic", Convert.ToBase64String(
         System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", UserName, Password))));

Error with .Net Framework 4.6.2

I installed the latest version using NuGet and used it in my .net framework 4.6.2 application but I'm getting the following error when deploying to Azure.
Could not load file or assembly 'System.Diagnostics.Tracing' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040).
I'd really appreciate some help figuring this out. If any more information is needed, just ask in the comments.

Passing per-request headers

I started to look on how to do authentication and most solutions I see suggest that you pass your JWT token (or similar) in a http header. The HttpClient object is intended to be reused across threads etc, and thus I expect the same for GraphQLHttpClient.

I have seen code examples (somewhere) where DefaultRequestHeaders is configured with a JWT token. This feels really awkward since that is intended for headers that should be common for all requests, e.g. User-Agent and things like that. Not per-user autentication tokens. If you have a service-to-service setup where the service authenticates once, it might make sense to add it in the DefaultRequestHeaders, otherwise not.

In my setup, I will pass per-user authentication tokens and would like to set headers per-request to the GraphQL endpoint. I do not want to use DefaultRequestHeaders since that not would support concurrent use of the HttpClient/GraphQLHttpClient (which is the intended usage pattern of HttpClient) since the DefaultRequestHeaders is a per-client global setting.

My suggestion: Allow to configure http request headers per request somehow.

Perhaps by making a subclass of GraphQLRequest that allow this. The GraphQLHttpClient should still support executing requests of the type GraphQLRequest, but if a subclass is used, it will consider the http-specific settings for this particular request.

Side note: A more pure solution that is transport independent would be to add a token argument to all queries and mutations that need authentication. This would be such a mess though... It could be cleaned up by using variables, but still really messy and very easy to miss passing the token to one of the 20 different queries/fields being invoked.

Query support type Int!

This is my query

        var keyRequest = new GraphQLRequest
        {
            Query = @"
                            mutation upload($lot: Int!,$start: Int!,$end: Int!,$x: String!,$y: String!,$issue_date: BigInt!,$key_version: BigInt!) 
                        {
                            uploadSerialNumbers(lot: $lot,start: $start,end: $end,x: $x,y: $y,issue_date: $issue_date,key_version: $key_version) 
                           
                        }",
            OperationName = "upload",
            Variables = new
            {
                lot = 991,
                start = 1,
                end = 10,
                x = "182f000d767dc7695a7793dfce32",
                y = "04ef74f2738eec34daf98478afdc",
                issue_date= 1531101827922,
                key_version= 1
            }
        };

When I post query to web service . I found error like this

"Variable "$lot" of type "Int" used in position expecting type "Int!"."

New release

Are there any plans for a new release of this?

Query With Parameters

{
getSample (Id: "13614162")
{
pProfileDetail{
Firstname,
}
}
}

please help to pass above query in Dot net (Console, any Core)

CodeGen from introspection

it would be really great to have codegen support. in the REST/swagger world it's very common to generate the classes from the definition/spec. With the introspection support in graphql and codegen support of roslyn this should be possible as well.

Setup CI

Setup a CI for testing and compilation of releases

[Issues(?)] string args is not working

            string email = "sample", string password = "sample";
            try
            {
                var request = new GraphQLRequest
                {
                    Query = @"
mutation {
  login(email: $email, password: $password) {
    isOk,
    userId,
    token
  }
}",
                    Variables = new
                    {
                        email,
                        password
                    }
                };
                var response = await graphQLClient.PostAsync(request);

I coded like above
But can't receive response

What's wrong? or is this issue??

Object => Query support

Any plans to have Object => Query support?

class hero {
   string id;
}

client.executeQuery(new Hero {
    id = "1"
});

How to pass headers

In v1.0.3 how do I pass an Authorization header when I do a POST? This is what my code looks like:

string jsonQuery = "";
using (StreamReader r = new StreamReader("query.json"))
{
          jsonQuery = r.ReadToEnd();
}

var requestGraphQl = new GraphQLRequest
{
      Query = jsonQuery
};

var graphQLClient = new GraphQLClient("http://myUrl.com/testing");
var graphQLResponse = await graphQLClient.PostAsync(requestGraphQl);
var responsePeople = graphQLResponse.GetDataFieldAs<AllPeople>("allPeople");

Doubts about HTTP exception processing

The code for ReadHttpResponseMessageAsync only checks for a valid HTTP status code if the Json parsing throws an exception. Is that strictly correct? That is, is it guaranteed that every HTTP error returned by the server will result in a Json parse exception?

My code always looks like this:

using (var response = await httpClient.PostAsync("shredding-results", request, mediaTypeFormatter).ConfigureAwait(false))
{
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsAsync<ShreddingResult>().ConfigureAwait(false);
}

Basic query question/advice

Hi,

Thank you for creating such a good client for GraphQL calls. Currently, I am attempting to implement the client at my job since we are migrating our Automation calls from REST calls to GraphQL calls to hit the new GraphQL server. I have been trying to use the client to first obtain an Auth Token, which I need in order to proceed to another call.
At the moment this is how I am proceeding with the call. I am not well versed in Async performance specially since C# is an Synchronous language.

I don't seem to be returning anything. I was hoping you could guide me as to what I am doing wrong or how to better use the client to hit our server.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GraphQL.Client;
using GraphQL.Common.Request;
using GraphQL.Common.Response;

Method:

    public static async Task<GraphQLResponse> GetNewToken(string email, string password)
        {
            var tokenRequest = new GraphQLRequest
            {
                Query = @"
                  {
                    getUserToken(username: $email , password: $password )
                  }"
            };
            var endPoint = new GraphQLClient(ApiEndpointURI);
            var authToken = await endPoint.GetAsync(tokenRequest);
            Console.WriteLine(authToken.Data);
            return authToken.Data;
        }

I am making the call on a sandbox environment. When I Console.WriteLine VS outputs:

System.Threading.Tasks.Task`1[GraphQL.Common.Response.GraphQLResponse]

When I go into debugger mode and I step into the function. I step over to authToken.Data and get the following error:
error CS0234: The Type or namespace name 'Common' does not exist in the namespace...
I feel that much of this issue also falls on me not understanding how the username and password are interpolated correctly.

I really appreciate your assitance with the issue. Best!

Subsribtions over wss

Hello, I am trying recent release v2.0.0-alpha.1 and its new subsriptions feature.
Subsriptions were not working for me due to my server is listening on "wss://..."

Now I am no sure how to fix this the correct way:

  • Should I change my server to be able to handle this?
  • Should graphql-client determine whenever to use "ws://..." or "wss://..." from "http://" or "https://" endpoints?

Thanks.

JSON error when trying to hit GitHub GraphQL API

I'm trying to do a GitHub API sample request in code that I have already done with curl. Unfortunately, my requests all fail with a JSON exception. Since I've only been trying to work with GraphQL for about a day, it's very likely I am screwing up something very obvious, but I've narrowed it down to a success/fail situation that only differs by the API endpoint.

If I call GetIntrospectionQueryAsync on https://swapi.apis.guru/, everything appears to work fine. As soon as I change that URL to https://api.github.com/graphql, it throws the following exception when executing GetIntrospectionQueryAsync. (Here is my repro solution GraphQLFailRepro.zip.)

Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: R. Path '', line 0, position 0.
   at Newtonsoft.Json.JsonTextReader.ParseValue()
   at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at GraphQL.Client.GraphQLClient.<ReadHttpResponseMessageAsync>d__20.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at GraphQL.Client.GraphQLClient.<GetAsync>d__16.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at GraphQL.Client.GraphQLClientExtensions.<GetIntrospectionQueryAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at GraphQLFailRepro.Program.<AttemptGitHubIntrospection>d__1.MoveNext() in /Users/…/Projects/GraphQLFailRepro/GraphQLFailRepro/Program.cs:line 26
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at GraphQLFailRepro.Program.<Main>d__3.MoveNext() in /Users/…/Projects/GraphQLFailRepro/GraphQLFailRepro/Program.cs:line 53

Adding Caching Support

This client seems like a very thin wrapper over a HttpClient using dynamic. It would be nice to add many of the features in the Apollo JavaScript client such as client side caching.

You could use the IDistributedCache in the Microsoft.Extensions.Caching.Abstractions NuGet package to store cached objects. That package has lots of providers e.g. In-Memory and Redis built for it already.

Authorization or Header passing

var graphQLClient = new GraphQLClient("https://swapi.apis.guru/");
var graphQLResponse = await graphQLClient.PostAsync(heroRequest);

where we can pass header details or any authorization details in asp.net core, i am unable to pass any header details.

Make GetAsync preserve Endpoint query params such as API keys

Problem

GetAsync discards query parameters specified in the service URI GraphQLHttpClientOptions.Endpoint.

Motivation

This is a problem if we are working with e.g. service endpoints that require an API key. Azure Functions are one such example. Here the API key can be provided through query parameter in the URI or an HTTP header. An example of such an endpoint URI with key is https://graphqlapi-dev.azurewebsites.net/api/GraphQLService?code=Xp12xdeP3SrF8H/CSaWk5m9Xyf0E5S4gyEF/ex/2L1wEbV5oAPdLQg==

If we send a query to that endpoint GetAsync will send a query to https://graphqlapi-dev.azurewebsites.net/api/GraphQLService?query=... without the API key in the code parameter.
This means access will be denied, making the request fail.

The expected URI would be https://graphqlapi-dev.azurewebsites.net/api/GraphQLService?code=Xp12xdeP3SrF8H/CSaWk5m9Xyf0E5S4gyEF/ex/2L1wEbV5oAPdLQg==&query=...

Cause

The problem is found in the GetAsync method on GraphQLHttpHandler:

		public async Task<GraphQLResponse> GetAsync(GraphQLRequest request, CancellationToken cancellationToken = default) {
			if (request == null) { throw new ArgumentNullException(nameof(request)); }
			if (request.Query == null) { throw new ArgumentNullException(nameof(request.Query)); }

			// THE PROBLEM IS THE FOLLOWING CODE  
			var queryParamsBuilder = new StringBuilder($"query={request.Query}", 3);
			if (request.OperationName != null) { queryParamsBuilder.Append($"&operationName={request.OperationName}"); }
			if (request.Variables != null) { queryParamsBuilder.Append($"&variables={JsonConvert.SerializeObject(request.Variables)}"); }
			using (var httpResponseMessage = await this.HttpClient.GetAsync($"{this.Options.EndPoint}?{queryParamsBuilder.ToString()}", cancellationToken).ConfigureAwait(false)) {
				return await this.ReadHttpResponseMessageAsync(httpResponseMessage).ConfigureAwait(false);
			}
		}

Context

For comparison, it looks like PostAsync already preserves the query parameters in the URI since it uses the externally provided service URI in GraphQLHttpClientOptions.Endpoint without modification.

Proposal

Would you be interested in a fix for GetAsync that preserves any query parameters specified in the Endpoint?

[Subscriptions] - Design

Enable the feature of sending subscriptions like query.

Open discusion about what is the best way to implement it, API, and technologies.

Problem running on Blazor Preview

Hi,

I ran the same exact code in a console app (.net Core 2.1.300-preview2-008533) and as a Blazor App (Preview 0.2.0). Console worked fine, but in Blazor:

WASM: Newtonsoft.Json.JsonSerializationException: Error getting value from 'Query' on 'GraphQL.Common.Request.GraphQLRequest'. ---> System.NullReferenceException: Object reference not set to an instance of an object.

Needless to say Query wasn't null and I tried with postQueryAsync as well to verify.

I am guessing that the difference might be that Blazor runs on Mono.

GraphQLError class need more properties

Hi @deinok , I am playing with this graphQL client so far so good. Error handling on our server actually have a additional property which send the exact errorcode. Based on which different actions are taken, however when there is an error, i only see Location and Message property, any additional properties returned are ignored. Can you please add something like this to your classes so any additional properties which were returned will not be lost, instead will be loaded in this property ?

[JsonExtensionData] private IDictionary<string, JToken> _additionalData;

Also, is this library good to use with UWP app?

Thank You

Graphql Query Batching

Hi,
Does this client lib support graphql query batching? If yes, would be appreciated if you guy can provide some sample on how to do this.

Thanks.
Jeff

Better Body reading

In case of no 200 Http Code a JSON exception is thrown, don't giving information about why?
The exception actualy thrown seems more like a parsing exception than a Http Exception

Change *.props Files

In order to indicate the change of ownership, change all the links of *.props and *.csproj

Type Based Deserialization

Hello there

We do face some problems with this client.
The deserialization happens without reflecting the types properly.

Let me clear things out:

Challenge:
One of the most powerful features of GraphQL are Unions. But unions do have the problem that they are of different types. If we want to store objects of different types in a property we need to wrap them with some kind of abstraction (Likewise an interface). That's given.
As given by C#'s type system it's not possible to create an instance of an interface. Which obviously makes sense.
So if we query for union types (or any type basically). We actually need to parse them into the right classes.

Solution:
Newtonsoft provides type based de- / serialization. With the setting TypeNameHandling.Auto and a custom SerializationBinder it's possible to parse Json based on it's "$type" property.
One thing left. We need to know the types. And this is the most important part of any typesafe graphql client. We do need to always query __typename.

Proposal

  • decorating every request with __typename on all nodes
  • extending GraphQLClientOptions by a property TypeNameRegister.
  • creating TypeNameRegister
    public interface ITypeNameRegister {
          Dictionary<string, Type> {get; set;}
          
          /// <summary>
          /// Registers a type for deserialization
          /// </summary>
          /// <param name="schemaTypeName">Name of the type in the schema (property __typename)</param>
          /// <param name="type">The type to parse the object to</param>
          void RegisterType(string schemaTypeName, Type type);
        
          /// <summary>
          /// Registers a type for deserialization
          /// </summary>
          /// /// <param name="schemaTypeName">Name of the type in the schema (property __typename)</param>
          /// <typeparam name="Type">The type to parse the object to</typeparam>
          void RegisterType<Type>(string schemaTypeName)

    }
  • creating a TypeNameSerializer and a TypenameSerialization Binder
  • automatically search for know types and add the "$type" property for the deserialization.
  // something like this:       
       GraphQLResponse response; 
       ....
        
            foreach (var typeToken in response.Data.SelectTokens("$..*.__typename"))
            {
                if (typeToken is JToken token)
                {
                   // that will be based on the dictionary in the actual implementation
                    if (TypenameSerializationBinder.KnownTypes.FirstOrDefault(x => x.Name == token.Value<string>()) is Type type)
                    {
                        token.Parent?.Parent?.First?.AddBeforeSelf(new JProperty("$type", type.Name));
                    }
                }
            }
        

Latest stable version (1.0.3) does not expose IGraphQLClient

I'm using version 1.0.3 of graphql-client through nuget.

I can access the GraphQLClient type, but not IGraphQLClient nor GraphQLHttpClient.

I'd like to access the client through an interface, so I can mock it out in unit tests.

Also, the available GraphQLClient is described as obsolete in the source.
https://github.com/graphql-dotnet/graphql-client/blob/master/src/GraphQL.Client/Obsolete.GraphQLClient.cs

Will IGraphQLClient and GraphQLHttpClient be available soon?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.