Skip to content

Commit 0e94d68

Browse files
committed
- Added support to control Persisted Query payload field name for other GraphQL servers (e.g. Relay server) which may be different than HotChocolate .NET GraphQL Server.
- Added global configuration support via FlurlGraphQLConfig.ConfigureDefaults(config => ...) so that configurable options can be set once globlly with current support for Persisted Query Field Name and Json Serializer Settings.
1 parent 8d8abaa commit 0e94d68

10 files changed

+287
-66
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using Newtonsoft.Json;
3+
4+
namespace FlurlGraphQL.Querying
5+
{
6+
public interface IFlurlGraphQLConfig
7+
{
8+
JsonSerializerSettings NewtonsoftJsonSerializerSettings { get; }
9+
string PersistedQueryPayloadFieldName { get; }
10+
}
11+
12+
public sealed class FlurlGraphQLConfig : IFlurlGraphQLConfig
13+
{
14+
public const string DefaultPersistedQueryFieldName = "id";
15+
16+
private FlurlGraphQLConfig()
17+
{
18+
NewtonsoftJsonSerializerSettings = JsonConvert.DefaultSettings?.Invoke();
19+
PersistedQueryPayloadFieldName = DefaultPersistedQueryFieldName;
20+
}
21+
22+
public static IFlurlGraphQLConfig DefaultConfig { get; private set; } = new FlurlGraphQLConfig();
23+
24+
/// <summary>
25+
/// Configure the Default values for Sql Bulk Helpers and Materialized Data Helpers.
26+
/// </summary>
27+
/// <param name="configAction"></param>
28+
public static void ConfigureDefaults(Action<FlurlGraphQLConfig> configAction)
29+
{
30+
configAction.AssertArgIsNotNull(nameof(configAction));
31+
32+
var newConfig = new FlurlGraphQLConfig();
33+
configAction.Invoke(newConfig);
34+
DefaultConfig = newConfig;
35+
}
36+
37+
public static void ResetDefaults()
38+
{
39+
DefaultConfig = new FlurlGraphQLConfig();
40+
}
41+
42+
public JsonSerializerSettings NewtonsoftJsonSerializerSettings { get; set; }
43+
public string PersistedQueryPayloadFieldName { get; set; }
44+
}
45+
}

FlurlGraphQL.Querying/Flurl/FlurlGraphQLRequest.cs

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Flurl.Http.Content;
1313
using Flurl.Util;
1414
using Newtonsoft.Json;
15+
using Newtonsoft.Json.Linq;
1516
using NullValueHandling = Flurl.NullValueHandling;
1617

