question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

@GraphQLNonNull ignored on Input objects when @JsonProperty is used

See original GitHub issue

Hello,

recently the frontend team informed me that on some of our inputs there are no required fields. I’ve traced down the bug to Jackson’s @JsonProperty which was added some time ago on constructor parameters (constructor itself was annotated with @JsonCreator).

Here is the MVCE that produces no ! in generated GQL schema (i.e. every field is nullable):

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import graphql.schema.idl.SchemaPrinter;
import io.leangen.graphql.GraphQLSchemaGenerator;
import io.leangen.graphql.annotations.GraphQLArgument;
import io.leangen.graphql.annotations.GraphQLNonNull;
import io.leangen.graphql.annotations.GraphQLQuery;

class Test {
    public static void main(String[] args) {
        GraphQLSchemaGenerator schemaGenerator = new GraphQLSchemaGenerator()
                .withOperationsFromSingleton(new Test());
        System.out.println(new SchemaPrinter().print(schemaGenerator.generate()));
    }
    @GraphQLQuery(name = "RegisterUser")
    public String registerUser(@GraphQLArgument(name = "input") @GraphQLNonNull UserInput input) {
        return input.getFirstName();
    }
}

class UserInput {
    private String firstName;
    private String lastName;

    @JsonCreator
    public UserInput(@JsonProperty("firstName") String firstName,
                     @JsonProperty("lastName") String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @GraphQLNonNull
    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }
}

Schema produced:

#Query root
type Query {
  RegisterUser(input: UserInputInput!): String @_mappedOperation(operation : "__internal__")
}

input UserInputInput @_mappedType(type : "__internal__") {
  firstName: String
  lastName: String
}

Parameter input is okay, but firstName in UserInputInput is nullable (not required), and there is @GraphQLNonNull annotation on getter getFirstName.

I’ve tried following without success:

  • adding setters, and annotation it @GraphQLNonNull UserInput setFirstName (String name)
  • adding setter, but annotating parameter UserInput setFirstName (@GraphQLNonNull String name)

Correct schema is produced when @JsonProperty is removed, e.g. with this constructor:

    public UserInput(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

(It seems that @JsonCreator annotation does not matter)

input UserInputInput @_mappedType(type : "__internal__") {
  firstName: String!
  lastName: String
}

So, I thought that @JsonProperty is considered as “truth source” because it has JsonProperty.required field which is by default false. But, adding @JsonProperty(value = "firstName", required = true) on constructor parameter (or on private field itself, or both) does not change anything.

I’m using graphql-spqr:0.9.9 and graphql-java:11.0.

Is this a bug or is there any workaround for this? Maybe using @GraphQLInputField? (I haven’t used that before).

P.S. Another question, I saw on gitter that someone asked how to get rid of @_mappedOperation and @_mappedType, but answer was vague. Is it possible now, did you find out a way?

Thank you in advance for answer and for this very helpful library!

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:15 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
kaqqaocommented, Jul 8, 2019

@EnilPajic I’ve opened a new issue for JsonProperty#required: https://github.com/leangen/graphql-spqr/issues/287

1reaction
kaqqaocommented, Jul 8, 2019

Hey everyone. Sorry for the silence on my part, I’m away on a business trip these days.

As for the reason the annotation is apparently ignored… When inputs are mapped using Jackson, the logic is as follows:

  • Find the explicitly @GraphQLInputField annotated element
  • Find the primary mutator that Jackson will use to deserialize the values
  • Find the field belonging to the property
  • Merge the types of all 3

So for the case such as:

@JsonCreator
public UserInput(@JsonProperty("firstName") String firstName,
                 @JsonProperty("lastName") String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

@GraphQLNonNull
public String getFirstName() {
    return firstName;
}

There’s no explicitly annotated element, and the constructor is the primary mutator (that’s what will be used to deserialize), so only the field type and the ctor parameter type get merged (getter is not involved), thus the annotation is ignored. (So @Kirintale pretty much nailed it)

To force the logic to take the getter into account, you can mark it with @GraphQLInputField. Or move @GraphQLNonNull to the ctor param or the field.

The reason it works the way it does is to avoid the situation where a bunch of unrelated language elements have to be reasoned about at the same time to know what the mapping will be. The field is always taken into account to allow the usage of things like Lombok. The primary mutator is taken into account because that is what will actually be used by Jackson. The only remaining element is the one the user explicitly decides to involve.

P.S. Another question, I saw on gitter that someone asked how to get rid of @_mappedOperation and @_mappedType, but answer was vague. Is it possible now, did you find out a way?

SPQR uses those to provide some its functionality, so you can’t get rid of them completely without tricky repercussions… If you meant how to avoid printing them, there were changes in the SchemaPrinter that lets you skip directives (includeDirectives), but that’s coming from graphql-java so I have no control over it. If you need a more granular choice if what to print, I’d suggest making a PR on graphql-java.

I’ll read thought the rest of the thread as soon as I get a chance and respond to whatever else there is…

Read more comments on GitHub >

github_iconTop Results From Across the Web

Ignoring new fields on JSON objects using Jackson [duplicate]
Jackson provides an annotation that can be used on class level (JsonIgnoreProperties). Add the following to the top of your class (not to...
Read more >
leangen/graphql-spqr - Gitter
Does anyone here know if its possible in SPQR to use GraphQL directives to do input validation? Basically I'd like to be able...
Read more >
@JsonIgnore, @JsonProperty and @JsonAlias importance in ...
This annotation helps us in ignoring certain properties of a Java class when it is getting serialized to a JSON object. Let's first...
Read more >
Spring Boot — Dynamically ignore fields while serializing ...
But what if our consumer want to ignore few fields dynamically based on their input ? We can use MappingJacksonValue class to achieve...
Read more >
How to ignore a field of JSON object using the Jackson library ...
The Jackson @JsonIgnore annotation can be used to ignore a certain property or field of a Java object. The property can be ignored...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found