Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Read schema from SDL file. #118

Open
kiranchandran opened this issue Jan 6, 2023 · 6 comments
Open

[Feature Request] Read schema from SDL file. #118

kiranchandran opened this issue Jan 6, 2023 · 6 comments

Comments

@kiranchandran
Copy link

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?

@Husqvik
Copy link
Owner

Husqvik commented Jan 6, 2023

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?

@kiranchandran
Copy link
Author

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

@Husqvik
Copy link
Owner

Husqvik commented Jan 6, 2023

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;
}

@kiranchandran
Copy link
Author

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

@Husqvik
Copy link
Owner

Husqvik commented Jan 12, 2023

@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.

@kiranchandran
Copy link
Author

kiranchandran commented Jan 12, 2023

Hi @Husqvik,

Sure np. Thanks for your help and directions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants