Reputation: 2711
I am working on a data migration project - API to API.
The destination API is graphQL, we have a number of objects to push into the destination and the shapes vary so I am looking for some advice on how best to dynamically build mutations/queries specifically in c#.
Currently we are just using templates and using find/replace routines to inject values. While this approach does work as the shapes of the data vary this becomes evermore complex and inelegant.
I am looking for any advice/pointers from anyone who have may have had a similar scenario or knows of any libraries I should look at.
Upvotes: 5
Views: 7772
Reputation: 126
I developed simple library for translating linq-expressions to graphql-queries. It may be useful for back-to-back grapqhl-queries.
https://github.com/RDavydenko/SmartGraphQLClient
Install-Package SmartGraphQLClient
services.AddSmartGraphQLClient();
using SmartGraphQLClient;
GraphQLHttpClient client = ... // from DI
var users = await client.Query<UserModel>("users")
.Include(x => x.Roles)
.ThenInclude(x => x.Users)
.Where(x => x.UserName.StartsWith("A") || x.Roles.Any(r => r.Code == RoleCode.ADMINISTRATOR))
.Select(x => new
{
x.Id,
Name = x.UserName,
x.Roles,
IsAdministrator = x.Roles.Any(r => r.Code == RoleCode.ADMINISTRATOR)
})
.Skip(5)
.Take(10)
.Argument("secretKey", "1234")
.ToListAsync();
Your request will be translated to graphql-query:
{
users (
where: {
or: [
{ userName: { startsWith: "A" } }
{ roles: { some: { code: { eq: ADMINISTRATOR } } } }
]
}
skip: 5
take: 10
secretKey: "1234"
) {
id
userName
roles {
code
name
description
id
users {
userName
age
id
}
}
}
}
Upvotes: 3
Reputation: 638
Update - 13/02/2018
I have since updated this monstrosity to cater for nested sub selections and GraphQl enums so if anyone is interested here it is in GitHub
Orignal answer
I've got the same requirement. Couldn't find anything out there so I've come up with this very inelegant solution. It works for my scenarios so I'll post it here for anyone else looking for a solution.
public static class GraphQlObjectParser
{
public static string Parse(string queryType, string queryName, string[] subSelection, object @object = null, string objectTypeName = null)
{
var query = queryType + "{" + queryName;
if (@object != null)
{
query += "(";
if (objectTypeName != null)
{
query += objectTypeName + ":" + "{";
}
var queryData = string.Empty;
foreach (var propertyInfo in @object.GetType().GetProperties())
{
var value = propertyInfo.GetValue(@object);
if (value != null)
{
var type = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
var valueQuotes = type == typeof(string) ? "\"" : string.Empty;
var queryPart = char.ToLowerInvariant(propertyInfo.Name[0]) + propertyInfo.Name.Substring(1) + ":" + valueQuotes + value + valueQuotes;
queryData += queryData.Length > 0 ? "," + queryPart : queryPart;
}
}
query += (objectTypeName != null ? queryData + "}" : queryData) + ")";
}
if (subSelection.Length > 0)
{
query += subSelection.Aggregate("{", (current, s) => current + (current.Length > 1 ? "," + s : s)) + "}";
}
query += "}";
return query;
}
}
This works for both queries and mutations. Examples of usage are:
var query = GraphQlObjectParser.Parse("query", "users", new[] { "id", "name" });
will give you
query{users{id,name}}
or
var query = GraphQlObjectParser.Parse("query", "user", new[] { "id", "name" }, new User { Id = 1 });
will give you
query{user(id:1){id,name}}
or
var query = GraphQlObjectParser.Parse("mutation", "user", new[] { "id", "name" }, new User { Id = 1, Name = "John" }, "data");
will give you
mutation{user(data:{id:1,name:"John"}){id,name}}
It'll work with enums which is why I needed this solution in the first place. You can pass in a sub selection without the object or the object without an object type name. I've tried to cover as many bases as possible although I've not yet catered for sub selection of a sub selection. I'll update here if/when I code this one.
Hope it helps someone.
Upvotes: 6