Skip to content

Commit

Permalink
add StarWars schema classes, fix local execution client
Browse files Browse the repository at this point in the history
  • Loading branch information
rose-a committed Sep 14, 2021
1 parent ce649f7 commit fe110b6
Show file tree
Hide file tree
Showing 20 changed files with 400 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="GraphQL" Version="2.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Reactive.Compatibility" Version="4.3.2" />
<PackageReference Include="GraphQL" Version="4.6.0" />
<PackageReference Include="GraphQL.NewtonsoftJson" Version="4.6.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
10 changes: 7 additions & 3 deletions src/GraphQL.Client.LocalExecution/GraphQLLocalExecutionClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading.Tasks;
using GraphQL.Client.Abstractions;
using GraphQL.Client.Serializer.Newtonsoft;
using GraphQL.NewtonsoftJson;
using GraphQL.Subscription;
using GraphQL.Types;
using Newtonsoft.Json;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class GraphQLLocalExecutionClient<TSchema> : IGraphQLClient where TSchema
public IGraphQLJsonSerializer Serializer { get; }

private readonly DocumentExecuter _documentExecuter;
private readonly DocumentWriter _documentWriter;

public GraphQLLocalExecutionClient(TSchema schema, IGraphQLJsonSerializer serializer)
{
Expand All @@ -50,6 +52,7 @@ public GraphQLLocalExecutionClient(TSchema schema, IGraphQLJsonSerializer serial
if (!Schema.Initialized)
Schema.Initialize();
_documentExecuter = new DocumentExecuter();
_documentWriter = new DocumentWriter();
}

public void Dispose() { }
Expand Down Expand Up @@ -109,12 +112,13 @@ private async Task<ExecutionResult> ExecuteAsync(GraphQLRequest request, Cancell
return result;
}

private Task<GraphQLResponse<TResponse>> ExecutionResultToGraphQLResponse<TResponse>(ExecutionResult executionResult, CancellationToken cancellationToken = default)
private async Task<GraphQLResponse<TResponse>> ExecutionResultToGraphQLResponse<TResponse>(ExecutionResult executionResult, CancellationToken cancellationToken = default)
{
string json = await _documentWriter.WriteToStringAsync(executionResult);
// serialize result into utf8 byte stream
var resultStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(executionResult, _variablesSerializerSettings)));
var resultStream = new MemoryStream(Encoding.UTF8.GetBytes(json));
// deserialize using the provided serializer
return Serializer.DeserializeFromUtf8StreamAsync<TResponse>(resultStream, cancellationToken);
return await Serializer.DeserializeFromUtf8StreamAsync<TResponse>(resultStream, cancellationToken);
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using GraphQL.Client.Tests.Common.Chat.Schema;
using GraphQL.Client.Tests.Common.Helpers;
using GraphQL.Client.Tests.Common.StarWars;
using GraphQL.Client.Tests.Common.StarWars.TestData;
using Xunit;

namespace GraphQL.Client.Serializer.Tests
Expand Down
4 changes: 2 additions & 2 deletions tests/GraphQL.Client.Tests.Common/Common.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using GraphQL.Client.Tests.Common.Chat.Schema;
using GraphQL.StarWars;
using GraphQL.StarWars.Types;
using GraphQL.Client.Tests.Common.StarWars;
using GraphQL.Client.Tests.Common.StarWars.Types;
using Microsoft.Extensions.DependencyInjection;

namespace GraphQL.Client.Tests.Common
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="GraphQL" Version="4.6.0" />
<PackageReference Include="GraphQL.Server.Transports.Subscriptions.Abstractions" Version="5.0.2" />
<PackageReference Include="GraphQL.StarWars" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
<PackageReference Include="Microsoft.Reactive.Testing" Version="5.0.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GraphQL.Builders;
using GraphQL.Client.Tests.Common.StarWars.Types;
using GraphQL.Types.Relay.DataObjects;

