ZeroQL icon indicating copy to clipboard operation
ZeroQL copied to clipboard

Support GraphQL’s transport layer agnostics

Open DaveRMaltby opened this issue 1 year ago • 5 comments

For integration testing of our product which uses Hot Chocolate, I am not standing up an HTTP endpoint in order to make GraphQL calls. Hot Chocolate exposes an IRequestExecutor.ExecuteAsync() method which allows us to avoid HTTP altogether and just provide json back and forth.
Initially when I began to use your library, I believed that I could use the generated ZeroQL classes based on my schema and just create my own version of ZeroQL.GraphQLClient<> which didn't require a System.Net.Http.HttpClient. Of course, I also had to copy the GraphQLClientLambdaExtensions.cs code and modify it to use my new GraphQLClient derivative. Anyhow, I hit a deadend without making changes to the ZeroQL library in that the ZeroQL code anaylizers are coming back with a DiagnosticDescriptor of Descriptors.FailedToConvert. I believe that there is some conversion that is depending on a client inherited from ZeroQL.GraphQLClient<>. Anyhow, seems like changes in ZeroQL are needed to accomplish what I'm hoping to do.

I have already forked and created a branch that works in the unit test that I created. The PR will follow momentarily.

DaveRMaltby avatar Oct 31 '22 15:10 DaveRMaltby

Created PR #15 to solve this issue.

DaveRMaltby avatar Oct 31 '22 15:10 DaveRMaltby

I had a quick look at your PR. As for me, this implementation contains too much of the "leaky abstraction". The relations between abstractions depend on the knowledge that it is not an HTTP transport. When ideally, it should not care about it.

I am going to have a more deep look at PR later on and also will think about other ways to support your use case.

byme8 avatar Oct 31 '22 19:10 byme8

I understand your concern and have the same concerns in mind too. With this issue having so many touch points into your codebase, I held back on trying to do too much to address these concerns that you're bringing up. But on the other hand, I didn't want to purpose this significate feature request without providing some type of solution. Thanks for looking into it.

DaveRMaltby avatar Oct 31 '22 20:10 DaveRMaltby

Solution for your use case:


  public static async Task<IGraphQLResult> Execute()
  {
      var serviceCollection = new ServiceCollection();
      var server = TestServer.Program.AddGraphQL(serviceCollection);
      var excutor = await server.BuildRequestExecutorAsync();

      var httpClient = new HttpClient(new HotChocoClientHandler(excutor))
      {
          BaseAddress = new Uri("http://localhost:10000/graphql")
      };

      var qlClient = new TestServerClient(httpClient);
      // place to replace
      var response = await qlClient.Query(static q => q.Me(o => o.FirstName));

      return response;
  }


public class HotChocoClientHandler : HttpClientHandler
{
    private readonly IRequestExecutor executor;

    public HotChocoClientHandler(IRequestExecutor executor)
    {
        this.executor = executor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
    {
        var requestJson = await httpRequest.Content!.ReadAsStringAsync(cancellationToken);
        var request = JsonConvert.DeserializeObject<GraphQLRequest>(requestJson);
        var executionResult = await executor.ExecuteAsync(request.Query, cancellationToken);
        var json = await executionResult.ToJsonAsync();

        return new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new ReadOnlyMemoryContent(Encoding.UTF8.GetBytes(json))
        };
    }
}

About different transport layers, it is more complicated. At some point, I want to experiment and replace JSON with MessagePack or Protobuff. It would be an excellent time to look at transport layers.

byme8 avatar Nov 01 '22 17:11 byme8

Thank you sir! Looks like a great workaround that should let my project return to using your NuGet packages (instead of a fork). Appreciate it! I was unaware about the HttpClientHandler class. Clever.

DaveRMaltby avatar Nov 01 '22 17:11 DaveRMaltby