1718
namespace FlurlGraphQL.Querying
@@ -214,9 +215,7 @@ public async Task<IFlurlGraphQLResponse> PostGraphQLQueryAsync<TVariables>(
214215
//Execute the Request with shared Exception handling...
215216
return await ExecuteRequestWithExceptionHandling(async () =>
216217
{
217-
//Execute the Query with the GraphQL Server...
218-
var graphqlPayload = new FlurlGraphQLRequestPayload(graphqlQueryType, graphqlQueryOrId, this.GraphQLVariablesInternal);
219-
var jsonPayload = SerializeToJsonWithOptionalGraphQLSerializerSettings(graphqlPayload);
218+
var jsonPayload = BuildPostRequestJsonPayload();
220219

221220
//Since we have our own GraphQL Serializer Settings, our payload is already serialized so we can just send it!
222221
//NOTE: Borrowed directly from the Flurl.PostJsonAsync() method but
@@ -232,6 +231,29 @@ public async Task<IFlurlGraphQLResponse> PostGraphQLQueryAsync<TVariables>(
232231
}).ConfigureAwait(false);
233232
}
234233

234+
protected string BuildPostRequestJsonPayload()
235+
{
236+
//Execute the Query with the GraphQL Server...
237+
//var graphqlPayload = new FlurlGraphQLRequestPayloadBuilder(graphqlQueryType, graphqlQueryOrId, this.GraphQLVariablesInternal);
238+
var graphqlPayload = new Dictionary<string, object>();
239+
graphqlPayload.Add("variables", this.GraphQLVariablesInternal);
240+
241+
switch (GraphQLQueryType)
242+
{
243+
case GraphQLQueryType.Query:
244+
graphqlPayload.Add("query", this.GraphQLQuery);
245+
break;
246+
case GraphQLQueryType.PersistedQuery:
247+
graphqlPayload.Add(GetPersistedQueryPayloadFieldName(), this.GraphQLQuery);
248+
break;
249+
default:
250+
throw new ArgumentOutOfRangeException(nameof(this.GraphQLQueryType), $"GraphQL payload for Query Type [{this.GraphQLQueryType}] cannot be initialized.");
251+
}
252+
253+
var json = SerializeToJsonWithOptionalGraphQLSerializerSettings(graphqlPayload);
254+
return json;
255+
}
256+
235257
/// <summary>
236258
/// STRONGLY DISCOURAGED -- Execute the GraphQL query with the Server using GET request.
237259
/// This is Strongly Discouraged as POST requests are much more robust. But this is provided for edge cases where GET requests must be used.
@@ -267,9 +289,14 @@ public async Task<IFlurlGraphQLResponse> GetGraphQLQueryAsync<TVariables>(
267289
{
268290
switch (this.GraphQLQueryType)
269291
{
270-
case GraphQLQueryType.Query: this.SetQueryParam("query", this.GraphQLQuery); break;
271-
case GraphQLQueryType.PersistedQuery: this.SetQueryParam("id", this.GraphQLQuery); break;
272-
default: throw new ArgumentOutOfRangeException(nameof(graphqlQueryType), $"GraphQL Query Type [{graphqlQueryType}] cannot be initialized.");
292+
case GraphQLQueryType.Query:
293+
this.SetQueryParam("query", this.GraphQLQuery);
294+
break;
295+
case GraphQLQueryType.PersistedQuery:
296+
this.SetQueryParam(GetPersistedQueryPayloadFieldName(), this.GraphQLQuery);
297+
break;
298+
default:
299+
throw new ArgumentOutOfRangeException(nameof(graphqlQueryType), $"GraphQL Query Type [{graphqlQueryType}] cannot be initialized.");
273300
}
274301

275302
if (this.GraphQLVariablesInternal?.Any() ?? false)
@@ -288,6 +315,26 @@ public async Task<IFlurlGraphQLResponse> GetGraphQLQueryAsync<TVariables>(
288315
}).ConfigureAwait(false);
289316
}
290317

318+
protected string GetPersistedQueryPayloadFieldName()
319+
{
320+
return ContextBag.TryGetValue(ContextItemKeys.PersistedQueryPayloadFieldName, out var fieldName)
321+
? fieldName.ToString()
322+
: FlurlGraphQLConfig.DefaultConfig.PersistedQueryPayloadFieldName ?? FlurlGraphQLConfig.DefaultPersistedQueryFieldName;
323+
}
324+
325+
protected string SerializeToJsonWithOptionalGraphQLSerializerSettings(object obj)
326+
{
327+
var jsonSerializerSettings = ContextBag?.TryGetValue(nameof(JsonSerializerSettings), out var serializerSettings) ?? false
328+
? serializerSettings as JsonSerializerSettings
329+
: null;
330+
331+
//Ensure that all json parsing uses a Serializer with the GraphQL Contract Resolver...
332+
//NOTE: We still support normal Serializer Default settings via Newtonsoft framework!
333+
//var jsonSerializer = JsonSerializer.CreateDefault(jsonSerializerSettings);
334+
var json = JsonConvert.SerializeObject(obj, jsonSerializerSettings);
335+
return json;
336+
}
337+
291338
protected async Task<FlurlGraphQLResponse> ExecuteRequestWithExceptionHandling(Func<Task<FlurlGraphQLResponse>> sendRequestFunc)
292339
{
293340
sendRequestFunc.AssertArgIsNotNull(nameof(sendRequestFunc));
@@ -312,20 +359,6 @@ protected async Task<FlurlGraphQLResponse> ExecuteRequestWithExceptionHandling(F
312359
}
313360
}
314361

315-
protected string SerializeToJsonWithOptionalGraphQLSerializerSettings(object obj)
316-
{
317-
var jsonSerializerSettings = ContextBag?.TryGetValue(nameof(JsonSerializerSettings), out var serializerSettings) ?? false
318-
? serializerSettings as JsonSerializerSettings
319-
: null;
320-
321-
//Ensure that all json parsing uses a Serializer with the GraphQL Contract Resolver...
322-
//NOTE: We still support normal Serializer Default settings via Newtonsoft framework!
323-
//var jsonSerializer = JsonSerializer.CreateDefault(jsonSerializerSettings);
324-
var json = JsonConvert.SerializeObject(obj, jsonSerializerSettings);
325-
return json;
326-
}
327-
328-
329362
#endregion
330363

331364
#region IFlurlRequest Interface Implementations

FlurlGraphQL.Querying/Flurl/FlurlGraphQLRequestExtensions.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010

1111
namespace FlurlGraphQL.Querying
1212
{
13+
public static class ContextItemKeys
14+
{
15+
public const string NewtonsoftJsonSerializerSettings = nameof(NewtonsoftJsonSerializerSettings);
16+
public const string PersistedQueryPayloadFieldName = nameof(PersistedQueryPayloadFieldName);
17+
}
18+
1319
public static class FlurlGraphQLRequestExtensions
1420
{
1521
#region ToGraphQLRequest() Internal Helpers...
@@ -212,7 +218,8 @@ public static IFlurlGraphQLRequest SetGraphQLVariables(this IFlurlRequest reques
212218

213219
#endregion
214220

215-
#region NewtonsoftJson Serializer Settings (ONLY Available after an IFlurlRequest is initialized...
221+
#region Configuration Extension - NewtonsoftJson Serializer Settings (ONLY Available after an IFlurlRequest is initialized)...
222+
216223

217224
/// <summary>
218225
/// Initialize the query body for a GraphQL query request.
@@ -222,11 +229,27 @@ public static IFlurlGraphQLRequest SetGraphQLVariables(this IFlurlRequest reques
222229
/// <returns>Returns an IFlurlGraphQLRequest for ready to chain for further initialization or execution.</returns>
223230
public static IFlurlGraphQLRequest SetGraphQLNewtonsoftJsonSerializerSettings(this IFlurlRequest request, JsonSerializerSettings jsonSerializerSettings)
224231
{
232+
jsonSerializerSettings.AssertArgIsNotNull(nameof(jsonSerializerSettings));
225233
var graphqlRequest = (FlurlGraphQLRequest)request.ToGraphQLRequest();
226-
graphqlRequest.SetContextItem(nameof(JsonSerializerSettings), jsonSerializerSettings);
234+
graphqlRequest.SetContextItem(ContextItemKeys.NewtonsoftJsonSerializerSettings, jsonSerializerSettings);
227235
return graphqlRequest;
228236
}
229237

238+
/// <summary>
239+
/// Initialize the query body for a GraphQL query request.
240+
/// </summary>
241+
/// <param name="request"></param>
242+
/// <param name="fieldName"></param>
243+
/// <returns>Returns an IFlurlGraphQLRequest for ready to chain for further initialization or execution.</returns>
244+
public static IFlurlGraphQLRequest SetPersistedQueryPayloadFieldName(this IFlurlRequest request, string fieldName)
245+
{
246+
fieldName.AssertArgIsNotNullOrBlank(nameof(fieldName));
247+
var graphqlRequest = (FlurlGraphQLRequest)request.ToGraphQLRequest();
248+
graphqlRequest.SetContextItem(ContextItemKeys.PersistedQueryPayloadFieldName, fieldName);
249+
return graphqlRequest;
250+
}
251+
252+
230253
#endregion
231254

232255
#region PostGraphQLQueryAsnc()...

FlurlGraphQL.Querying/Flurl/InternalClasses/FlurlGraphQLRequestPayload.cs

Lines changed: 0 additions & 31 deletions
This file was deleted.

FlurlGraphQL.Querying/Flurl/InternalClasses/FlurlGraphQLResponsePayload.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public IGraphQLQueryResults<TResult> LoadTypedResults<TResult>(string queryOpera
3434
? queryResultJson.FirstField()
3535
: queryResultJson.Field(queryOperationName);
3636

37-
var jsonSerializerSettings = ContextBag?.TryGetValue(nameof(JsonSerializerSettings), out var serializerSettings) ?? false
37+
var jsonSerializerSettings = ContextBag?.TryGetValue(ContextItemKeys.NewtonsoftJsonSerializerSettings, out var serializerSettings) ?? false
3838
? serializerSettings as JsonSerializerSettings
3939
: null;
4040

FlurlGraphQL.Querying/FlurlGraphQL.Querying.csproj

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
5-
<Version>1.1.0</Version>
6-
<AssemblyVersion>1.1.0</AssemblyVersion>
7-
<FileVersion>1.1.0</FileVersion>
5+
<Version>1.2.0</Version>
6+
<AssemblyVersion>1.2.0</AssemblyVersion>
7+
<FileVersion>1.2.0</FileVersion>
88
<Authors>BBernard / CajunCoding</Authors>
99
<Company>CajunCoding</Company>
1010
<Description>GraphQL client querying extensions for Flurl.Http -- lightweight, simplified, asynchronous, fluent GraphQL client querying API extensions for the amazing Flurl Http library!</Description>
@@ -14,13 +14,15 @@
1414
<RepositoryUrl>https://github.com/cajuncoding/FlurlGraphQL</RepositoryUrl>
1515
<PackageReleaseNotes>
1616
Release Notes:
17-
- Added support for Persisted Queries via .WithGraphQLPersistedQuery() api.
18-
- Added support to execute GraphQL as GET requests for edge cases, though POST requests are highly encouraged.
19-
- Improved consistency of use of (optional) custom Json Serialization settings when SetGraphQLNewtonsoftJsonSerializerSettings() is used to override the default GraphQL settings;
20-
now initial query payloads sent also use these instead of the default Flurl settings. Some edge cases in GraphQL may require more advanced control over the Json serialization process
21-
than the default Flurl Serializer interface offers and the default settings are internal and not accessible so we provide a way to control this for GraphQL specifically.
17+
- Added support to control the Persisted Query payload field name for other GraphQL servers (e.g. Relay server) which may be different than HotChocolate .NET GraphQL Server.
18+
- Added global configuration support via FlurlGraphQLConfig.ConfigureDefaults(config => ...) so that configurable options can be set once globlly with current support for Persisted Query Field Name and Json Serializer Settings.
2219

2320
Prior Release Notes:
21+
- Added support for Persisted Queries via .WithGraphQLPersistedQuery() api.
22+
- Added support to execute GraphQL as GET requests for edge cases, though POST requests are highly encouraged.
23+
- Improved consistency of use of (optional) custom Json Serialization settings when SetGraphQLNewtonsoftJsonSerializerSettings() is used to override the default GraphQL settings;
24+
now initial query payloads sent also use these instead of the default Flurl settings. Some edge cases in GraphQL may require more advanced control over the Json serialization process
25+
than the default Flurl Serializer interface offers and the default settings are internal and not accessible so we provide a way to control this for GraphQL specifically.
2426
- Initial release of the GraphQL Querying (only) extensions for Flurl.Http.
2527
- Supporting querying of typed data results with support for Relay based Cursor Paging, HotChocolate based Offset paging, Batch querying, Flurl style exception handling,
2628
and simplified data models when using nested paginated results from GraphQL.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using FlurlGraphQL.Querying.Tests.Models;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
using Newtonsoft.Json;
6+
7+
namespace FlurlGraphQL.Querying.Tests
8+
{
9+
[TestClass]
10+
public class FlurlGraphQLConfigTests : BaseFlurlGraphQLTest
11+
{
12+
[TestMethod]
13+
public void TestGlobalConfig()
14+
{
15+
var defaultConfig = FlurlGraphQLConfig.DefaultConfig;
16+
Assert.AreEqual(FlurlGraphQLConfig.DefaultPersistedQueryFieldName, defaultConfig.PersistedQueryPayloadFieldName);
17+
Assert.IsNull(defaultConfig.NewtonsoftJsonSerializerSettings);
18+
19+
FlurlGraphQLConfig.ConfigureDefaults(config =>
20+
{
21+
config.PersistedQueryPayloadFieldName = "test";
22+
config.NewtonsoftJsonSerializerSettings = new JsonSerializerSettings()
23+
{
24+
MaxDepth = 99,
25+
NullValueHandling = NullValueHandling.Include,
26+
TypeNameHandling = TypeNameHandling.None
27+
};
28+
});
29+
30+
var newConfig = FlurlGraphQLConfig.DefaultConfig;
31+
Assert.IsTrue(newConfig is IFlurlGraphQLConfig);
32+
Assert.AreEqual("test", newConfig.PersistedQueryPayloadFieldName);
33+
Assert.AreEqual(99, newConfig.NewtonsoftJsonSerializerSettings.MaxDepth);
34+
35+
Assert.AreNotEqual(defaultConfig.PersistedQueryPayloadFieldName, newConfig.PersistedQueryPayloadFieldName);
36+
Assert.AreNotEqual(defaultConfig.NewtonsoftJsonSerializerSettings, newConfig.NewtonsoftJsonSerializerSettings);
37+
38+
//We need to RESET our Defaults so we don't affect other Unit Tests...
39+
FlurlGraphQLConfig.ResetDefaults();
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)