namespace GraphQL.Client.Tests.Common.StarWars.Extensions
{
public static class ResolveFieldContextExtensions
{
public static Connection<U> GetPagedResults<T, U>(this IResolveConnectionContext<T> context, StarWarsData data, List<string> ids) where U : StarWarsCharacter
{
List<string> idList;
List<U> list;
string cursor;
string endCursor;
var pageSize = context.PageSize ?? 20;

if (context.IsUnidirectional || context.After != null || context.Before == null)
{
if (context.After != null)
{
idList = ids
.SkipWhile(x => !Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(x)).Equals(context.After))
.Take(context.First ?? pageSize).ToList();
}
else
{
idList = ids
.Take(context.First ?? pageSize).ToList();
}
}
else
{
if (context.Before != null)
{
idList = ids.Reverse<string>()
.SkipWhile(x => !Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(x)).Equals(context.Before))
.Take(context.Last ?? pageSize).ToList();
}
else
{
idList = ids.Reverse<string>()
.Take(context.Last ?? pageSize).ToList();
}
}

list = data.GetCharactersAsync(idList).Result as List<U> ?? throw new InvalidOperationException($"GetCharactersAsync method should return list of '{typeof(U).Name}' items.");
cursor = list.Count > 0 ? list.Last().Cursor : null;
endCursor = ids.Count > 0 ? Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(ids.Last())) : null;

return new Connection<U>
{
Edges = list.Select(x => new Edge<U> { Cursor = x.Cursor, Node = x }).ToList(),
TotalCount = list.Count,
PageInfo = new PageInfo
{
EndCursor = endCursor,
HasNextPage = endCursor == null ? false : cursor != endCursor,
}
};
}
}
}
90 changes: 90 additions & 0 deletions tests/GraphQL.Client.Tests.Common/StarWars/StarWarsData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GraphQL.Client.Tests.Common.StarWars.Types;

namespace GraphQL.Client.Tests.Common.StarWars
{
public class StarWarsData
{
private readonly List<StarWarsCharacter> _characters = new List<StarWarsCharacter>();

public StarWarsData()
{
_characters.Add(new Human
{
Id = "1",
Name = "Luke",
Friends = new List<string> { "3", "4" },
AppearsIn = new[] { 4, 5, 6 },
HomePlanet = "Tatooine",
Cursor = "MQ=="
});
_characters.Add(new Human
{
Id = "2",
Name = "Vader",
AppearsIn = new[] { 4, 5, 6 },
HomePlanet = "Tatooine",
Cursor = "Mg=="
});

_characters.Add(new Droid
{
Id = "3",
Name = "R2-D2",
Friends = new List<string> { "1", "4" },
AppearsIn = new[] { 4, 5, 6 },
PrimaryFunction = "Astromech",
Cursor = "Mw=="
});
_characters.Add(new Droid
{
Id = "4",
Name = "C-3PO",
AppearsIn = new[] { 4, 5, 6 },
PrimaryFunction = "Protocol",
Cursor = "NA=="
});
}

public IEnumerable<StarWarsCharacter> GetFriends(StarWarsCharacter character)
{
if (character == null)
{
return null;
}

var friends = new List<StarWarsCharacter>();
var lookup = character.Friends;
if (lookup != null)
{
foreach (var c in _characters.Where(h => lookup.Contains(h.Id)))
friends.Add(c);
}
return friends;
}

public StarWarsCharacter AddCharacter(StarWarsCharacter character)
{
character.Id = _characters.Count.ToString();
_characters.Add(character);
return character;
}

public Task<Human> GetHumanByIdAsync(string id)
{
return Task.FromResult(_characters.FirstOrDefault(h => h.Id == id && h is Human) as Human);
}

public Task<Droid> GetDroidByIdAsync(string id)
{
return Task.FromResult(_characters.FirstOrDefault(h => h.Id == id && h is Droid) as Droid);
}

public Task<List<StarWarsCharacter>> GetCharactersAsync(List<string> guids)
{
return Task.FromResult(_characters.Where(c => guids.Contains(c.Id)).ToList());
}
}
}
35 changes: 35 additions & 0 deletions tests/GraphQL.Client.Tests.Common/StarWars/StarWarsMutation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using GraphQL.Client.Tests.Common.StarWars.Types;
using GraphQL.Types;

