Husqvik / GraphQlClientGenerator

GraphQL C# client generator
MIT License
213 stars 49 forks source link

Nullable input type #87

Closed Mfolmer closed 3 years ago

Mfolmer commented 3 years ago

First of all great work with this! Really like the developer experience using this generator. However, I encountered an issue (it might be me missing something rather than an issue 😄)

Have the following input type defined:

public record ModifyUnitAdministatorInput(
        int Id,
        Optional<string?> Cvr,
        Optional<string?> CompanyName,
        );

and mutation signature:

public async Task<ModifyUnitAdministratorPayload> ModifyUnitAdministator(
            ModifyUnitAdministatorInput input, [Service] ApplicationDbContext dbContext) {...}

Generating the client:

var schema = await GraphQlGenerator.RetrieveSchema(url);
var csharpCode = new GraphQlGenerator().GenerateFullClientCSharpFile(schema, clientName);

Then I get some strange queries when building with nullable values:

string? value = null;
var unitAdminInput = new ModifyUnitAdministatorInput{ Cvr = value };
var mutationBuilder = new MutationQueryBuilder()
                .WithModifyUnitAdministator(
                    new ModifyUnitAdministratorPayloadQueryBuilder()
                        .WithUnitAdministrator(
                            new UnitAdministratorQueryBuilder()
                                .WithCvr()
                            )
                        , unitAdminInput);
"mutation{
    modifyUnitAdministator(input:{
        cvr:\"GraphQLApiClient.QueryBuilderParameter`1[System.String]\"
   })
   {
     unitAdministrator { cvr }
   }
}"

Am I doing something wrong?

Husqvik commented 3 years ago

ModifyUnitAdministatorInput is your own class. How can you even pass it as input parameter? Also the generated query suggest a string value is passed as parameter value not ModifyUnitAdministatorInput The QueryBuilderParameter type is a wrapper needed for handling difference between parameters passed as reference and parameters passed as value. If value is passed as parameter is implicitly converted to this type.

Mfolmer commented 3 years ago

Yes ModifyUnitAdministatorInput is my own class. However, as it is used as input for the mutation the generator will create the same class.

public partial class ModifyUnitAdministatorInput : IGraphQlInputObject
    {
        private InputPropertyInfo _id;
        private InputPropertyInfo _cvr;
        private InputPropertyInfo _companyName;

        #if !GRAPHQL_GENERATOR_DISABLE_NEWTONSOFT_JSON
        [JsonConverter(typeof(QueryBuilderParameterConverter<int?>))]
        #endif
        public QueryBuilderParameter<int?>? Id
        {
            get => (QueryBuilderParameter<int?>?)_id.Value;
            set => _id = new InputPropertyInfo { Name = "id", Value = value };
        }

        #if !GRAPHQL_GENERATOR_DISABLE_NEWTONSOFT_JSON
        [JsonConverter(typeof(QueryBuilderParameterConverter<string?>))]
        #endif
        public QueryBuilderParameter<string?>? Cvr
        {
            get => (QueryBuilderParameter<string?>?)_cvr.Value;
            set => _cvr = new InputPropertyInfo { Name = "cvr", Value = value };
        }

        #if !GRAPHQL_GENERATOR_DISABLE_NEWTONSOFT_JSON
        [JsonConverter(typeof(QueryBuilderParameterConverter<string?>))]
        #endif
        public QueryBuilderParameter<string?>? CompanyName
        {
            get => (QueryBuilderParameter<string?>?)_companyName.Value;
            set => _companyName = new InputPropertyInfo { Name = "companyName", Value = value };
        }

        IEnumerable<InputPropertyInfo> IGraphQlInputObject.GetPropertyValues()
        {
            if (_id.Name != null) yield return _id;
            if (_cvr.Name != null) yield return _cvr;
            if (_companyName.Name != null) yield return _companyName;
        }
    }

Therefore, it can be used as input like this:

public MutationQueryBuilder WithModifyUnitAdministator(ModifyUnitAdministratorPayloadQueryBuilder modifyUnitAdministratorPayloadQueryBuilder, QueryBuilderParameter<ModifyUnitAdministatorInput> input, string? alias = null, SkipDirective? skip = null, IncludeDirective? include = null, StreamDirective? stream = null)
        {
            var args = new List<QueryBuilderArgumentInfo>();
            args.Add(new QueryBuilderArgumentInfo { ArgumentName = "input", ArgumentValue = input} );
            return WithObjectField("modifyUnitAdministator", alias, modifyUnitAdministratorPayloadQueryBuilder, new GraphQlDirective?[] { skip, include, stream }, args);
        }

However, using it with nullable will produce the strange result when building the query. Hope it makes sense. FYI the generator is used with the following config:

CSharpVersion = CSharpVersion.NewestWithNullableReferences
Husqvik commented 3 years ago

Fixed, do you need a new nuget?

Mfolmer commented 3 years ago

That would be awesome yes, thanks for the quick response and fix!

Husqvik commented 3 years ago

https://www.nuget.org/packages/GraphQlClientGenerator/0.9.4