-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose the "Query" and "Mutation" extension methods on the IGraphqlClient iterface rather than the concrete type #95
Comments
At the moment, we need the concrete implementation because it contains references to public class GraphQLClient<TQuery, TMutation> : IGraphQLClient, IDisposable
{
// ...
}
public static async Task<GraphQLResult<TResult>> Query<TVariables, TQuery, TMutation, TResult>(
this GraphQLClient<TQuery, TMutation> client,
string name,
TVariables variables,
Func<TVariables, TQuery, TResult> query,
CancellationToken cancellationToken = default,
[CallerArgumentExpression(nameof(query))] string queryKey = null!)
{
//..
} We can move it to the interface level, but then you will need to manually pass the correct
What is the problem with the concrete client inside the class constructor? |
I wonder if an intermediate interface could help here:
That said, As for getting around this now, it might actually be easier to mock interactions with the public class WidgetService
{
private readonly ConcreteZeroGraphQLClient _client;
public QueryService(ConcreteZeroGraphQLClient client) { _client = client; }
public async Task<int> GetLengthOfLargestWidgetAsync()
{
IEnumerable<Widget> widgets = _client.Query(...);
return widgets.Max(w => w.Length);
}
} public class WidgetServiceTests
{
[Test]
public async Task GetLengthOfLargestWidget_WhenGraphQLClientReturnsLotsOfResults_ReturnsLargest()
{
// Arrange
var mockHttpClient = CreateHttpClientMock(); // Out of scope for this demo
mockHttpClient
.Setup(x => x.SendAsync(It.IsAny<HttpRequestMessage>, It.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
Content = new StringContent("""
{
"widgets": [
{ "name": "foo", "length": 12 },
{ "name": "bar", "length": 6 },
{ "name": "baz", "length": 8 }
]
}
""")
});
var graphQLClient = new ConcreteGraphQLClient(mockHttpClient.Object);
var widgetService = new WidgetService(graphQLClient);
// Act
var largest = await widgetService.GetLengthOfLargestWidgetAsync();
// Assert
largest.Should().Be(12);
}
} |
Checkout v7.0.0-preview.1 Now there is a way to create wrapper around the initial ZeroQL client and it works on interface level too. Here example how to create wrapper interface, fake it and use without server: var httpClient = new HttpClient
{
BaseAddress = new Uri("http://localhost:10000/graphql")
};
var zeroQlClient = new UserZeroQLClient(httpClient);
var wrapper = new UserGraphQlClient(zeroQlClient);
var fakeInterface = A.Fake<IUserGraphQLClient>();
A.CallTo(() => fakeInterface.QueryAsync(A<Func<Query, User>>.Ignored, A<string>.Ignored))
.Returns(new User(new ID("FAKE_1"), "FAKE_FIRST_NAME", "FAKE_LAST_NAME"));
var serviceWithFake = new SomeService(fakeInterface);
var serviceWithReal = new SomeService(wrapper);
var fakeUser = await serviceWithFake.GetCurrentUser();
var realUser = await serviceWithReal.GetCurrentUser();
Console.WriteLine(JsonSerializer.Serialize(fakeUser));
// {"Id":{"Value":"FAKE_1"},"FirstName":"FAKE_FIRST_NAME","LastName":"FAKE_LAST_NAME"}
Console.WriteLine(JsonSerializer.Serialize(realUser));
// {"Id":{"Value":"1"},"FirstName":"John","LastName":"Smith"}
public record User(ID Id, string FirstName, string LastName);
public class SomeService(IUserGraphQLClient wrapper)
{
public async Task<User?> GetCurrentUser()
{
// here we are doing query purely on top of interface
var response = await wrapper.QueryAsync(q => q
.Me(u => new User(u.Id, u.FirstName, u.LastName)));
return response;
}
}
public interface IUserGraphQLClient
{
Task<TResult?> QueryAsync<TResult>(
[GraphQLLambda] Func<Query, TResult> query,
[CallerArgumentExpression(nameof(query))]
string queryKey = "");
}
public class UserGraphQlClient(UserZeroQLClient client) : IUserGraphQLClient
{
public async Task<TResult?> QueryAsync<TResult>(
[GraphQLLambda] Func<Query, TResult> query,
[CallerArgumentExpression(nameof(query))]
string queryKey = "")
{
var result = await client.Query(query, queryKey: queryKey);
return result.Data!;
}
} |
Right now you can only use the "lambda" syntax on the concrete graphQLClient, it would be nice if this could be used on the interface:
At present the only callable method on IGraphqlClient is
Execute
forcing us down the "Request" syntax route.I explored the lambda syntax as a workaround for bug #94 and was forced to pass in the generated concrete client into my class constructor, moving away from the interface reference that I currently used.
The text was updated successfully, but these errors were encountered: