Skip to content

Commit

Permalink
Merge pull request #930 from clement911/graphql-system-text-json
Browse files Browse the repository at this point in the history
GraphService method to get GraphQL results with System.Text.Json instead of Newtonsoft.Json
  • Loading branch information
clement911 committed Sep 14, 2023
2 parents 4630ec6 + e9a64ba commit 6a5b8b6
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 26 deletions.
2 changes: 1 addition & 1 deletion ShopifySharp.Tests/Graph_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public async Task ListsOrdersUsingGraphService()
};
// Serialize the GraphQL query and the variables into a JToken. Must use a JToken for now, or else the service
// will assume we are using a GraphQL string and send with the wrong content type.
var serializerSettings = Infrastructure.Serializer.CreateSettings();
var serializerSettings = Infrastructure.Serializer.CreateNewtonsoftSettings();
var serializer = JsonSerializer.Create(serializerSettings);
var requestBody = JToken.FromObject(new
{
Expand Down
19 changes: 16 additions & 3 deletions ShopifySharp/Infrastructure/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace ShopifySharp.Infrastructure
/// </summary>
public static class Serializer
{
public static JsonSerializerSettings CreateSettings()
public static JsonSerializerSettings CreateNewtonsoftSettings()
{
return new JsonSerializerSettings
{
Expand All @@ -23,11 +23,19 @@ public static JsonSerializerSettings CreateSettings()
};
}

public static string Serialize(object data) => JsonConvert.SerializeObject(data, CreateSettings());
public static string Serialize(object data) => JsonConvert.SerializeObject(data, CreateNewtonsoftSettings());

public static T Deserialize<T>(string json, string rootElementPath = null, DateParseHandling? dateParseHandlingOverride = null)
{
var settings = CreateSettings();
if (typeof(T) == typeof(System.Text.Json.JsonDocument))
return DeserializeWithSystemTextJson<T>(json);
else
return DeserializeWithNewtonsoft<T>(json, rootElementPath, dateParseHandlingOverride);
}

private static T DeserializeWithNewtonsoft<T>(string json, string rootElementPath, DateParseHandling? dateParseHandlingOverride)
{
var settings = CreateNewtonsoftSettings();
if (dateParseHandlingOverride != null)
settings.DateParseHandling = dateParseHandlingOverride.Value;

Expand All @@ -42,5 +50,10 @@ public static T Deserialize<T>(string json, string rootElementPath = null, DateP
return JsonConvert.DeserializeObject<T>(json, settings);
}
}

private static T DeserializeWithSystemTextJson<T>(string json)
{
return System.Text.Json.JsonSerializer.Deserialize<T>(json);
}
}
}
48 changes: 31 additions & 17 deletions ShopifySharp/Services/Graph/GraphService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System;
using System.Text.Json;

namespace ShopifySharp
{
Expand All @@ -30,24 +32,32 @@ public GraphService(string myShopifyUrl, string shopAccessToken, string apiVersi
_apiVersion = apiVersion;
}

/// <inheritdoc />
public virtual async Task<JToken> PostAsync(string body, int? graphqlQueryCost = null, CancellationToken cancellationToken = default)
public virtual Task<JToken> PostAsync(JToken body, int? graphqlQueryCost = null, CancellationToken cancellationToken = default)
{
var req = PrepareRequest("graphql.json");
return PostAsync(JsonConvert.SerializeObject(body), graphqlQueryCost, cancellationToken);
}

var content = new StringContent(body, Encoding.UTF8, "application/graphql");
public virtual async Task<JToken> PostAsync(string body, int? graphqlQueryCost = null, CancellationToken cancellationToken = default)
{
var res = await PostAsync<JToken>(body, graphqlQueryCost, cancellationToken);
return res["data"];
}

return await SendAsync(req, content, graphqlQueryCost, cancellationToken);
public virtual async Task<JsonElement> Post2Async(string body, int? graphqlQueryCost = null, CancellationToken cancellationToken = default)
{
var res = await PostAsync<JsonDocument>(body, graphqlQueryCost, cancellationToken);
return res.RootElement.GetProperty("data");
}

/// <inheritdoc />
public virtual async Task<JToken> PostAsync(JToken body, int? graphqlQueryCost = null, CancellationToken cancellationToken = default)
private async Task<T> PostAsync<T>(string body, int? graphqlQueryCost, CancellationToken cancellationToken)
{
var req = PrepareRequest("graphql.json");

var content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json");
var content = new StringContent(body, Encoding.UTF8, "application/graphql");

return await SendAsync(req, content, graphqlQueryCost, cancellationToken);
var res = await SendAsync<T>(req, content, graphqlQueryCost, cancellationToken);

return res;
}

/// <summary>
Expand All @@ -57,34 +67,38 @@ public virtual async Task<JToken> PostAsync(JToken body, int? graphqlQueryCost =
/// <param name="content">The HttpContent, be it GraphQL or Json.</param>
/// <param name="graphqlQueryCost">An estimation of the cost of this query.</param>
/// <returns>A JToken containing the data from the request.</returns>
protected virtual async Task<JToken> SendAsync(RequestUri req, HttpContent content, int? graphqlQueryCost, CancellationToken cancellationToken = default)
protected virtual async Task<T> SendAsync<T>(RequestUri req, HttpContent content, int? graphqlQueryCost, CancellationToken cancellationToken = default)
{
var response = await ExecuteRequestAsync(req, HttpMethod.Post, cancellationToken, content, null, graphqlQueryCost, DateParseHandling.None);
var response = await ExecuteRequestCoreAsync<T>(req, HttpMethod.Post, cancellationToken, content, null, null, graphqlQueryCost, DateParseHandling.None);

CheckForErrors(response);

return response.Result["data"];
return response.Result;
}

/// <summary>
/// Since Graph API Errors come back with error code 200, checking for them in a way similar to the REST API doesn't work well without potentially throwing unnecessary errors. This loses the requestId, but otherwise is capable of passing along the message.
/// </summary>
/// <param name="requestResult">The <see cref="RequestResult{JToken}" /> response from ExecuteRequestAsync.</param>
/// <exception cref="ShopifyException">Thrown if <paramref name="requestResult"/> contains an error.</exception>
protected virtual void CheckForErrors(RequestResult<JToken> requestResult)
protected virtual void CheckForErrors<T>(RequestResult<T> requestResult)
{
if (requestResult.Result["errors"] != null)
var res = JToken.Parse(requestResult.RawResult);
if (res["errors"] != null)
{
var errorList = new List<string>();

foreach (var error in requestResult.Result["errors"])
foreach (var error in res["errors"])
{
errorList.Add(error["message"].ToString());
}

var message = requestResult.Result["errors"].FirstOrDefault()["message"].ToString();
var message = res["errors"].FirstOrDefault()["message"].ToString();

var requestIdHeader = requestResult.Response.Headers.FirstOrDefault(h => h.Key.Equals("X-Request-Id", StringComparison.OrdinalIgnoreCase));
var requestId = requestIdHeader.Value?.FirstOrDefault();

throw new ShopifyException(requestResult.Response, HttpStatusCode.OK, errorList, message, requestResult.RawResult, "");
throw new ShopifyException(requestResult.Response, HttpStatusCode.OK, errorList, message, requestResult.RawResult, requestId);
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions ShopifySharp/Services/ShopifyService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,9 @@ private string ReadLinkHeader(HttpResponseMessage response)
HttpMethod method,
CancellationToken cancellationToken,
HttpContent content = null,
Dictionary<string, string> headers = null,
int? graphqlQueryCost = null,
DateParseHandling? dateParseHandlingOverride = null)
Dictionary<string, string> headers = null)
{
return await this.ExecuteRequestCoreAsync<JToken>(uri, method, cancellationToken, content, headers, null, graphqlQueryCost, dateParseHandlingOverride);
return await this.ExecuteRequestCoreAsync<JToken>(uri, method, cancellationToken, content, headers, null, null);
}

/// <summary>
Expand Down
3 changes: 2 additions & 1 deletion ShopifySharp/ShopifySharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<AssemblyName>ShopifySharp</AssemblyName>
<RootNamespace>ShopifySharp</RootNamespace>
<VersionPrefix>6.4.0</VersionPrefix>
<VersionPrefix>6.5.0</VersionPrefix>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Expand All @@ -22,5 +22,6 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="2.1.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="all" />
<PackageReference Include="newtonsoft.json" Version="13.0.2" />
<PackageReference Include="System.Text.Json" Version="7.0.3" />
</ItemGroup>
</Project>

0 comments on commit 6a5b8b6

Please sign in to comment.