Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ gradle.projectsEvaluated {
dependencies {
compile 'javax.validation:validation-api:1.1.0.Final'
compile 'com.graphql-java:graphql-java:15.0'
implementation "jakarta.xml.bind:jakarta.xml.bind-api:2.3.2"
implementation "org.glassfish.jaxb:jaxb-runtime:2.3.2"


// OSGi
compileOnly 'org.osgi:org.osgi.core:6.0.0'
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.jvmargs=-Dfile.encoding=UTF-8

version = 8.3
version = 8.3-nosto1
5 changes: 3 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#Mon Nov 30 23:20:00 EET 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 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.dataFetchers;

import graphql.schema.GraphQLSchemaElement;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeVisitor;
import graphql.util.TraversalControl;
import graphql.util.TraverserContext;

public class GraphQLUndefined implements GraphQLType {
@Override
public TraversalControl accept(TraverserContext<GraphQLSchemaElement> context, GraphQLTypeVisitor visitor) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,38 @@
*/
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;
import graphql.annotations.processor.typeFunctions.TypeFunction;
import graphql.schema.*;
import static graphql.annotations.processor.util.NamingKit.toGraphqlName;
import static graphql.annotations.processor.util.PrefixesUtil.addPrefixToPropertyName;
import static graphql.annotations.processor.util.PrefixesUtil.extractPrefixedName;
import static graphql.annotations.processor.util.ReflectionKit.constructNewInstance;
import static graphql.annotations.processor.util.ReflectionKit.newInstance;

import java.lang.reflect.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static graphql.annotations.processor.util.NamingKit.toGraphqlName;
import static graphql.annotations.processor.util.PrefixesUtil.addPrefixToPropertyName;
import static graphql.annotations.processor.util.PrefixesUtil.extractPrefixedName;
import static graphql.annotations.processor.util.ReflectionKit.constructNewInstance;
import static graphql.annotations.processor.util.ReflectionKit.newInstance;
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;
import graphql.annotations.processor.typeFunctions.TypeFunction;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLInputObjectType;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLType;


/**
Expand Down Expand Up @@ -111,48 +124,74 @@ private Object[] invocationArgs(DataFetchingEnvironment environment, ProcessingE

graphql.schema.GraphQLType graphQLType = typeFunction.buildType(true, paramType, p.getAnnotatedType(), container);
if (envArgs.containsKey(parameterName)) {
result.add(buildArg(p.getParameterizedType(), graphQLType, envArgs.get(parameterName)));
result.add(buildArg(p.getParameterizedType(), graphQLType, envArgs.containsKey(parameterName) ? Optional.ofNullable(envArgs.get(parameterName)) : null));
} else {
result.add(null);
}
}
return result.toArray();
}

private Object buildArg(Type p, GraphQLType graphQLType, Object arg) {
@SuppressWarnings("ConstantConditions")
private Object buildArg(Type p, GraphQLType graphQLType, Optional<Object> arg) {
if (arg == null) {
return null;
}
if (graphQLType instanceof graphql.schema.GraphQLNonNull) {
graphQLType = ((graphql.schema.GraphQLNonNull) graphQLType).getWrappedType();
}

if (p instanceof Class<?> && graphQLType instanceof GraphQLInputObjectType) {
Constructor<?> constructors[] = ((Class) p).getConstructors();
Constructor<?>[] constructors = ((Class) p).getConstructors();
Constructor<?> constructor = getBuildArgConstructor(constructors);
Parameter[] parameters = constructor.getParameters();
if (parameters.length == 1 && parameters[0].getType().isAssignableFrom(arg.getClass())) {
return constructNewInstance(constructor, arg);

if (parameters.length == 1 && arg.isPresent() && parameters[0].getType().isAssignableFrom(arg.get().getClass())) {
if (parameters[0].getType().isAssignableFrom(Optional.class)) {
return constructNewInstance(constructor, arg);
} else {
return constructNewInstance(constructor, arg.orElse(null));
}
} else {
List<Object> objects = new ArrayList<>();
Map map = (Map) arg;
Map map = (Map) arg.orElseGet(Collections::emptyMap);
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)));
if (!map.containsKey(name)) {
objects.add(null);
} else {
objects.add(buildArg(parameter.getParameterizedType(), ((GraphQLInputObjectType) graphQLType).getField(name).getType(), Optional.ofNullable(map.get(name))));
}
}
return constructNewInstance(constructor, objects.toArray(new Object[objects.size()]));
}
} else if (p instanceof ParameterizedType && graphQLType instanceof GraphQLList) {
List<Object> list = new ArrayList<>();
Type subType = ((ParameterizedType) p).getActualTypeArguments()[0];
GraphQLType wrappedType = ((GraphQLList) graphQLType).getWrappedType();
if (((ParameterizedType) p).getRawType() == Optional.class) {
if (arg == null) {
return null;
} else {
Type subType = ((ParameterizedType) p).getActualTypeArguments()[0];
return Optional.ofNullable(buildArg(subType, graphQLType, arg));
}
} else {
List<Object> list = new ArrayList<>();
Type subType = ((ParameterizedType) p).getActualTypeArguments()[0];
GraphQLType wrappedType = ((GraphQLList) graphQLType).getWrappedType();

for (Object item : ((List) arg)) {
list.add(buildArg(subType, wrappedType, item));
for (Object item : ((List) arg.orElseGet(Collections::emptyList))) {
list.add(buildArg(subType, wrappedType, Optional.ofNullable(item)));
}
return list;
}
} else if (p instanceof ParameterizedType && ((ParameterizedType) p).getRawType() == Optional.class) {
Type subType = ((ParameterizedType) p).getActualTypeArguments()[0];
if (arg == null) {
return null;
} else {
return Optional.ofNullable(buildArg(subType, new GraphQLUndefined(), arg));
}

return list;
} else {
return arg;
return arg.orElse(null);
}
}

Expand Down
96 changes: 84 additions & 12 deletions src/test/java/graphql/annotations/GraphQLInputTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@
*/
package graphql.annotations;

import static graphql.annotations.AnnotationsSchemaCreator.newAnnotationsSchema;
import static graphql.schema.GraphQLSchema.newSchema;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.TypeResolutionEnvironment;
Expand All @@ -20,18 +33,11 @@
import graphql.annotations.annotationTypes.GraphQLTypeResolver;
import graphql.annotations.processor.GraphQLAnnotations;
import graphql.annotations.processor.exceptions.GraphQLAnnotationsException;
import graphql.schema.*;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static graphql.annotations.AnnotationsSchemaCreator.newAnnotationsSchema;
import static graphql.schema.GraphQLSchema.newSchema;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import graphql.schema.GraphQLInputObjectType;
import graphql.schema.GraphQLNamedType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;
import graphql.schema.TypeResolver;

@SuppressWarnings("unchecked")
public class GraphQLInputTest {
Expand Down Expand Up @@ -123,6 +129,32 @@ public String value(@GraphQLName("input") RecursiveInputObject input) {
}
}

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public static class AnotherCode {

public AnotherCode(@GraphQLName("firstField") Optional<String> one, @GraphQLName("secondField") String two) {
this.firstField = one;
this.secondField = two;
}

@GraphQLField
public Optional<String> firstField;

@GraphQLField
private final String secondField;
}

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public static class AnotherCodeWithSingleField {

public AnotherCodeWithSingleField(@GraphQLName("one") Optional<String> one) {
this.field = one;
}

@GraphQLField
public Optional<String> field;
}

public static class Code {
public Code(@GraphQLName("map") HashMap map) {
this.firstField = (String) map.get("firstField");
Expand All @@ -135,6 +167,33 @@ public Code(@GraphQLName("map") HashMap map) {
public String secondField;
}

public static class QueryUndefinedParameter {

@SuppressWarnings({"unused", "OptionalAssignedToNull"})
@GraphQLField
public String something(@GraphQLName("code") AnotherCode code) {
return (code.firstField != null ? code.firstField.orElse("") : "") + code.secondField;
}

@SuppressWarnings({"unused", "OptionalAssignedToNull"})
@GraphQLField
public String somethingWithOneField(@GraphQLName("code") AnotherCodeWithSingleField code) {
return code.field != null ? code.field.orElse("") : "was undefined";
}

@SuppressWarnings({"unused", "OptionalAssignedToNull"})
@GraphQLField
public String otherthing(@GraphQLName("code") AnotherCode code) {
return (code.firstField != null ? code.firstField.orElse("") : "") + code.secondField;
}

@SuppressWarnings({"unused", "OptionalAssignedToNull", "OptionalUsedAsFieldOrParameterType"})
@GraphQLField
public String listthings(@GraphQLName("codes") Optional<List<AnotherCode>> code) {
return code == null ? "was null" : (code.map(anotherCodes -> "code was " + anotherCodes.size()).orElse("was empty"));
}
}

public static class QueryMultipleDefinitions {
@GraphQLField
public String something(@GraphQLName("code") Code code) {
Expand Down Expand Up @@ -191,6 +250,19 @@ public void query() {
assertEquals(((Map<String, Map<String, String>>) result.getData()).get("object").get("value"), "testa");
}

@Test
public void queryWithUndefinableParameters() {
GraphQLSchema schema = newAnnotationsSchema().query(QueryUndefinedParameter.class).build();

GraphQL graphQL = GraphQL.newGraphQL(schema).build();
ExecutionResult result = graphQL.execute("{ something(code: {firstField:\"a\",secondField:\"b\"}) otherthing(code: {secondField:\"c\"}) listthings somethingWithOneField(code: {}) }", new QueryUndefinedParameter());
assertTrue(result.getErrors().isEmpty());
assertEquals(((Map<String, String>) result.getData()).get("something"), "ab");
assertEquals(((Map<String, String>) result.getData()).get("otherthing"), "c");
assertEquals(((Map<String, String>) result.getData()).get("listthings"), "was null");
assertEquals(((Map<String, String>) result.getData()).get("somethingWithOneField"), "was undefined");
}

@Test
public void queryMultipleDefinitions() {
GraphQLSchema schema = newAnnotationsSchema().query(QueryMultipleDefinitions.class).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,4 +501,4 @@ public void queryingFieldsFromNoApiEntityFetcher_noMatchingFieldInEntity_throwEx
assertFalse(result.getErrors().isEmpty());
assertTrue(((ExceptionWhileDataFetching) result.getErrors().get(0)).getException().getCause() instanceof NoSuchFieldException);
}
}
}