Skip to content

Commit edb22d3

Browse files
committed
Add FlurlGraphQL icons and Nuget icon configuration. Updated Readme with details for v2.0 release! Code cleanup to consolidate convert methods from custom extensions to now be encapsulated within the response processor classes that implement them for better code readability and maintainability as this functionality doesn't need to be external to the processors that use them. Updated GitHub action to deploy FlurlGraphQL.Newtonsoft package.
1 parent 9db684b commit edb22d3

16 files changed

+366
-237
lines changed

.github/workflows/main.yml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,19 @@ jobs:
2323
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
2424
- uses: actions/checkout@v2
2525

26-
# Runs a single command using the runners shell
2726
- name: Run a one-line script
28-
run: echo Executing Main Branch commit Workflow!
29-
30-
# Runs a set of commands using the runners shell
31-
#- name: Run a multi-line script
32-
# run: |
33-
# echo Add other actions to build,
34-
# echo test, and deploy your project.
35-
- name: "Publish NuGet: FlurlGraphQL.Querying"
27+
run: echo Executing Main Branch NuGet Publish Workflow!
28+
29+
- name: "Publish NuGet: FlurlGraphQL"
30+
uses: alirezanet/[email protected]
31+
with:
32+
# Filepath of the project to be packaged, relative to root of repository
33+
PROJECT_FILE_PATH: FlurlGraphQL/FlurlGraphQL.csproj
34+
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
35+
36+
- name: "Publish NuGet: FlurlGraphQL"
3637
uses: alirezanet/[email protected]
3738
with:
3839
# Filepath of the project to be packaged, relative to root of repository
39-
PROJECT_FILE_PATH: FlurlGraphQL.Querying/FlurlGraphQL.Querying.csproj
40+
PROJECT_FILE_PATH: FlurlGraphQL.Newtonsoft/FlurlGraphQL.Newtonsoft.csproj
4041
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
191 KB
Binary file not shown.
60.3 KB
Loading
44.1 KB
Loading
6.51 KB
Loading

FlurlGraphQL.Newtonsoft/FlurlGraphQL.Newtonsoft.csproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
<FileVersion>2.0.0</FileVersion>
1111
<Authors>BBernard / CajunCoding</Authors>
1212
<Company>CajunCoding</Company>
13-
<Description>Newtonsoft JSON Supportt for FlurlGraphQL -- A GraphQL client extensions for Flurl.Http - lightweight, simplified, asynchronous, fluent GraphQL client API extensions for the amazing Flurl Http library!</Description>
13+
<Description>Newtonsoft JSON Supportt for FlurlGraphQL v2.0+ -- A GraphQL client extensions for Flurl.Http - lightweight, simplified, asynchronous, fluent GraphQL client API extensions for the amazing Flurl Http library!</Description>
1414
<Copyright>Copyright © 2023</Copyright>
1515
<PackageLicenseExpression>MIT</PackageLicenseExpression>
16+
<PackageIcon>flurl-graphql-icon-nuget.png</PackageIcon>
1617
<PackageProjectUrl>https://github.com/cajuncoding/FlurlGraphQL</PackageProjectUrl>
1718
<RepositoryUrl>https://github.com/cajuncoding/FlurlGraphQL</RepositoryUrl>
1819
<PackageReleaseNotes>
1920
Release Notes:
20-
- Newtonsoft JSON Compatibility implementation for FlurlGraphQL using the all new Flurl v4.0.
21+
- Newtonsoft JSON Compatibility implementation for FlurlGraphQL v2.0 using the all new Flurl v4.0+.
2122

2223
</PackageReleaseNotes>
2324
<PackageTags>graphql, graph-ql, graphql client, graphql query, flurl, newtonsoft, rest, http, http client, json, hotchocolate, paging, cursor</PackageTags>
@@ -44,4 +45,7 @@
4445
<InternalsVisibleTo Include="FlurlGraphQL.Benchmarks" />
4546
</ItemGroup>
4647

