Last active
September 14, 2017 06:42
-
-
Save trbngr/dd6721bc03c8d08750076b309392baf4 to your computer and use it in GitHub Desktop.
experiments in graphql-dotnet syntax
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq.Expressions; | |
using GraphQL; | |
using GraphQL.Builders; | |
using GraphQL.Resolvers; | |
using GraphQL.Types; | |
using LanguageExt; | |
using static LanguageExt.Prelude; | |
namespace Api.GraphQL | |
{ | |
public class Syntax | |
{ | |
public static Try<ObjectGraphType<T>> GraphType<T>( | |
string name, | |
string description = null, | |
FieldsBuilder<T> fields = null, | |
GraphTypeConfig<T> config = null, | |
IEnumerable<Type> interfaces = null) | |
{ | |
var definedFields = Optional(fields).IfNone(_ => Try(Lst<FieldType>.Empty)); | |
var configurationFn = Optional(config).IfNone(identity); | |
return definedFields(default(Fields<T>)) | |
.Map(fs => fs.Fold( | |
state: configurationFn(new ObjectGraphType<T> | |
{ | |
Name = name ?? typeof(T).Name, | |
Description = description ?? $"{typeof(T).Name} Type", | |
Interfaces = interfaces ?? new Type[0] | |
}), | |
folder: (gt, field) => | |
{ | |
gt.AddField(field); | |
return gt; | |
}) | |
); | |
} | |
public static Try<ObjectGraphType<object>> Query(QueryFieldsBuilder fields) => | |
fields(default(Fields<object>)).Map(fs => { }); | |
public static Schema Schema(ObjectGraphType<object> query) => new Schema(); | |
} | |
public delegate FieldBuilder<TSource, TReturn> MField<TSource, TReturn>(); | |
public delegate FieldBuilder<TSrc, TProp> FieldConfig<TSrc, TProp>(FieldBuilder<TSrc, TProp> fn); | |
public delegate ObjectGraphType<T> GraphTypeConfig<T>(ObjectGraphType<T> fn); | |
public delegate Try<Lst<FieldType>> FieldsBuilder<TSrc>(Fields<TSrc> fs); | |
public delegate Try<Lst<FieldType>> QueryFieldsBuilder(Fields<object> fs); | |
public struct Fields<TSrc> | |
{ | |
static Try<Type> tpe<TProp>(string name, bool nullable = false) => | |
Try(() => | |
{ | |
try | |
{ | |
return typeof(TProp).GetGraphTypeFromType(nullable); | |
} | |
catch (ArgumentOutOfRangeException exp) | |
{ | |
throw new ArgumentException( | |
$"The GraphQL type for Field: '{name}' on parent type: '{typeof(TSrc).Name}' could not be derived implicitly. \n", | |
exp | |
); | |
} | |
}); | |
public Try<FieldType> Field<TProp>(Expression<Func<TSrc, TProp>> member, bool nullable = false, FieldConfig<TSrc, TProp> config = null) | |
{ | |
var cfg = Optional(config).IfNone(identity); | |
return tpe<TProp>(member.NameOf(), nullable) | |
.Map(type => cfg(FieldBuilder.Create<TSrc, TProp>(type) | |
.Resolve(new ExpressionFieldResolver<TSrc, TProp>(member)) | |
.Name(member.NameOf())) | |
.FieldType | |
); | |
} | |
public Try<FieldType> Field<TGraphType>( | |
string name, | |
string description = null, | |
QueryArguments arguments = null, | |
Func<ResolveFieldContext<TSrc>, object> resolve = null, | |
string deprecationReason = null | |
) => | |
Try(new FieldType | |
{ | |
Name = name, | |
Description = description, | |
DeprecationReason = deprecationReason, | |
Type = typeof(TGraphType), | |
Arguments = arguments, | |
Resolver = resolve != null | |
? new FuncFieldResolver<TSrc, object>(resolve) | |
: null, | |
}); | |
public Try<FieldType> Field<T>(Try<ObjectGraphType<T>> type, | |
string name, | |
string description = null, | |
QueryArguments arguments = null, | |
Func<ResolveFieldContext<T>, object> resolve = null, | |
string deprecationReason = null) => | |
Try(new FieldType | |
{ | |
Name = name, | |
Description = description, | |
DeprecationReason = deprecationReason, | |
Type = typeof(ObjectGraphType<T>), | |
Arguments = arguments, | |
Resolver = resolve != null | |
? new FuncFieldResolver<T, object>(resolve) | |
: null, | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using GraphQL; | |
using GraphQL.Types; | |
using Xunit; | |
using static LanguageExt.Prelude; | |
namespace Api.GraphQL.Tests | |
{ | |
using static Syntax; | |
public class SyntaxTests | |
{ | |
[Fact] | |
public void swapi() | |
{ | |
var schema = Reader<StarWarsData, Schema>(data => | |
{ | |
var droidType = GraphType<Droid>( | |
name: "Droid", | |
fields: fs => | |
from id in fs.Field( | |
config: f => f.Description("The id of the droid."), | |
member: x => x.Id | |
) | |
from name in fs.Field( | |
config: f => f.Description("The name of the droid."), | |
nullable: true, | |
member: x => x.Name | |
) | |
from appears in fs.Field<ListGraphType<EpisodeEnum>>("appearsIn") | |
from function in fs.Field( | |
config: f => f.Description("The primary function of the droid."), | |
nullable: true, | |
member: x => x.PrimaryFunction | |
) | |
select List(id, name, appears) | |
); | |
var humanType = GraphType<Human>( | |
"Human", | |
fields: fs => | |
from id in fs.Field( | |
config: f => f.Description("The id of the human."), | |
member: x => x.Id | |
) | |
from name in fs.Field( | |
config: f => f.Description("The name of the human."), | |
nullable: true, | |
member: x => x.Name | |
) | |
from appears in fs.Field<ListGraphType<EpisodeEnum>>("appearsIn") | |
from function in fs.Field( | |
config: f => f.Description("The home planet of the human."), | |
nullable: true, | |
member: x => x.HomePlanet | |
) | |
select List(id, name, appears, function) | |
); | |
var query = Query( | |
fields: fs => | |
from droid in fs.Field(droidType, "Droid") | |
from human in fs.Field(humanType, "human") | |
select List(droid, human) | |
); | |
return query.Match( | |
Fail: e => throw e, | |
Succ: Schema | |
); | |
}); | |
Schema swapi = schema(new StarWarsData()).Value; | |
} | |
} | |
///================== | |
public abstract class StarWarsCharacter | |
{ | |
public string Id { get; set; } | |
public string Name { get; set; } | |
public string[] Friends { get; set; } | |
public int[] AppearsIn { get; set; } | |
} | |
public class Human : StarWarsCharacter | |
{ | |
public string HomePlanet { get; set; } | |
} | |
public class Droid : StarWarsCharacter | |
{ | |
public string PrimaryFunction { get; set; } | |
} | |
public class StarWarsData | |
{ | |
private readonly List<Human> _humans = new List<Human>(); | |
private readonly List<Droid> _droids = new List<Droid>(); | |
public StarWarsData() | |
{ | |
_humans.Add(new Human | |
{ | |
Id = "1", | |
Name = "Luke", | |
Friends = new[] { "3", "4" }, | |
AppearsIn = new[] { 4, 5, 6 }, | |
HomePlanet = "Tatooine" | |
}); | |
_humans.Add(new Human | |
{ | |
Id = "2", | |
Name = "Vader", | |
AppearsIn = new[] { 4, 5, 6 }, | |
HomePlanet = "Tatooine" | |
}); | |
_droids.Add(new Droid | |
{ | |
Id = "3", | |
Name = "R2-D2", | |
Friends = new[] { "1", "4" }, | |
AppearsIn = new[] { 4, 5, 6 }, | |
PrimaryFunction = "Astromech" | |
}); | |
_droids.Add(new Droid | |
{ | |
Id = "4", | |
Name = "C-3PO", | |
AppearsIn = new[] { 4, 5, 6 }, | |
PrimaryFunction = "Protocol" | |
}); | |
} | |
public IEnumerable<StarWarsCharacter> GetFriends(StarWarsCharacter character) | |
{ | |
if (character == null) | |
{ | |
return null; | |
} | |
var friends = new List<StarWarsCharacter>(); | |
var lookup = character.Friends; | |
if (lookup != null) | |
{ | |
_humans.Where(h => lookup.Contains(h.Id)).Apply(friends.Add); | |
_droids.Where(d => lookup.Contains(d.Id)).Apply(friends.Add); | |
} | |
return friends; | |
} | |
public Task<Human> GetHumanByIdAsync(string id) | |
{ | |
return Task.FromResult(_humans.FirstOrDefault(h => h.Id == id)); | |
} | |
public Task<Droid> GetDroidByIdAsync(string id) | |
{ | |
return Task.FromResult(_droids.FirstOrDefault(h => h.Id == id)); | |
} | |
public Human AddHuman(Human human) | |
{ | |
human.Id = Guid.NewGuid().ToString(); | |
_humans.Add(human); | |
return human; | |
} | |
} | |
public class EpisodeEnum : EnumerationGraphType | |
{ | |
public EpisodeEnum() | |
{ | |
Name = "Episode"; | |
Description = "One of the films in the Star Wars Trilogy."; | |
AddValue("NEWHOPE", "Released in 1977.", 4); | |
AddValue("EMPIRE", "Released in 1980.", 5); | |
AddValue("JEDI", "Released in 1983.", 6); | |
} | |
} | |
public enum Episodes | |
{ | |
NEWHOPE = 4, | |
EMPIRE = 5, | |
JEDI = 6 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment