Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/GraphQL.Client.Http/GraphQLHttpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private async Task<GraphQLResponse<TResponse>> SendHttpPostRequestAsync<TRespons

private HttpRequestMessage GenerateHttpRequestMessage(GraphQLRequest request) {
var message = new HttpRequestMessage(HttpMethod.Post, this.Options.EndPoint) {
Content = new StringContent(request.SerializeToJson(Options), Encoding.UTF8, "application/json")
Content = new StringContent(request.SerializeToJson(Options), Encoding.UTF8, Options.MediaType)
};

if (request is GraphQLHttpRequest httpRequest)
Expand Down
28 changes: 23 additions & 5 deletions src/GraphQL.Client.Http/GraphQLHttpClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text.Json;
using System.Threading.Tasks;
using Dahomey.Json;
using Dahomey.Json.Serialization.Converters.Factories;

namespace GraphQL.Client.Http {

Expand All @@ -20,9 +21,7 @@ public class GraphQLHttpClientOptions {
/// <summary>
/// The <see cref="JsonSerializerOptions"/> that is going to be used
/// </summary>
public JsonSerializerOptions JsonSerializerOptions { get; set; } = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}.SetupExtensions();
public JsonSerializerOptions JsonSerializerOptions { get; set; } = GetDefaultJsonSerializerOptions();

/// <summary>
/// The <see cref="System.Net.Http.HttpMessageHandler"/> that is going to be used
Expand All @@ -32,13 +31,13 @@ public class GraphQLHttpClientOptions {
/// <summary>
/// The <see cref="MediaTypeHeaderValue"/> that will be send on POST
/// </summary>
public MediaTypeHeaderValue MediaType { get; set; } = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); // This should be "application/graphql" also "application/x-www-form-urlencoded" is Accepted
public string MediaType { get; set; } = "application/json"; // This should be "application/graphql" also "application/x-www-form-urlencoded" is Accepted

/// <summary>
/// The back-off strategy for automatic websocket/subscription reconnects. Calculates the delay before the next connection attempt is made.<br/>
/// default formula: min(n, 5) * 1,5 * random(0.0, 1.0)
/// </summary>
public Func<int, TimeSpan> BackOffStrategy = n => {
public Func<int, TimeSpan> BackOffStrategy { get; set; } = n => {
var rnd = new Random();
return TimeSpan.FromSeconds(Math.Min(n, 5) * 1.5 + rnd.NextDouble());
};
Expand All @@ -53,5 +52,24 @@ public class GraphQLHttpClientOptions {
/// </summary>
public Func<GraphQLRequest, GraphQLHttpClient, Task<GraphQLRequest>> PreprocessRequest { get; set; } = (request, client) => Task.FromResult(request);

/// <summary>
/// Generates the default <see cref="JsonSerializerOptions"/>
/// </summary>
/// <returns></returns>
public static JsonSerializerOptions GetDefaultJsonSerializerOptions() {
var options = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

//options.Converters.Add(new JsonSerializerOptionsState(options));
//options.Converters.Add(new DictionaryConverterFactory());
//options.Converters.Add(new CollectionConverterFactory());
//options.Converters.Add(new JsonNodeConverterFactory());
//options.Converters.Add(new ObjectConverterFactory());

options.SetupExtensions();

return options;
}
}
}
11 changes: 11 additions & 0 deletions src/GraphQL.Client.Http/GraphQLSerializationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Dahomey.Json;
using Dahomey.Json.Serialization.Converters.Factories;

namespace GraphQL.Client.Http {
public static class GraphQLSerializationExtensions {
Expand All @@ -21,5 +23,14 @@ public static ValueTask<TGraphQLResponse> DeserializeFromJsonAsync<TGraphQLRespo
return JsonSerializer.DeserializeAsync<TGraphQLResponse>(stream, options.JsonSerializerOptions, cancellationToken);
}

public static JsonSerializerOptions SetupDahomeyJson(this JsonSerializerOptions options) {
options.Converters.Add(new JsonSerializerOptionsState(options));
options.Converters.Add(new DictionaryConverterFactory());
options.Converters.Add(new CollectionConverterFactory());
//options.Converters.Add(new JsonNodeConverterFactory());
options.Converters.Add(new ObjectConverterFactory());

return options;
}
}
}
1 change: 1 addition & 0 deletions src/GraphQL.Client/GraphQL.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="4.7.0" />
<PackageReference Include="System.Reactive" Version="4.1.2" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup>
Expand Down
15 changes: 10 additions & 5 deletions src/GraphQL.Client/GraphQLResponse.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace GraphQL.Client {

public class GraphQLResponse<T> : IEquatable<GraphQLResponse<T>?> {

[JsonPropertyName("data")]
public T Data { get; set; }

[JsonPropertyName("errors")]
public GraphQLError[]? Errors { get; set; }

public IDictionary<string, dynamic>? Extensions { get; set; }
[JsonPropertyName("extensions")]
public JsonElement? Extensions { get; set; }

public override bool Equals(object? obj) => this.Equals(obj as GraphQLResponse<T>);

public bool Equals(GraphQLResponse<T>? other) {
if (other == null) { return false; }
if (ReferenceEquals(this, other)) { return true; }
if (!EqualityComparer<dynamic?>.Default.Equals(this.Data, other.Data)) { return false; }
if (!EqualityComparer<T>.Default.Equals(this.Data, other.Data)) { return false; }
{
if (this.Errors != null && other.Errors != null) {
if (!Enumerable.SequenceEqual(this.Errors, other.Errors)) { return false; }
}
else if (this.Errors != null && other.Errors == null) { return false; }
else if (this.Errors == null && other.Errors != null) { return false; }
}
if (!EqualityComparer<IDictionary<string, dynamic>?>.Default.Equals(this.Extensions, other.Extensions)) { return false; }
if (!EqualityComparer<JsonElement?>.Default.Equals(this.Extensions, other.Extensions)) { return false; }
return true;
}

public override int GetHashCode() {
unchecked {
var hashCode = EqualityComparer<dynamic?>.Default.GetHashCode(this.Data);
var hashCode = EqualityComparer<T>.Default.GetHashCode(this.Data);
{
if (this.Errors != null) {
foreach (var element in this.Errors) {
Expand All @@ -42,7 +47,7 @@ public override int GetHashCode() {
hashCode = (hashCode * 397) ^ 0;
}
}
hashCode = (hashCode * 397) ^ EqualityComparer<IDictionary<string, dynamic>?>.Default.GetHashCode(this.Extensions);
hashCode = (hashCode * 397) ^ EqualityComparer<JsonElement?>.Default.GetHashCode(this.Extensions);
return hashCode;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/GraphQL.Primitives/GraphQL.Primitives.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
<PropertyGroup>
<Description>GraphQL basic types</Description>
<RootNamespace>GraphQL</RootNamespace>
<TargetFrameworks>netstandard1.0;netstandard2.0</TargetFrameworks>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="System.Text.Json" Version="4.7.0" />
</ItemGroup>

</Project>
16 changes: 11 additions & 5 deletions src/GraphQL.Primitives/GraphQLError.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace GraphQL {

Expand All @@ -11,23 +13,27 @@ public class GraphQLError : IEquatable<GraphQLError?> {

/// <summary>
/// The extensions of the error
/// </summary>
public IDictionary<string, dynamic?>? Extensions { get; set; }
/// </summary>
[JsonPropertyName("extensions")]
public JsonElement? Extensions { get; set; }

/// <summary>
/// The locations of the error
/// </summary>
[JsonPropertyName("locations")]
public GraphQLLocation[]? Locations { get; set; }

/// <summary>
/// The message of the error
/// </summary>
[JsonPropertyName("message")]
public string Message { get; set; }

/// <summary>
/// The Path of the error
/// </summary>
public dynamic[]? Path { get; set; }
[JsonPropertyName("path")]
public object[]? Path { get; set; }

/// <summary>
/// Returns a value that indicates whether this instance is equal to a specified object
Expand All @@ -45,7 +51,7 @@ public override bool Equals(object? obj) =>
public bool Equals(GraphQLError? other) {
if (other == null) { return false; }
if (ReferenceEquals(this, other)) { return true; }
if (!EqualityComparer<IDictionary<string, dynamic?>?>.Default.Equals(this.Extensions, other.Extensions)) { return false; }
if (!EqualityComparer<JsonElement?>.Default.Equals(this.Extensions, other.Extensions)) { return false; }
{
if (this.Locations != null && other.Locations != null) {
if (!this.Locations.SequenceEqual(other.Locations)) { return false; }
Expand All @@ -70,7 +76,7 @@ public bool Equals(GraphQLError? other) {
public override int GetHashCode() {
var hashCode = 0;
if (this.Extensions != null) {
hashCode = hashCode ^ EqualityComparer<IDictionary<string, dynamic?>>.Default.GetHashCode(this.Extensions);
hashCode = hashCode ^ EqualityComparer<JsonElement?>.Default.GetHashCode(this.Extensions);
}
if (this.Locations != null) {
hashCode = hashCode ^ EqualityComparer<GraphQLLocation[]>.Default.GetHashCode(this.Locations);
Expand Down
5 changes: 5 additions & 0 deletions src/GraphQL.Primitives/GraphQLRequest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace GraphQL {

Expand All @@ -11,16 +12,20 @@ public class GraphQLRequest : IEquatable<GraphQLRequest?> {
/// <summary>
/// The Query
/// </summary>
///
[JsonPropertyName("query")]
public string Query { get; set; }

/// <summary>
/// The name of the Operation
/// </summary>
[JsonPropertyName("operationName")]
public string? OperationName { get; set; }

/// <summary>
/// Represents the request variables
/// </summary>
[JsonPropertyName("variables")]
public virtual object? Variables { get; set; }


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.0" />
</ItemGroup>

Expand Down
8 changes: 7 additions & 1 deletion tests/GraphQL.Client.Http.Tests/JsonSerializationTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using Xunit;
using System.Text.Json;
using FluentAssertions;

namespace GraphQL.Client.Http.Tests {
public class JsonSerializationTests {
Expand All @@ -9,7 +11,9 @@ public void WebSocketResponseDeserialization() {
var testObject = new ExtendedTestObject { Id = "test", OtherData = "this is some other stuff" };
var json = JsonSerializer.Serialize(testObject);
var deserialized = JsonSerializer.Deserialize<TestObject>(json);

var dict = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
var childObject = (JsonElement) dict["ChildObject"];
childObject.GetProperty("Id").GetString().Should().Be(testObject.ChildObject.Id);
}

public class TestObject {
Expand All @@ -19,6 +23,8 @@ public class TestObject {

public class ExtendedTestObject : TestObject {
public string OtherData { get; set; }

public TestObject ChildObject{ get; set; } = new TestObject {Id = "1337"};
}
}
}
74 changes: 74 additions & 0 deletions tests/GraphQL.Integration.Tests/ExtensionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using Dahomey.Json;
using FluentAssertions;
using GraphQL.Client;
using GraphQL.Integration.Tests.Extensions;
using GraphQL.Integration.Tests.Helpers;
using IntegrationTestServer;
using IntegrationTestServer.ChatSchema;
using Xunit;

namespace GraphQL.Integration.Tests {
public class ExtensionsTest {
private static TestServerSetup SetupTest(bool requestsViaWebsocket = false) =>
WebHostHelpers.SetupTest<StartupChat>(requestsViaWebsocket);

[Fact]
public async void CanDeserializeExtensions() {

using var setup = SetupTest();
var response = await setup.Client.SendQueryAsync(new GraphQLRequest("query { extensionsTest }"),
() => new {extensionsTest = ""})
.ConfigureAwait(false);

response.Errors.Should().NotBeNull();
response.Errors.Should().ContainSingle();
response.Errors[0].Extensions.Should().NotBeNull();

JsonElement data = new JsonElement();
response.Errors[0].Extensions.Value.Invoking(element => data = element.GetProperty("data")).Should()
.NotThrow();

foreach (var item in ChatQuery.TestExtensions) {
JsonElement value = new JsonElement();
data.Invoking(element => value = element.GetProperty(item.Key)).Should().NotThrow();

switch (item.Value) {
case int i:
value.GetInt32().Should().Be(i);
break;
default:
value.GetString().Should().BeEquivalentTo(item.Value.ToString());
break;
}
}
}

[Fact]
public async void DontNeedToUseCamelCaseNamingStrategy() {

using var setup = SetupTest();
setup.Client.Options.JsonSerializerOptions = new JsonSerializerOptions().SetupExtensions();

const string message = "some random testing message";
var graphQLRequest = new GraphQLRequest(
@"mutation($input: MessageInputType){
addMessage(message: $input){
content
}
}",
new {
input = new {
fromId = "2",
content = message,
sentAt = DateTime.Now
}
});
var response = await setup.Client.SendMutationAsync(graphQLRequest, () => new { addMessage = new { content = "" } });

Assert.Equal(message, response.Data.addMessage.content);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
Expand Down
Loading