diff --git a/src/main/java/graphql/annotations/annotationTypes/GraphQLConstructor.java b/src/main/java/graphql/annotations/annotationTypes/GraphQLConstructor.java new file mode 100644 index 00000000..fa854007 --- /dev/null +++ b/src/main/java/graphql/annotations/annotationTypes/GraphQLConstructor.java @@ -0,0 +1,28 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + */ +package graphql.annotations.annotationTypes; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/*** + * When using object as argument of query or mutation, a HashMap Constructor is used to initialize + * the object fields. In order to identify the correct constructor this annotation must be used, otherwise + * the object fields will be null + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.CONSTRUCTOR) +public @interface GraphQLConstructor { +} diff --git a/src/main/java/graphql/annotations/dataFetchers/MethodDataFetcher.java b/src/main/java/graphql/annotations/dataFetchers/MethodDataFetcher.java index a5fc6ab2..7ba39f7d 100644 --- a/src/main/java/graphql/annotations/dataFetchers/MethodDataFetcher.java +++ b/src/main/java/graphql/annotations/dataFetchers/MethodDataFetcher.java @@ -1,6 +1,4 @@ /** - * Copyright 2016 Yurii Rashkovskii - * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -15,6 +13,7 @@ package graphql.annotations.dataFetchers; import graphql.annotations.annotationTypes.GraphQLBatched; +import graphql.annotations.annotationTypes.GraphQLConstructor; import graphql.annotations.annotationTypes.GraphQLInvokeDetached; import graphql.annotations.annotationTypes.GraphQLName; import graphql.annotations.processor.ProcessingElementsContainer; @@ -68,10 +67,9 @@ public MethodDataFetcher(Method method, TypeFunction typeFunction, ProcessingEle public T get(DataFetchingEnvironment environment) { try { T obj; - if (Modifier.isStatic(method.getModifiers())){ + if (Modifier.isStatic(method.getModifiers())) { return (T) method.invoke(null, invocationArgs(environment, container)); - } - else if (method.isAnnotationPresent(GraphQLBatched.class) || method.isAnnotationPresent(GraphQLInvokeDetached.class)) { + } else if (method.isAnnotationPresent(GraphQLBatched.class) || method.isAnnotationPresent(GraphQLInvokeDetached.class)) { obj = newInstance((Class) method.getDeclaringClass()); } else if (!method.getDeclaringClass().isInstance(environment.getSource())) { obj = newInstance((Class) method.getDeclaringClass(), environment.getSource()); @@ -130,21 +128,19 @@ private Object buildArg(Type p, GraphQLType graphQLType, Object arg) { } if (p instanceof Class && graphQLType instanceof GraphQLInputObjectType) { Constructor constructors[] = ((Class) p).getConstructors(); - for (Constructor constructor : constructors) { - Parameter[] parameters = constructor.getParameters(); - if (parameters.length == 1 && parameters[0].getType().isAssignableFrom(arg.getClass())) { - return constructNewInstance(constructor, arg); - } else { - List objects = new ArrayList<>(); - Map map = (Map) arg; - for (Parameter parameter : parameters) { - String name = toGraphqlName(parameter.getAnnotation(GraphQLName.class) != null ? parameter.getAnnotation(GraphQLName.class).value() : parameter.getName()); - objects.add(buildArg(parameter.getParameterizedType(), ((GraphQLInputObjectType) graphQLType).getField(name).getType(), map.get(name))); - } - return constructNewInstance(constructor, objects.toArray(new Object[objects.size()])); + Constructor constructor = getBuildArgConstructor(constructors); + Parameter[] parameters = constructor.getParameters(); + if (parameters.length == 1 && parameters[0].getType().isAssignableFrom(arg.getClass())) { + return constructNewInstance(constructor, arg); + } else { + List objects = new ArrayList<>(); + Map map = (Map) arg; + for (Parameter parameter : parameters) { + String name = toGraphqlName(parameter.getAnnotation(GraphQLName.class) != null ? parameter.getAnnotation(GraphQLName.class).value() : parameter.getName()); + objects.add(buildArg(parameter.getParameterizedType(), ((GraphQLInputObjectType) graphQLType).getField(name).getType(), map.get(name))); } + return constructNewInstance(constructor, objects.toArray(new Object[objects.size()])); } - return null; } else if (p instanceof ParameterizedType && graphQLType instanceof GraphQLList) { List list = new ArrayList<>(); Type subType = ((ParameterizedType) p).getActualTypeArguments()[0]; @@ -160,6 +156,24 @@ private Object buildArg(Type p, GraphQLType graphQLType, Object arg) { } } + + /*** + * return the constructor to call in order to build the object + * @param constructors Object constructors + * @return the annotated constructor if present else return the first constructor + */ + private Constructor getBuildArgConstructor(Constructor constructors[]) { + if (constructors != null) { + for (Constructor constructor : constructors) { + if (constructor.isAnnotationPresent(GraphQLConstructor.class)) { + return constructor; + } + } + return constructors[0]; + } + return null; + } + private Object getGraphQLFieldValue(Object source, String fieldName) throws IllegalAccessException, NoSuchFieldException, InvocationTargetException { Object methodValue = getValueFromMethod(source, fieldName); if (methodValue != null) return methodValue;