Skip to content

Instantly share code, notes, and snippets.

@trbngr
Last active September 14, 2017 06:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trbngr/dd6721bc03c8d08750076b309392baf4 to your computer and use it in GitHub Desktop.
Save trbngr/dd6721bc03c8d08750076b309392baf4 to your computer and use it in GitHub Desktop.
experiments in graphql-dotnet syntax
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,
});
}
}
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