From 16173653e4f88ce7a9bd47ba49b996d9c6491cbf Mon Sep 17 00:00:00 2001 From: Gregor Eichelberger Date: Mon, 13 Jul 2026 21:29:28 +0200 Subject: [PATCH] Add extend input type support --- .../retrievers/GraphQLExtensionsHandler.java | 36 +++++++++++++++++++ .../retrievers/GraphQLTypeRetriever.java | 2 +- .../typeBuilders/InputObjectBuilder.java | 8 ++++- .../annotations/GraphQLExtensionsTest.java | 32 +++++++++++++++++ .../annotations/GraphQLObjectTest.java | 16 +++++++-- 5 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/main/java/graphql/annotations/processor/retrievers/GraphQLExtensionsHandler.java b/src/main/java/graphql/annotations/processor/retrievers/GraphQLExtensionsHandler.java index a2f49c65..44389ab0 100644 --- a/src/main/java/graphql/annotations/processor/retrievers/GraphQLExtensionsHandler.java +++ b/src/main/java/graphql/annotations/processor/retrievers/GraphQLExtensionsHandler.java @@ -20,6 +20,7 @@ import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; import graphql.annotations.processor.searchAlgorithms.SearchAlgorithm; import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLInputObjectField; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferencePolicy; @@ -77,6 +78,41 @@ private void addExtensionField(GraphQLFieldDefinition gqlField, List getExtensionInputFields(Class object, List definedFields, ProcessingElementsContainer container) throws CannotCastMemberException { + List fields = new ArrayList<>(); + String typeName = container.getInputPrefix() + graphQLObjectInfoRetriever.getTypeName(object) + container.getInputSuffix(); + if (container.getExtensionsTypeRegistry().containsKey(object)) { + for (Class aClass : container.getExtensionsTypeRegistry().get(object)) { + for (Method method : graphQLObjectInfoRetriever.getOrderedMethods(aClass)) { + if (method.isBridge() || method.isSynthetic()) { + continue; + } + if (methodSearchAlgorithm.isFound(method)) { + addExtensionInputField(fieldRetriever.getInputField(method, container, typeName), fields, definedFields); + } + } + for (Field field : getAllFields(aClass).values()) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + if (fieldSearchAlgorithm.isFound(field)) { + addExtensionInputField(fieldRetriever.getInputField(field, container, typeName), fields, definedFields); + } + } + } + } + return fields; + } + + private void addExtensionInputField(GraphQLInputObjectField gqlField, List fields, List definedFields) { + if (!definedFields.contains(gqlField.getName())) { + definedFields.add(gqlField.getName()); + fields.add(gqlField); + } else { + throw new GraphQLAnnotationsException("Duplicate field found in extension : " + gqlField.getName(), null); + } + } + public void registerTypeExtension(Class objectClass, ProcessingElementsContainer container) { GraphQLTypeExtension typeExtension = objectClass.getAnnotation(GraphQLTypeExtension.class); diff --git a/src/main/java/graphql/annotations/processor/retrievers/GraphQLTypeRetriever.java b/src/main/java/graphql/annotations/processor/retrievers/GraphQLTypeRetriever.java index 3bc15637..9dc87842 100644 --- a/src/main/java/graphql/annotations/processor/retrievers/GraphQLTypeRetriever.java +++ b/src/main/java/graphql/annotations/processor/retrievers/GraphQLTypeRetriever.java @@ -77,7 +77,7 @@ public GraphQLType getGraphQLType(Class object, ProcessingElementsContainer c } else { if (isInput) { type = new InputObjectBuilder(graphQLObjectInfoRetriever, fieldSearchAlgorithm, methodSearchAlgorithm, - graphQLFieldRetriever).getInputObjectBuilder(object, container).build(); + graphQLFieldRetriever, extensionsHandler).getInputObjectBuilder(object, container).build(); } else { type = new OutputObjectBuilder(graphQLObjectInfoRetriever, fieldSearchAlgorithm, methodSearchAlgorithm, graphQLFieldRetriever, graphQLInterfaceRetriever, extensionsHandler).getOutputObjectBuilder(object, container).build(); diff --git a/src/main/java/graphql/annotations/processor/typeBuilders/InputObjectBuilder.java b/src/main/java/graphql/annotations/processor/typeBuilders/InputObjectBuilder.java index 553922d2..fd9db9cc 100644 --- a/src/main/java/graphql/annotations/processor/typeBuilders/InputObjectBuilder.java +++ b/src/main/java/graphql/annotations/processor/typeBuilders/InputObjectBuilder.java @@ -17,6 +17,7 @@ import graphql.annotations.annotationTypes.GraphQLDescription; import graphql.annotations.processor.ProcessingElementsContainer; import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; +import graphql.annotations.processor.retrievers.GraphQLExtensionsHandler; import graphql.annotations.processor.retrievers.GraphQLFieldRetriever; import graphql.annotations.processor.retrievers.GraphQLObjectInfoRetriever; import graphql.annotations.processor.searchAlgorithms.SearchAlgorithm; @@ -36,12 +37,14 @@ public class InputObjectBuilder { private SearchAlgorithm fieldSearchAlgorithm; private SearchAlgorithm methodSearchAlgorithm; private GraphQLFieldRetriever graphQLFieldRetriever; + private GraphQLExtensionsHandler extensionsHandler; - public InputObjectBuilder(GraphQLObjectInfoRetriever graphQLObjectInfoRetriever, SearchAlgorithm fieldSearchAlgorithm, SearchAlgorithm methodSearchAlgorithm, GraphQLFieldRetriever graphQLFieldRetriever) { + public InputObjectBuilder(GraphQLObjectInfoRetriever graphQLObjectInfoRetriever, SearchAlgorithm fieldSearchAlgorithm, SearchAlgorithm methodSearchAlgorithm, GraphQLFieldRetriever graphQLFieldRetriever, GraphQLExtensionsHandler extensionsHandler) { this.graphQLObjectInfoRetriever = graphQLObjectInfoRetriever; this.methodSearchAlgorithm = methodSearchAlgorithm; this.fieldSearchAlgorithm = fieldSearchAlgorithm; this.graphQLFieldRetriever = graphQLFieldRetriever; + this.extensionsHandler = extensionsHandler; } /** @@ -85,6 +88,9 @@ public GraphQLInputObjectType.Builder getInputObjectBuilder(Class object, Pro builder.field(gqlField); } } + + builder.fields(extensionsHandler.getExtensionInputFields(object, definedFields, container)); + return builder; } diff --git a/src/test/java/graphql/annotations/GraphQLExtensionsTest.java b/src/test/java/graphql/annotations/GraphQLExtensionsTest.java index 4d9b0136..db2bd3ec 100644 --- a/src/test/java/graphql/annotations/GraphQLExtensionsTest.java +++ b/src/test/java/graphql/annotations/GraphQLExtensionsTest.java @@ -192,4 +192,36 @@ public void testDuplicateField() { assertTrue(e.getMessage().startsWith("Duplicate field")); } + public static class TestInput { + @GraphQLField + public String field; + } + + @GraphQLTypeExtension(GraphQLExtensionsTest.TestInput.class) + public static class TestInputExtension { + @GraphQLField + public String field2; + } + + public static class QueryWithInput { + @GraphQLField + public String echo(TestInput input) { + return input.field; + } + } + + @Test + public void inputFields() { + GraphQLSchema schema = newAnnotationsSchema().query(QueryWithInput.class).typeExtension(TestInputExtension.class).build(); + GraphQLInputObjectType object = (GraphQLInputObjectType) schema.getType("InputTestInput"); + + List fields = object.getFields(); + assertEquals(fields.size(), 2); + + fields = ImmutableList.sortedCopyOf(Comparator.comparing(GraphQLInputObjectField::getName), fields); + assertEquals(fields.get(0).getName(), "field"); + assertEquals(fields.get(1).getName(), "field2"); + assertEquals(fields.get(1).getType(), GraphQLString); + } + } diff --git a/src/test/java/graphql/annotations/GraphQLObjectTest.java b/src/test/java/graphql/annotations/GraphQLObjectTest.java index 0f6f84de..8fa5359d 100644 --- a/src/test/java/graphql/annotations/GraphQLObjectTest.java +++ b/src/test/java/graphql/annotations/GraphQLObjectTest.java @@ -21,6 +21,7 @@ import graphql.annotations.annotationTypes.GraphQLNonNull; import graphql.annotations.processor.GraphQLAnnotations; import graphql.annotations.processor.ProcessingElementsContainer; +import graphql.annotations.processor.retrievers.GraphQLExtensionsHandler; import graphql.annotations.processor.retrievers.GraphQLFieldRetriever; import graphql.annotations.processor.retrievers.GraphQLObjectInfoRetriever; import graphql.annotations.processor.searchAlgorithms.BreadthFirstSearch; @@ -59,6 +60,15 @@ public void init() { this.graphQLAnnotations = new GraphQLAnnotations(); } + private static GraphQLExtensionsHandler newExtensionsHandler(GraphQLObjectInfoRetriever graphQLObjectInfoRetriever) { + GraphQLExtensionsHandler extensionsHandler = new GraphQLExtensionsHandler(); + extensionsHandler.setGraphQLObjectInfoRetriever(graphQLObjectInfoRetriever); + extensionsHandler.setFieldSearchAlgorithm(new ParentalSearch(graphQLObjectInfoRetriever)); + extensionsHandler.setMethodSearchAlgorithm(new BreadthFirstSearch(graphQLObjectInfoRetriever)); + extensionsHandler.setFieldRetriever(new GraphQLFieldRetriever()); + return extensionsHandler; + } + public static class DefaultAValue implements Supplier { @Override @@ -723,7 +733,7 @@ public void complexInputObjectArgument() { public void inputObject() { GraphQLObjectInfoRetriever graphQLObjectInfoRetriever = new GraphQLObjectInfoRetriever(); GraphQLInputObjectType type = new InputObjectBuilder(graphQLObjectInfoRetriever, new ParentalSearch(graphQLObjectInfoRetriever), - new BreadthFirstSearch(graphQLObjectInfoRetriever), new GraphQLFieldRetriever()). + new BreadthFirstSearch(graphQLObjectInfoRetriever), new GraphQLFieldRetriever(), newExtensionsHandler(graphQLObjectInfoRetriever)). getInputObjectBuilder(InputObject.class, this.graphQLAnnotations.getContainer()).build(); assertEquals(type.getName(), DEFAULT_INPUT_PREFIX + InputObject.class.getSimpleName(), "Type name prefix did not match expected value"); @@ -737,7 +747,7 @@ public void inputObjectCustomPrefixes() { container.setInputPrefix(""); container.setInputSuffix("Input"); GraphQLInputObjectType type = new InputObjectBuilder(graphQLObjectInfoRetriever, new ParentalSearch(graphQLObjectInfoRetriever), - new BreadthFirstSearch(graphQLObjectInfoRetriever), new GraphQLFieldRetriever()). + new BreadthFirstSearch(graphQLObjectInfoRetriever), new GraphQLFieldRetriever(), newExtensionsHandler(graphQLObjectInfoRetriever)). getInputObjectBuilder(InputObject.class, this.graphQLAnnotations.getContainer()).build(); assertEquals(type.getName(), "" + InputObject.class.getSimpleName() + "Input", "Type name prefix did not match expected value"); @@ -808,7 +818,7 @@ public void optionalInput() { GraphQLObjectType object = this.graphQLAnnotations.object(OptionalTest.class); GraphQLObjectInfoRetriever graphQLObjectInfoRetriever = new GraphQLObjectInfoRetriever(); GraphQLInputObjectType inputObject = new InputObjectBuilder(graphQLObjectInfoRetriever, new ParentalSearch(graphQLObjectInfoRetriever), - new BreadthFirstSearch(graphQLObjectInfoRetriever), new GraphQLFieldRetriever()). + new BreadthFirstSearch(graphQLObjectInfoRetriever), new GraphQLFieldRetriever(), newExtensionsHandler(graphQLObjectInfoRetriever)). getInputObjectBuilder(OptionalTest.class, this.graphQLAnnotations.getContainer()).build(); GraphQLObjectType mutation = GraphQLObjectType.newObject().name("mut").field(newFieldDefinition().name("test").type(object).