48+
<ItemGroup>
49+
<None Include="..\FlurlGraphQL.Icons\flurl-graphql-icon-nuget.png" Pack="true" Visible="true" PackagePath="" />
50+
</ItemGroup>
4751
</Project>

FlurlGraphQL.Newtonsoft/FlurlGraphQLNewtonsoftJsonExtensions.cs

Lines changed: 1 addition & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
using System.Threading.Tasks;
55
using Flurl.Http;
66
using Flurl.Http.Newtonsoft;
7-
using FlurlGraphQL.ReflectionExtensions;
8-
using FlurlGraphQL.TypeCacheHelpers;
97
using Newtonsoft.Json;
108
using Newtonsoft.Json.Linq;
119

@@ -62,107 +60,7 @@ public static IFlurlGraphQLRequest UseGraphQLNewtonsoftJson(this IFlurlGraphQLRe
6260

6361
#region Json Parsing Extensions - JsonConvert Strategy
6462

65-
internal static IGraphQLQueryResults<TEntityResult> ParseJsonToGraphQLResultsInternal<TEntityResult>(this JToken json, JsonSerializerSettings jsonSerializerSettings = null)
66-
where TEntityResult : class
67-
{
68-
if (json == null)
69-
return new GraphQLQueryResults<TEntityResult>();
70-
71-
//Ensure that all json parsing uses a Serializer with the GraphQL Contract Resolver...
72-
//NOTE: We still support normal Serializer Default settings via Newtonsoft framework!
73-
var jsonSerializer = JsonSerializer.CreateDefault(jsonSerializerSettings);
74-
jsonSerializer.Converters.Add(new FlurlGraphQLNewtonsoftJsonPaginatedResultsConverter());
75-
76-
return ParseJsonToGraphQLResultsWithJsonSerializerInternal<TEntityResult>(json, jsonSerializer);
77-
}
78-
79-
internal static IGraphQLQueryResults<TEntityResult> ParseJsonToGraphQLResultsWithJsonSerializerInternal<TEntityResult>(this JToken json, JsonSerializer jsonSerializer)
80-
where TEntityResult : class
81-
{
82-
if (json == null)
83-
return new GraphQLQueryResults<TEntityResult>();
84-
85-
//Dynamically parse the data from the results...
86-
//NOTE: We process PageInfo as Cursor Paging as the Default (because it's strongly encouraged by GraphQL.org
87-
// & Offset Paging model is a subset of Cursor Paging (less flexible).
88-
var pageInfo = json.Field(GraphQLFields.PageInfo)?.ToObject<GraphQLCursorPageInfo>();
89-
var totalCount = (int?)json.Field(GraphQLFields.TotalCount);
90-
91-
PaginationType? paginationType = null;
92-
IReadOnlyList<TEntityResult> entityResults = null;
93-
94-
//Dynamically resolve the Results from:
95-
// - the Nodes child of the Data Result (for nodes{} based Cursor Paginated queries)
96-
// - the Items child of the Data Result (for items{} based Offset Paginated queries)
97-
// - the Edges->Node child of the the Data Result (for Edges based queries that provide access to the Cursor)
98-
// - finally use the (non-nested) array of results if not a Paginated result set of any kind above...
99-
if (json.Field(GraphQLFields.Nodes) is JArray nodesJson)
100-
{
101-
entityResults = nodesJson.ToObject<TEntityResult[]>(jsonSerializer);
102-
paginationType = PaginationType.Cursor;
103-
}
104-
else if (json.Field(GraphQLFields.Items) is JArray itemsJson)
105-
{
106-
entityResults = itemsJson.ToObject<TEntityResult[]>(jsonSerializer);
107-
paginationType = PaginationType.Offset;
108-
}
109-
//Handle Edges case (which allow access to the Cursor)
110-
else if (json.Field(GraphQLFields.Edges) is JArray edgesJson)
111-
{
112-
paginationType = PaginationType.Cursor;
113-
var entityType = typeof(TEntityResult);
114-
115-
//Handle case where GraphQLEdge<TNode> wrapper class is used to simplify retrieving the Edges!
116-
if (entityType.IsDerivedFromGenericParent(GraphQLTypeCache.IGraphQLEdgeEntityType))
117-
{
118-
//If the current type is a Generic GraphQLEdge<TEntity> then we can directly deserialize to the Generic Type!
119-
//entityResults = edges.Select(edge => edge?.ToObject<TEntityResult>(jsonSerializer)).ToList();
120-
entityResults = edgesJson.ToObject<TEntityResult[]>(jsonSerializer);
121-
}
122-
//Handle all other cases including when the Entity implements IGraphQLEdge (e.g. the entity has a Cursor Property)...
123-
else
124-
{
125-
entityResults = edgesJson
126-
.FlattenGraphQLEdgesJsonToArrayOfNodes()
127-
.ToObject<TEntityResult[]>(jsonSerializer);
128-
}
129-
}
130-
else
131-
{
132-
switch (json)
133-
{
134-
case JArray arrayResults:
135-
entityResults = arrayResults.ToObject<TEntityResult[]>(jsonSerializer);
136-
break;
137-
//TODO: Determine what this use case is really here to support????
138-
case JObject jsonObj when jsonObj.First is JArray firstArrayResults:
139-
entityResults = firstArrayResults.ToObject<TEntityResult[]>(jsonSerializer);
140-
break;
141-
//If only a single Object was returned then this is likely a Mutation so we return the single
142-
// item as the first-and-only result of the set...
143-
case JObject jsonObj:
144-
var singleResult = jsonObj.ToObject<TEntityResult>(jsonSerializer);
145-
entityResults = new[] { singleResult };
146-
break;
147-
}
148-
}
149-
150-
switch (paginationType)
151-
{
152-
//If the results have Paging Info we map to the correct type (Connection/Cursor or CollectionSegment/Offset)...
153-
case PaginationType.Cursor:
154-
return new GraphQLConnectionResults<TEntityResult>(entityResults, totalCount, pageInfo);
155-
case PaginationType.Offset:
156-
return new GraphQLCollectionSegmentResults<TEntityResult>(entityResults, totalCount, GraphQLOffsetPageInfo.FromCursorPageInfo(pageInfo));
157-
default:
158-
{
159-
//If we have a Total Count then we also must return a Paging result because it's possible to request TotalCount by itself without any other PageInfo or Nodes...
160-
return totalCount.HasValue
161-
? new GraphQLConnectionResults<TEntityResult>(entityResults, totalCount, pageInfo)
162-
: new GraphQLQueryResults<TEntityResult>(entityResults);
163-
}
164-
}
165-
}
63+
16664

16765
internal static JArray FlattenGraphQLEdgesJsonToArrayOfNodes(this JArray edgesJson)
16866
{

FlurlGraphQL.Newtonsoft/FlurlGraphQLNewtonsoftJsonResponseBaseProcessor.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ protected FlurlGraphQLNewtonsoftJsonResponseBaseProcessor(JObject rawDataJObject
1212
{
1313
this.RawDataJObject = rawDataJObject;
1414
this.Errors = errors?.AsReadOnly();
15-
this.JsonSerializer = newtonsoftJsonSerializer.AssertArgIsNotNull(nameof(newtonsoftJsonSerializer));
15+
this.GraphQLJsonSerializer = newtonsoftJsonSerializer.AssertArgIsNotNull(nameof(newtonsoftJsonSerializer));
1616
}
1717

1818
#region Non-interface Properties
19-
public FlurlGraphQLNewtonsoftJsonSerializer JsonSerializer { get; }
19+
public FlurlGraphQLNewtonsoftJsonSerializer GraphQLJsonSerializer { get; }
2020
#endregion
2121

2222
protected JObject RawDataJObject { get; }
@@ -44,6 +44,6 @@ public virtual IGraphQLBatchQueryResults LoadBatchQueryResults()
4444
}
4545

4646
public virtual string GetErrorContent()
47-
=> ErrorContentSerialized ?? (ErrorContentSerialized = JsonSerializer.Serialize(this.Errors));
47+
=> ErrorContentSerialized ?? (ErrorContentSerialized = GraphQLJsonSerializer.Serialize(this.Errors));
4848
}
4949
}

