[Feature Request] Read schema from SDL file.
As of now the code generation works only with the schema in JSON format. Please provide a feature to load the SDL version too.
Meanwhile the feature is implemented, is there any workaround that I can apply by forking your code?
To achieve this you have to convert SDL schema into the JSON metadata format. SDL schema can be simply read using GraphQL package. But I don't see any point of doing that manually as every GraphQL server does this implicitly. What would be a reason to generate client without having a server?
Hi @Husqvik,
This is for creating the server side models from a GraphQL Schema. If you could direct me the class in GraphQL package which is responsible for converting SDL to JSON it would be a great help.
Thanks
Ok, I never thought about this usage. Here is a very rough idea how to convert SDL into the JSON format. I would almost bet there should be some one line helper to do so but I didn't find anything
void Main()
{
var schema = Schema.For(File.ReadAllText(@"d:\schema.gql"));
schema.Initialize();
var generatorSchema =
new GraphQlSchema
{
QueryType = schema.Query is null ? null : new GraphQlRequestType { Name = schema.Query.Name },
MutationType = schema.Mutation is null ? null : new GraphQlRequestType { Name = schema.Mutation.Name },
SubscriptionType = schema.Subscription is null ? null : new GraphQlRequestType { Name = schema.Subscription.Name },
Directives =
schema.Directives?.Select(d =>
new GraphQlDirective
{
Name = d.Name,
Description = d.Description,
Locations = d.Locations.Select(l => Enum.Parse<GraphQlDirectiveLocation>(l.ToString())).ToList(),
Args = d.Arguments.Select(a => new GraphQlArgument { Name = a.Name, Description = a.Description, Type = GetGraphQlTypeFromClrType(a.Type), DefaultValue = a.DefaultValue }).ToList()
}).ToList(),
Types =
schema.AdditionalTypeInstances.Select(t =>
PopulateSchemaProperties(
new GraphQlType
{
Name = t.Name,
Description = t.Description,
},
t))
.Where(t => t is not null)
.ToList()
}
;
}
private static GraphQlType PopulateSchemaProperties(GraphQlType targetType, IGraphType sourceType)
{
switch (sourceType)
{
case EnumerationGraphType enumType:
targetType.Kind = GraphQlTypeKind.Enum;
targetType.EnumValues = enumType.Values.Select(v => new GraphQlEnumValue { Name = v.Name, Description = v.Description, DeprecationReason = v.DeprecationReason, IsDeprecated = !String.IsNullOrEmpty(v.DeprecationReason) }).ToList();
break;
case ObjectGraphType objectType:
targetType.Kind = GraphQlTypeKind.Object;
targetType.Fields = objectType.Fields.Select(f => new GraphQlField { Name = f.Name, Description = f.Description, DeprecationReason = f.DeprecationReason, IsDeprecated = !String.IsNullOrEmpty(f.DeprecationReason), Type = GetGraphQlType(f.ResolvedType) }).ToList();
//targetType.Interfaces = objectType.Interfaces.Select(i => i.); // TODO
break;
case InputObjectGraphType inputObjectType:
targetType.Kind = GraphQlTypeKind.InputObject;
targetType.InputFields = inputObjectType.Fields.Select(f => new GraphQlArgument { Name = f.Name, Description = f.Description, DefaultValue = f.DefaultValue, Type = GetGraphQlType(f.ResolvedType) }).ToList();
break;
case UnionGraphType unionType:
targetType.Kind = GraphQlTypeKind.Union;
targetType.PossibleTypes = unionType.PossibleTypes.Select(t => new GraphQlFieldType { Name = t.Name, Kind = GraphQlTypeKind.Object }).ToList();
break;
default:
targetType = null;
break;
}
return targetType;
}
private static GraphQlFieldType GetGraphQlTypeFromClrType(Type clrType)
{
if (clrType.IsGenericType && clrType.GetGenericTypeDefinition() == typeof(NonNullGraphType<>))
{
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.NonNull,
OfType = GetGraphQlTypeFromClrType(clrType.GetGenericArguments()[0])
};
}
if (clrType == typeof(BooleanGraphType))
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "Boolean"
};
if (clrType == typeof(FloatGraphType))
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "Float"
};
if (clrType == typeof(IdGraphType))
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "ID"
};
if (clrType == typeof(IntGraphType))
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "Int"
};
if (clrType == typeof(StringGraphType))
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "String"
};
return null;
}
private static GraphQlFieldType GetGraphQlType(IGraphType graphType)
{
switch (graphType)
{
case null: return null;
case NonNullGraphType nonNullType:
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.NonNull,
OfType = GetGraphQlType(nonNullType.ResolvedType)
};
case ListGraphType listType:
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.List,
OfType = GetGraphQlType(listType.ResolvedType)
};
case BooleanGraphType:
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "Boolean"
};
case FloatGraphType:
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "Float"
};
case IdGraphType:
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "ID"
};
case IntGraphType:
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "Int"
};
case StringGraphType:
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Scalar,
Name = "String"
};
case EnumerationGraphType enumType:
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Enum,
Name = enumType.Name
};
case ObjectGraphType objectType:
return
new GraphQlFieldType
{
Kind = GraphQlTypeKind.Object,
Name = objectType.Name
};
case InterfaceGraphType interfaceType:
return null;
case UnionGraphType unionType:
return null;
default:
throw new NotSupportedException($"type \"{graphType.GetType().FullName}\" mapping not supported");
}
return null;
}
Hi @Husqvik Sorry for the late reply. Thanks a lot for dedicating time for writing these codes and much appreciated. It will definitely help me this time. In case if this use case makes sense, is it possible for you to include this in the next release?
Thanks
@kiranchandran I'll need to think about it a bit more. I've never considered the usage to generate server data model so I need to go through C# server implementation features how they could match such generated class. The quick mapping I've tested is definitely not 100% right, so that would need some polishing too.
Hi @Husqvik,
Sure np. Thanks for your help and directions.
@Husqvik Great tool, thank you.
Any updates on this request? I also need to be able to generate the models from the SDL file.
You can use GraphQL.NewtonsoftJson package.
var schema = Schema.For(File.ReadAllText(@"schema.gql"));
schema.Initialize();
var json = await schema.ExecuteAsync(new GraphQLSerializer(true), o => o.Query = IntrospectionQuery.Text);
var generatorSchema = JsonConvert.DeserializeObject<GraphQlResult>(json).Data.Schema;
var csharpCode = new GraphQlGenerator().GenerateFullClientCSharpFile(generatorSchema);
Console.WriteLine(csharpCode);
excellent @Husqvik thank you!