namespace GraphQL.Client.Tests.Common.StarWars
{
/// <example>
/// This is an example JSON request for a mutation
/// {
/// "query": "mutation ($human:HumanInput!){ createHuman(human: $human) { id name } }",
/// "variables": {
/// "human": {
/// "name": "Boba Fett"
/// }
/// }
/// }
/// </example>
public class StarWarsMutation : ObjectGraphType<object>
{
public StarWarsMutation(StarWarsData data)
{
Name = "Mutation";

Field<HumanType>(
"createHuman",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<HumanInputType>> { Name = "human" }
),
resolve: context =>
{
var human = context.GetArgument<Human>("human");
return data.AddCharacter(human);
});
}
}
}
33 changes: 33 additions & 0 deletions tests/GraphQL.Client.Tests.Common/StarWars/StarWarsQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using GraphQL.Client.Tests.Common.StarWars.Types;
using GraphQL.Types;

namespace GraphQL.Client.Tests.Common.StarWars
{
public class StarWarsQuery : ObjectGraphType<object>
{
public StarWarsQuery(StarWarsData data)
{
Name = "Query";

Field<CharacterInterface>("hero", resolve: context => data.GetDroidByIdAsync("3"));
Field<HumanType>(
"human",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the human" }
),
resolve: context => data.GetHumanByIdAsync(context.GetArgument<string>("id"))
);

Func<IResolveFieldContext, string, object> func = (context, id) => data.GetDroidByIdAsync(id);

FieldDelegate<DroidType>(
"droid",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the droid" }
),
resolve: func
);
}
}
}
18 changes: 18 additions & 0 deletions tests/GraphQL.Client.Tests.Common/StarWars/StarWarsSchema.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using GraphQL.Types;
using Microsoft.Extensions.DependencyInjection;

namespace GraphQL.Client.Tests.Common.StarWars
{
public class StarWarsSchema : Schema
{
public StarWarsSchema(IServiceProvider serviceProvider)
: base(serviceProvider)
{
Query = serviceProvider.GetRequiredService<StarWarsQuery>();
Mutation = serviceProvider.GetRequiredService<StarWarsMutation>();

Description = "Example StarWars universe schema";
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System.Collections;
using System.Collections.Generic;

namespace GraphQL.Client.Tests.Common.StarWars
namespace GraphQL.Client.Tests.Common.StarWars.TestData
{
/// <summary>
/// Test data object
/// </summary>
public class StarWarsHumans : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using GraphQL.Types;
using GraphQL.Types.Relay;

namespace GraphQL.Client.Tests.Common.StarWars.Types
{
public class CharacterInterface : InterfaceGraphType<StarWarsCharacter>
{
public CharacterInterface()
{
Name = "Character";

Field<NonNullGraphType<StringGraphType>>("id", "The id of the character.", resolve: context => context.Source.Id);
Field<StringGraphType>("name", "The name of the character.", resolve: context => context.Source.Name);

Field<ListGraphType<CharacterInterface>>("friends");
Field<ConnectionType<CharacterInterface, EdgeType<CharacterInterface>>>("friendsConnection");
Field<ListGraphType<EpisodeEnum>>("appearsIn", "Which movie they appear in.");
}
}
}
30 changes: 30 additions & 0 deletions tests/GraphQL.Client.Tests.Common/StarWars/Types/DroidType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using GraphQL.Client.Tests.Common.StarWars.Extensions;
using GraphQL.Types;

namespace GraphQL.Client.Tests.Common.StarWars.Types
{
public class DroidType : ObjectGraphType<Droid>
{
public DroidType(StarWarsData data)
{
Name = "Droid";
Description = "A mechanical creature in the Star Wars universe.";

Field<NonNullGraphType<StringGraphType>>("id", "The id of the droid.", resolve: context => context.Source.Id);
Field<StringGraphType>("name", "The name of the droid.", resolve: context => context.Source.Name);

Field<ListGraphType<CharacterInterface>>("friends", resolve: context => data.GetFriends(context.Source));

Connection<CharacterInterface>()
.Name("friendsConnection")
.Description("A list of a character's friends.")
.Bidirectional()
.Resolve(context => context.GetPagedResults<Droid, StarWarsCharacter>(data, context.Source.Friends));

Field<ListGraphType<EpisodeEnum>>("appearsIn", "Which movie they appear in.");
Field<StringGraphType>("primaryFunction", "The primary function of the droid.", resolve: context => context.Source.PrimaryFunction);

Interface<CharacterInterface>();
}
}
}
Loading

0 comments on commit fe110b6

Please sign in to comment.