FlurlGraphQL.Newtonsoft/FlurlGraphQLNewtonsoftJsonResponseConverterProcessor.cs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using System;
22
using System.Collections.Generic;
33
using Newtonsoft.Json.Linq;
4+
using Newtonsoft.Json;
5+
using FlurlGraphQL.ReflectionExtensions;
6+
using FlurlGraphQL.TypeCacheHelpers;
47

58
namespace FlurlGraphQL.JsonProcessing
69
{
@@ -24,13 +27,115 @@ public override IGraphQLQueryResults<TResult> LoadTypedResults<TResult>(string q
2427
? rawDataJson.FirstField()
2528
: rawDataJson.Field(queryOperationName);
2629

27-
var typedResults = querySingleResultJson.ParseJsonToGraphQLResultsInternal<TResult>(JsonSerializer.JsonSerializerSettings);
30+
var typedResults = ParseJsonToGraphQLResultsInternal<TResult>(querySingleResultJson, GraphQLJsonSerializer.JsonSerializerSettings);
2831

2932
//Ensure that the Results we return are initialized along with any potential Errors (that have already been parsed/captured)...
3033
if (this.Errors != null && typedResults is GraphQLQueryResults<TResult> graphqlResults)
3134
graphqlResults.Errors = this.Errors;
3235

3336
return typedResults;
3437
}
38+
39+
internal IGraphQLQueryResults<TEntityResult> ParseJsonToGraphQLResultsInternal<TEntityResult>(JToken json, JsonSerializerSettings jsonSerializerSettings = null)
40+
where TEntityResult : class
41+
{
42+
if (json == null)
43+
return new GraphQLQueryResults<TEntityResult>();
44+
45+
//Ensure that all json parsing uses a Serializer with the GraphQL Contract Resolver...
46+
//NOTE: We still support normal Serializer Default settings via Newtonsoft framework!
47+
var jsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(jsonSerializerSettings);
48+
jsonSerializer.Converters.Add(new FlurlGraphQLNewtonsoftJsonPaginatedResultsConverter());
49+
50+
return ParseJsonToGraphQLResultsWithJsonSerializerInternal<TEntityResult>(json, jsonSerializer);
51+
}
52+
53+
internal IGraphQLQueryResults<TEntityResult> ParseJsonToGraphQLResultsWithJsonSerializerInternal<TEntityResult>(JToken json, JsonSerializer jsonSerializer)
54+
where TEntityResult : class
55+
{
56+
if (json == null)
57+
return new GraphQLQueryResults<TEntityResult>();
58+
59+
//Dynamically parse the data from the results...
60+
//NOTE: We process PageInfo as Cursor Paging as the Default (because it's strongly encouraged by GraphQL.org
61+
// & Offset Paging model is a subset of Cursor Paging (less flexible).
62+
var pageInfo = json.Field(GraphQLFields.PageInfo)?.ToObject<GraphQLCursorPageInfo>();
63+
var totalCount = (int?)json.Field(GraphQLFields.TotalCount);
64+
65+
PaginationType? paginationType = null;
66+
IReadOnlyList<TEntityResult> entityResults = null;
67+
68+
//Dynamically resolve the Results from:
69+
// - the Nodes child of the Data Result (for nodes{} based Cursor Paginated queries)
70+
// - the Items child of the Data Result (for items{} based Offset Paginated queries)
71+
// - the Edges->Node child of the the Data Result (for Edges based queries that provide access to the Cursor)
72+
// - finally use the (non-nested) array of results if not a Paginated result set of any kind above...
73+
if (json.Field(GraphQLFields.Nodes) is JArray nodesJson)
74+
{
75+
entityResults = nodesJson.ToObject<TEntityResult[]>(jsonSerializer);
76+
paginationType = PaginationType.Cursor;
77+
}
78+
else if (json.Field(GraphQLFields.Items) is JArray itemsJson)
79+
{
80+
entityResults = itemsJson.ToObject<TEntityResult[]>(jsonSerializer);
81+
paginationType = PaginationType.Offset;
82+
}
83+
//Handle Edges case (which allow access to the Cursor)
84+
else if (json.Field(GraphQLFields.Edges) is JArray edgesJson)
85+
{
86+
paginationType = PaginationType.Cursor;
87+
var entityType = typeof(TEntityResult);
88+
89+
//Handle case where GraphQLEdge<TNode> wrapper class is used to simplify retrieving the Edges!
90+
if (entityType.IsDerivedFromGenericParent(GraphQLTypeCache.IGraphQLEdgeEntityType))
91+
{
92+
//If the current type is a Generic GraphQLEdge<TEntity> then we can directly deserialize to the Generic Type!
93+
//entityResults = edges.Select(edge => edge?.ToObject<TEntityResult>(jsonSerializer)).ToList();
94+
entityResults = edgesJson.ToObject<TEntityResult[]>(jsonSerializer);
95+
}
96+
//Handle all other cases including when the Entity implements IGraphQLEdge (e.g. the entity has a Cursor Property)...
97+
else
98+
{
99+
entityResults = edgesJson
100+
.FlattenGraphQLEdgesJsonToArrayOfNodes()
101+
.ToObject<TEntityResult[]>(jsonSerializer);
102+
}
103+
}
104+
else
105+
{
106+
switch (json)
107+
{
108+
case JArray arrayResults:
109+
entityResults = arrayResults.ToObject<TEntityResult[]>(jsonSerializer);
110+
break;
111+
//TODO: Determine what this use case is really here to support????
112+
case JObject jsonObj when jsonObj.First is JArray firstArrayResults:
113+
entityResults = firstArrayResults.ToObject<TEntityResult[]>(jsonSerializer);
114+
break;
115+
//If only a single Object was returned then this is likely a Mutation so we return the single
116+
// item as the first-and-only result of the set...
117+
case JObject jsonObj:
118+
var singleResult = jsonObj.ToObject<TEntityResult>(jsonSerializer);
119+
entityResults = new[] { singleResult };
120+
break;
121+
}
122+
}
123+
124+
switch (paginationType)
125+
{
126+
//If the results have Paging Info we map to the correct type (Connection/Cursor or CollectionSegment/Offset)...
127+
case PaginationType.Cursor:
128+
return new GraphQLConnectionResults<TEntityResult>(entityResults, totalCount, pageInfo);
129+
case PaginationType.Offset:
130+
return new GraphQLCollectionSegmentResults<TEntityResult>(entityResults, totalCount, GraphQLOffsetPageInfo.FromCursorPageInfo(pageInfo));
131+
default:
132+
{
133+
//If we have a Total Count then we also must return a Paging result because it's possible to request TotalCount by itself without any other PageInfo or Nodes...
134+
return totalCount.HasValue
135+
? new GraphQLConnectionResults<TEntityResult>(entityResults, totalCount, pageInfo)
136+
: new GraphQLQueryResults<TEntityResult>(entityResults);
137+
}
138+
}
139+
}
35140
}
36141
}

FlurlGraphQL.Newtonsoft/FlurlGraphQLNewtonsoftJsonResponseTransformProcessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public override IGraphQLQueryResults<TResult> LoadTypedResults<TResult>(string q
2121
? rawDataJson.FirstField()
2222
: rawDataJson.Field(queryOperationName);
2323

24-
var typedResults = querySingleResultJson.ConvertNewtonsoftJsonToGraphQLResultsWithJsonSerializerInternal<TResult>(JsonSerializer.JsonSerializerSettings);
24+
var typedResults = querySingleResultJson.ConvertNewtonsoftJsonToGraphQLResultsWithJsonSerializerInternal<TResult>(GraphQLJsonSerializer.JsonSerializerSettings);
2525

2626
//Ensure that the Results we return are initialized along with any potential Errors (that have already been parsed/captured)...
2727
if (this.Errors != null && typedResults is GraphQLQueryResults<TResult> graphqlResults)

0 commit comments

Comments
 (0)