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

Nullable reference types not detected in mutation parameters #204

Closed
breyed opened this issue Aug 12, 2022 · 11 comments
Closed

Nullable reference types not detected in mutation parameters #204

breyed opened this issue Aug 12, 2022 · 11 comments
Assignees

Comments

@breyed
Copy link
Contributor

breyed commented Aug 12, 2022

If a mutation uses separate parameters (vs. a parameters object), any of the parameters that are of a reference type and are marked as nullable still show up as non-nullable in the GraphQL.

For example, in a mutation:

void Update(InputType a, InputType? b) { ... }

Should output the GraphQL:

update(a: InputType!, b: InputType)

Currently, it outputs:

update(a: InputType!, b: InputType!)

EntityGraphQL should inspect the nullability:

var nullabilityContext = new NullabilityInfoContext();
var nullabilityInfo = nullabilityContext.Create(parameterInfo);
bool isNullable = nullabilityInfo.WriteState is NullabilityState.Nullable;
bzbetty pushed a commit to bzbetty/EntityGraphQL that referenced this issue Aug 13, 2022
@bzbetty
Copy link
Contributor

bzbetty commented Aug 13, 2022

Pretty sure this is an issue in ArgType.FromParameter.

It's passing through prop.Member to MakeArgType which is the parent method instead of the parameter itself.

Probably need to switch from using MemberInfo to ICustomAttributeProvider to achieve this. I thought I did that in one of the validation branches, but maybe it never made it.

@lukemurray lukemurray self-assigned this Aug 14, 2022
@lukemurray
Copy link
Collaborator

Thanks for the test. I'm doing a fix for this now. Also found an issue where autoAddInputTypes was not being used when not using MutationArgumentsAttribute.

@Eli-Black-Work
Copy link

@lukemurray Would this also fix an issue that I'm running into where nullable collections seem to not be recognized as nullable?

#nullable enable
public class User {
   public ICollection<Role>? Roles { get; set; } = null;
}

public static void ConfigureSchema(SchemaProvider<...> schema)
   schema.AddType<User>("user", null).AddAllFields();
}

query:

{"Query":" { users { roles { id } }"}

error:

{"errors":[{"message":"Field 'users' - Value cannot be null. (Parameter 'source')"}]}

It looks like Entity GraphQL is expecting the ICollection<Role>? Roles to be non-null, although it's marked as nullable.

If I should open a separate issue for this, just let me know, but I thought it might be related 🙂

@lukemurray
Copy link
Collaborator

This is actually hitting an error in the built expression. Likely users.Select(u => u.Roles.Select(r => new { id = r.Id })) where either users or one of there user .Roles is null.

@Eli-Black-Work
Copy link

Eli-Black-Work commented Aug 23, 2022

@lukemurray Thanks 🙂 Agreed that it's hitting an error in the built expression. I think the expression in this instance is built by EntityGraphQL, though.

Here's a more full snippet:

#nullable enable
public class UserDbContext : DbContext {
   public DbSet<UserWrapper> UserWrappers { get; set; }
}

public class UserWrapper {
   public string Json { get; set; }
}

public class User {
    public ICollection<Role>? Roles { get; set; } = null;
}

services.AddGraphQLSchema<UserDbContext>(options =>
{
    options.ConfigureSchema = ConfigureSchema;
});

public static void ConfigureSchema(SchemaProvider<UserDbContext> demoSchema)
{
    demoSchema.AddType<User>("user", null).AddAllFields();

    // Add custom root fields
    demoSchema.UpdateQuery(queryType =>
    {
        queryType.ReplaceField(
            "users",
            new
            {
                ...
            },
            (db, args) => db.UserWrappers.Select(userWrapper => JsonConvert.DeserializeObject<User>(userWrapper.Json)).ToList(),
            "List of users"
        );
    });
}

@lukemurray
Copy link
Collaborator

lukemurray commented Aug 23, 2022

Yes, I meant an expression that EntityGraphQL built.

Like any code users.Select(u => u.Roles.Select(r => new { id = r.Id })) will throw if something is null in your data.

If executing against EF we can get away with it as the expression is translated by EF to SQL so the null property is not actually hit.

If the data is not EF data context and there is a null EntityGraphQL tries to handle this by doing null checks. So you've likely hit an edge case where that null check is not working correctly.

E.g. EntityGraphQL builds something closer to this

users == null ? null : users.Select(u => u.Roles == null ? null : u.Roles.Select(r => new { id = r.Id }))

And typing that out I realise it is likely the inner check failing.

@lukemurray
Copy link
Collaborator

Can I ask what UserWrappers is? Is it a table via EF or something outside of the DB?

@Eli-Black-Work
Copy link

Eli-Black-Work commented Aug 23, 2022

@lukemurray UserWrappers is defined at the top of the example in my previous post 🙂

(Sorry, I pretty heavily edited my previous post after I posted it, so if you got an email with my reply, it probably has the old example in it)

public class UserDbContext : DbContext {
   public DbSet<UserWrapper> UserWrappers { get; set; }
}

public class UserWrapper {
   public string Json { get; set; }
}

@Eli-Black-Work
Copy link

I'm off of work for today. Will check in again tomorrow, God willing 🙂

@lukemurray
Copy link
Collaborator

Whoops I did miss that. Thanks

@Eli-Black-Work
Copy link

@lukemurray It looks like this bug probably isn't related to this issue, so I've split it off into a separate bug report: #221

@breyed Sorry for the noise! 🙂

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

4 participants