Repository navigation
Register input field types that have no Java property - #842
Open
oryan-block wants to merge 2 commits into
Open
oryan-block wants to merge 2 commits into
oryan-block wants to merge 2 commits into
Conversation
When the scanner couldn't find a Java property for an input field, e.g. because the input is bound to a Map or ObjectNode, the getter name doesn't match the schema field (getFoo_Back for foo_back) or the field is private, it only logged "Cannot find definition for field ..." and never registered the field's type. Nested input objects and custom scalars only reachable that way were left out of the scan, and the parser then failed with "Expected type 'X' to be a GraphQLInputType, but it wasn't!", which pointed users at the wrong problem. The scanner now still registers what such a field needs. A nested input object is registered without a class and its fields are scanned the same way; a class found for it later is set and scanned as usual, so it doesn't clash. A custom scalar is registered like any other. An enum can't be built without its Java class, so if none is found for it anywhere, the scan fails with a message naming the enum, the input type, the field and the class, and how to fix it. The garbled warning is replaced by one that's only logged when an input class (not a Map or JsonNode) lacks a matching property. Input fields of a Map or JsonNode are no longer matched against the methods and fields it inherits from the JDK or Jackson, so fields named like values, elements or fields work with ObjectNode. Getters declared by a Map subclass are still used. A class taken from the dictionary for an input field is matched against the field's unwrapped type, so a list field whose element type is in the dictionary no longer fails with "Java class is not a List". A misspelled property on an input class whose nested input type is only reachable through it used to fail the build with the misleading error above. It now builds with the warning and, like any other misspelled property, fails when a client sends the field, or the value is dropped if the ObjectMapper ignores unknown properties. There's still no build-time check that input class properties match the schema (#422). The parser's "Expected type ... to be a GraphQLInputType" error is replaced by one that tells apart a type that isn't defined, a type of the wrong kind (an output type used as an input or vice versa) and a type that was defined but never reached while scanning, with a hint for reaching it that depends on the kind of type. It names the field or argument using the type and its source location. Schemas passed through schemaString() are still parsed as one document without a source name, so their line numbers count from the start of all strings (#392). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
This was referenced Oct 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #479
Fixes #355
Fixes #224
Checklist
Description
If the scanner can't find a Java property for an input field, it never registers that field's type. So a nested input type or custom scalar that's only reachable through that field is never built, and the schema build fails with this:
There are a few ways to end up there. The input can be a
MaporObjectNoderesolver parameter, which has no properties at all. The getter can fail to match the schema field, likegetIncludedAgreement_Back()forIncludedAgreement_backin #479. Or the field can be private and mapped with@JsonProperty. #355 is the same problem with a custom scalar:foo: Fooon aCreateHumanInputbound to aMap. The workaround people use is a dummy query that takes every nested input as an argument, just so the scanner reaches them. And the message sends you looking for an output type used as an input, which is the complaint in #224.The
InputObjectTypeDefinitionbranch ofSchemaClassScanner.handleNewTypejust logged "Cannot find definition for field ..." whenfindInputValueTypecame back empty, and moved on.SchemaParser.determineInputTypeonly builds types the scanner observed, so it threw.Now the input branch lives in
handleInputObjectFields, which takes a nullable class so it can recurse. When a field has no property,handleInputValueWithoutJavaTypelooks at the field's type. A nested input object is registered without a class and its fields are scanned the same way. That only happens the first time it's seen, so recursive inputs are fine. If a class for it turns up later through another field,setTypeIfMissingfills it in and it's scanned as usual, without a "Two different classes" error. The triage prototype usedjava.util.Mapas a stand-in class instead, but input types never go into the class BiMap anyway, andMap's own methods could then get matched as properties. A custom scalar goes throughhandleFoundScalarType. An enum can't be built without its class. So if none is found anywhere by the end of the scan, it fails with aSchemaClassScannerErrorthat names the enum, the input type, the field and the class, and suggests a public getter or field, or a dictionary entry. Undefined types and output types are left for the parser to report.Two smaller scanner fixes came out of review. Fields of a
MaporJsonNodeinput are no longer matched against methods declared by the JDK or Jackson, so anObjectNodeinput with a field namedvalues,elementsorfieldsworks. Getters that aMapsubclass declares itself are still used. Also, a dictionary class for an input field is now matched against the unwrapped type. So a list field whose element class comes from the dictionary no longer fails with "Java class is not a List".On the parser side, both throws in
determineType/determineInputTypenow go throughunexpectedTypeError, which tells three cases apart: the type isn't defined in the schema, it's the wrong kind (an object, interface or union used as an input, or an input object used as an output), or it's defined but the scanner never reached it. For that last case the hint depends on the kind of type. For example,includeUnusedTypesdoesn't help for input types or scalars. The message names the field or argument that uses the type, plus the line, column and file name when known:To find that usage I search the definitions for the same
TypeNameinstance, and only on the error path. Passing the context down throughdetermineType/determineInputTypewould've been the other option, but the fix for #321 rewrites their List/NonNull branches in a separate PR and I didn't want the two to conflict.The new
NestedInputTypesTestruns real queries, not just the schema build. It covers a recursiveRequestFilterthrough aMapparameter, twoObjectNodeinputs sharing a nested input, the #479 case (mismatched getter and a private@JsonPropertyfield, two levels deep), a class found after the input was first reached without one, the dictionary list field,ObjectNodefield names andMapsubclass getters.SchemaClassScannerTestruns the #355 schema with a mutation.SchemaParserTestchecks the exact message for each case, plus the enum errors. The main scenarios (Map, ObjectNode, mismatched getter, the #355 scalar) fail on master with the old error, and the message tests fail on the old text. Two of the tests are guards that already passed: a class found later, and an enum class found elsewhere.#422 asks for a build-time check that input class property names match the schema. I left it out.
findInputValueTypeInTypeonly knows aboutname/getNamemethods and public fields, so a strict mode would flag setter-only classes,isXgetters,@JsonCreatorclasses, naming strategies and@JsonAnySetter. Doing it right means asking Jackson, but theObjectMapperis per field (PerFieldObjectMapperProvider) while an input type can be shared by several fields. So #422 stays open. The second complaint in #224 (syntax error line numbers when files are concatenated) isn't fixed here either.schemaString()is still parsed as one document without a source name. That's #392. For #465, mapping two dictionary names toObjectNodestill throws "value already present". That's how the BiMap works, but nested inputs underObjectNodeno longer need a dictionary entry, so I'll close that one by hand with a comment. One more limit: a custom scalar inside aMapinput reaches the resolver as whatever Jackson converts it to, not as the scalar's Java object. That's #436.The closed duplicates #448 (of #355) and #266, #261 and #326 (of #479) build and run with this change.
Behaviour change: the old "Expected type ... to be a GraphQLInputType" text is gone, so anything matching on it won't match anymore. An enum that's only reachable through a field without a property now fails during scanning with a
SchemaClassScannerErrorinstead of in the parser. The "Cannot find definition for field" WARN is replaced by "Cannot find a public getter or field for field ...". The new WARN is only logged for real input classes, soMapandJsonNodeinputs don't log it anymore. Input objects reached this way no longer show up ingetUnusedDefinitions(). Also, a misspelled property on an input class whose nested input type is only reachable through it used to fail the build, although with the misleading error. Now it builds with the WARN and fails when a client sends that field, or the value is dropped if theObjectMapperignores unknown properties. Misspelled scalar properties already behaved that way. Probably worth a note in the release notes.🤖 Generated with Claude Code