diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9bcf9994..00000000 --- a/.travis.yml +++ /dev/null @@ -1,3 +0,0 @@ -language: java -jdk: - - oraclejdk8 diff --git a/LICENSE b/LICENSE index f9f351c0..3b5b5f7e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,3 @@ -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 diff --git a/README.md b/README.md index 290ccde8..541d7414 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![logo](polaris-iconsmalredl.png?raw=true) +![logo](graphql-annotations.png?raw=true) # GraphQL-Java Annotations [![Build Status](https://travis-ci.org/graphql-java/graphql-java-annotations.svg?branch=master)](https://travis-ci.org/graphql-java/graphql-java-annotations) [![Maven Central](https://img.shields.io/maven-central/v/io.github.graphql-java/graphql-java-annotations.svg?maxAge=3000)]() @@ -6,6 +6,8 @@ [GraphQL-Java](https://github.com/andimarek/graphql-java) is a great library, but its syntax is a little bit verbose. This library offers an annotations-based syntax for GraphQL schema definition. +If you would like to use a tool that creates a graphql spring boot server using graphql-java-annotations, you can view the [graphql-spring-annotations](https://github.com/yarinvak/graphql-spring-annotations) library. + ## Table Of Contents - [Getting Started](#getting-started) @@ -35,7 +37,7 @@ syntax for GraphQL schema definition. ```groovy dependencies { - compile "io.github.graphql-java:graphql-java-annotations:7.2.1" + compile "io.github.graphql-java:graphql-java-annotations:8.0" } ``` @@ -45,7 +47,7 @@ dependencies { io.github.graphql-java graphql-java-annotations - 7.2.1 + 8.0 ``` @@ -377,7 +379,7 @@ public class HumanExtension { ## Type Inference -By default, standard GraphQL types (String, Integer, Long, Float, Boolean, Enum, List) will be inferred from Java types. Also, it will respect `@javax.validation.constraints.NotNull` annotation with respect to value's nullability, as well as `@GraphQLNonNull` +By default, standard GraphQL types (String, Integer, Long, Float, Boolean, Enum, List) will be inferred from Java types. Also, it will respect `@GraphQLNonNull` with respect to value's nullability Stream type is also supported and treated as a list. diff --git a/build.gradle b/build.gradle index 3b8ad3ba..100e075e 100644 --- a/build.gradle +++ b/build.gradle @@ -63,7 +63,7 @@ gradle.projectsEvaluated { dependencies { compile 'javax.validation:validation-api:1.1.0.Final' - compile 'com.graphql-java:graphql-java:13.0' + compile 'com.graphql-java:graphql-java:14.0' // OSGi compileOnly 'org.osgi:org.osgi.core:6.0.0' diff --git a/gradle.properties b/gradle.properties index 8d880e2f..a37d79a4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ org.gradle.daemon=true org.gradle.parallel=true org.gradle.jvmargs=-Dfile.encoding=UTF-8 -version = 7.2.1 +version = 8.0 diff --git a/graphql-annotations.png b/graphql-annotations.png new file mode 100644 index 00000000..e1512908 Binary files /dev/null and b/graphql-annotations.png differ diff --git a/polaris-iconsmalredl.png b/polaris-iconsmalredl.png deleted file mode 100644 index a17b9f19..00000000 Binary files a/polaris-iconsmalredl.png and /dev/null differ diff --git a/src/main/java/graphql/annotations/AnnotationsSchemaCreator.java b/src/main/java/graphql/annotations/AnnotationsSchemaCreator.java index 2b9c48e8..e9fb1aa9 100644 --- a/src/main/java/graphql/annotations/AnnotationsSchemaCreator.java +++ b/src/main/java/graphql/annotations/AnnotationsSchemaCreator.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 @@ -14,17 +12,23 @@ */ package graphql.annotations; +import graphql.annotations.directives.AnnotationsDirectiveWiring; +import graphql.annotations.directives.DirectiveSchemaVisitor; +import graphql.annotations.directives.TreeTransformerUtilWrapper; +import graphql.annotations.processor.DirectiveAndWiring; import graphql.annotations.processor.GraphQLAnnotations; import graphql.annotations.processor.typeFunctions.TypeFunction; import graphql.relay.Relay; import graphql.schema.GraphQLDirective; import graphql.schema.GraphQLSchema; import graphql.schema.GraphQLType; +import graphql.schema.SchemaTransformer; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; +import static graphql.schema.GraphQLSchema.newSchema; + public class AnnotationsSchemaCreator { public static Builder newAnnotationsSchema() { @@ -43,6 +47,7 @@ public static class Builder { private Boolean shouldAlwaysPrettify = null; private GraphQLAnnotations graphQLAnnotations; private GraphQLSchema.Builder graphqlSchemaBuilder; + private SchemaTransformer schemaTransformer = new SchemaTransformer(); /** * You can set your own schema builder, but its optional @@ -114,12 +119,18 @@ public Builder directives(Set> directiveClasses) { return this; } + public Builder directives(Class... directiveClasses) { + this.directivesObjectList.addAll(Arrays.asList(directiveClasses)); + return this; + } + /** * Add directive declaration class to create directives for the graphql schema + * * @param directiveContainerClass a directive container class (directives are defined as methods inside the class) * @return the builder after adding the directive container class to the list of directive container classes */ - public Builder directives(Class directiveContainerClass){ + public Builder directives(Class directiveContainerClass) { this.directiveContainerClasses.add(directiveContainerClass); return this; } @@ -234,7 +245,7 @@ public GraphQLSchema build() { } Set directives = directivesObjectList.stream().map(dir -> graphQLAnnotations.directive(dir)).collect(Collectors.toSet()); - directiveContainerClasses.forEach(dir->directives.addAll(graphQLAnnotations.directives(dir))); + directiveContainerClasses.forEach(dir -> directives.addAll(graphQLAnnotations.directives(dir))); Set additionalTypes = additionalTypesList.stream().map(additionalType -> additionalType.isInterface() ? @@ -252,7 +263,26 @@ public GraphQLSchema build() { } this.graphqlSchemaBuilder.additionalTypes(additionalTypes).additionalType(Relay.pageInfoType) .codeRegistry(graphQLAnnotations.getContainer().getCodeRegistryBuilder().build()); - return this.graphqlSchemaBuilder.build(); + GraphQLSchema schema = this.graphqlSchemaBuilder.build(); + // wire with directives + HashMap directiveWiringHashMap = transformDirectiveRegistry(this.graphQLAnnotations.getContainer().getDirectiveRegistry()); + DirectiveSchemaVisitor directiveSchemaVisitor = new DirectiveSchemaVisitor(directiveWiringHashMap, + graphQLAnnotations.getContainer().getCodeRegistryBuilder(), new TreeTransformerUtilWrapper()); + GraphQLSchema transformedSchema = this.schemaTransformer.transform(schema, directiveSchemaVisitor); + return newSchema(transformedSchema).codeRegistry(graphQLAnnotations.getContainer().getCodeRegistryBuilder().build()).build(); + } + + private HashMap transformDirectiveRegistry(Map directiveRegistry) { + HashMap map = new HashMap<>(); + directiveRegistry.forEach((directiveName, directiveAndWiring) -> { + try { + map.put(directiveName, directiveAndWiring.getWiringClass().newInstance()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + return map; } } } diff --git a/src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironment.java b/src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironment.java index 4dcc6350..71f641a9 100644 --- a/src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironment.java +++ b/src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironment.java @@ -17,6 +17,7 @@ import graphql.schema.GraphQLCodeRegistry; import graphql.schema.GraphQLDirective; import graphql.schema.GraphQLDirectiveContainer; +import graphql.schema.GraphQLSchemaElement; public interface AnnotationsWiringEnvironment { /** @@ -33,7 +34,7 @@ public interface AnnotationsWiringEnvironment { * * @return the parent name of the element */ - String getParentName(); + GraphQLSchemaElement getParentElement(); /** diff --git a/src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironmentImpl.java b/src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironmentImpl.java index b3fbb963..f4fa0830 100644 --- a/src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironmentImpl.java +++ b/src/main/java/graphql/annotations/directives/AnnotationsWiringEnvironmentImpl.java @@ -17,18 +17,19 @@ import graphql.schema.GraphQLCodeRegistry; import graphql.schema.GraphQLDirective; import graphql.schema.GraphQLDirectiveContainer; +import graphql.schema.GraphQLSchemaElement; public class AnnotationsWiringEnvironmentImpl implements AnnotationsWiringEnvironment { private final GraphQLDirectiveContainer element; private final GraphQLDirective directive; - private final String parentName; + private final GraphQLSchemaElement parentElement; private GraphQLCodeRegistry.Builder codeRegistryBuilder; public AnnotationsWiringEnvironmentImpl(GraphQLDirectiveContainer element, GraphQLDirective directive, - String parentName, GraphQLCodeRegistry.Builder codeRegistryBuilder) { + GraphQLSchemaElement parentElement, GraphQLCodeRegistry.Builder codeRegistryBuilder) { this.element = element; this.directive = directive; - this.parentName = parentName; + this.parentElement = parentElement; this.codeRegistryBuilder = codeRegistryBuilder; } @@ -43,8 +44,8 @@ public GraphQLDirective getDirective() { } @Override - public String getParentName() { - return parentName; + public GraphQLSchemaElement getParentElement() { + return parentElement; } @Override @@ -60,7 +61,7 @@ public boolean equals(Object o) { AnnotationsWiringEnvironmentImpl that = (AnnotationsWiringEnvironmentImpl) o; if (element != null ? !element.equals(that.element) : that.element != null) return false; - if (parentName != null ? !parentName.equals(that.parentName) : that.parentName != null) return false; + if (parentElement != null ? !parentElement.equals(that.parentElement) : that.parentElement != null) return false; if (codeRegistryBuilder != null ? !codeRegistryBuilder.equals(that.codeRegistryBuilder) : that.codeRegistryBuilder != null) return false; return directive != null ? directive.equals(that.directive) : that.directive == null; diff --git a/src/main/java/graphql/annotations/directives/DirectiveSchemaVisitor.java b/src/main/java/graphql/annotations/directives/DirectiveSchemaVisitor.java new file mode 100644 index 00000000..9a7a7346 --- /dev/null +++ b/src/main/java/graphql/annotations/directives/DirectiveSchemaVisitor.java @@ -0,0 +1,196 @@ +/** + * 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 + */ +/** + * 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.directives; + +import graphql.introspection.Introspection; +import graphql.schema.*; +import graphql.util.TraversalControl; +import graphql.util.TraverserContext; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DirectiveSchemaVisitor implements GraphQLTypeVisitor { + private HashMap directiveWiringMap; + private GraphQLCodeRegistry.Builder codeRegistryBuilder; + private TreeTransformerUtilWrapper transformerUtilWrapper; + + @FunctionalInterface + interface WiringFunction { + GraphQLDirectiveContainer apply(GraphQLDirective a, GraphQLDirectiveContainer b, + AnnotationsDirectiveWiring wiring, GraphQLSchemaElement parentElement) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException; + } + + private Map functionMap; + + + public DirectiveSchemaVisitor(HashMap directiveWiringMap, GraphQLCodeRegistry.Builder codeRegistryBuilder, + TreeTransformerUtilWrapper treeTransformerUtilWrapper) { + this.directiveWiringMap = directiveWiringMap; + this.functionMap = createFunctionsMap(); + this.codeRegistryBuilder = codeRegistryBuilder; + this.transformerUtilWrapper = treeTransformerUtilWrapper; + } + + @Override + public TraversalControl visitGraphQLArgument(GraphQLArgument node, TraverserContext context) { + return this.visitGraphQLType(GraphQLArgument.class, node, context); + } + + @Override + public TraversalControl visitGraphQLInterfaceType(GraphQLInterfaceType node, TraverserContext context) { + return this.visitGraphQLType(GraphQLInterfaceType.class, node, context); + } + + @Override + public TraversalControl visitGraphQLEnumType(GraphQLEnumType node, TraverserContext context) { + return this.visitGraphQLType(GraphQLEnumType.class, node, context); + } + + @Override + public TraversalControl visitGraphQLEnumValueDefinition(GraphQLEnumValueDefinition node, TraverserContext context) { + return this.visitGraphQLType(GraphQLEnumValueDefinition.class, node, context); + } + + @Override + public TraversalControl visitGraphQLFieldDefinition(GraphQLFieldDefinition node, TraverserContext context) { + return this.visitGraphQLType(GraphQLFieldDefinition.class, node, context); + } + + @Override + public TraversalControl visitGraphQLDirective(GraphQLDirective node, TraverserContext context) { + return TraversalControl.CONTINUE; + } + + @Override + public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField node, TraverserContext context) { + return this.visitGraphQLType(GraphQLInputObjectField.class, node, context); + } + + @Override + public TraversalControl visitGraphQLInputObjectType(GraphQLInputObjectType node, TraverserContext context) { + return this.visitGraphQLType(GraphQLInputObjectType.class, node, context); + } + + @Override + public TraversalControl visitGraphQLList(GraphQLList node, TraverserContext context) { + return TraversalControl.CONTINUE; + } + + @Override + public TraversalControl visitGraphQLNonNull(GraphQLNonNull node, TraverserContext context) { + return TraversalControl.CONTINUE; + } + + @Override + public TraversalControl visitGraphQLObjectType(GraphQLObjectType node, TraverserContext context) { + return this.visitGraphQLType(GraphQLObjectType.class, node, context); + } + + @Override + public TraversalControl visitGraphQLScalarType(GraphQLScalarType node, TraverserContext context) { + return this.visitGraphQLType(GraphQLScalarType.class, node, context); + } + + @Override + public TraversalControl visitGraphQLTypeReference(GraphQLTypeReference node, TraverserContext context) { + return TraversalControl.CONTINUE; + } + + @Override + public TraversalControl visitGraphQLUnionType(GraphQLUnionType node, TraverserContext context) { + return this.visitGraphQLType(GraphQLUnionType.class, node, context); + } + + private TraversalControl visitGraphQLType(Class typeOfContainer, + GraphQLDirectiveContainer node, TraverserContext context) { + List directives = node.getDirectives(); + if (directives.size() == 0) { + return TraversalControl.CONTINUE; + } + GraphQLDirectiveContainer newNode = node; + for (GraphQLDirective directive : directives) { + AnnotationsDirectiveWiring wiring = this.directiveWiringMap.get(directive.getName()); + if (wiring != null) { + try { + GraphQLSchemaElement parentElement = context.getParentNode(); + newNode = functionMap.get(typeOfContainer).apply(directive, newNode, + wiring, parentElement); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + return transformerUtilWrapper.changeNode(context, newNode); + } + + private void putInMap(Map map, Class clazz, String functionName, + Introspection.DirectiveLocation... locations) { + map.put(clazz, (d, e, wiring, parentElement) -> { + assertLocation(d, e, locations); + AnnotationsWiringEnvironmentImpl environment = + new AnnotationsWiringEnvironmentImpl(e, e.getDirective(d.getName()), parentElement, codeRegistryBuilder); + return (GraphQLDirectiveContainer) wiring.getClass().getMethod(functionName, AnnotationsWiringEnvironment.class) + .invoke(wiring, environment); + }); + } + + private Map createFunctionsMap() { + Map functionMap = new HashMap<>(); + putInMap(functionMap, GraphQLFieldDefinition.class, "onField", Introspection.DirectiveLocation.FIELD, Introspection.DirectiveLocation.FIELD_DEFINITION); + putInMap(functionMap, GraphQLObjectType.class, "onObject", Introspection.DirectiveLocation.OBJECT); + putInMap(functionMap, GraphQLArgument.class, "onArgument", Introspection.DirectiveLocation.ARGUMENT_DEFINITION); + putInMap(functionMap, GraphQLInterfaceType.class, "onInterface", Introspection.DirectiveLocation.INTERFACE); + putInMap(functionMap, GraphQLUnionType.class, "onUnion", Introspection.DirectiveLocation.UNION); + putInMap(functionMap, GraphQLEnumType.class, "onEnum", Introspection.DirectiveLocation.ENUM); + putInMap(functionMap, GraphQLEnumValueDefinition.class, "onEnumValue", Introspection.DirectiveLocation.ENUM_VALUE); + putInMap(functionMap, GraphQLScalarType.class, "onScalar", Introspection.DirectiveLocation.SCALAR); + putInMap(functionMap, GraphQLInputObjectType.class, "onInputObjectType", Introspection.DirectiveLocation.INPUT_OBJECT); + putInMap(functionMap, GraphQLInputObjectField.class, "onInputObjectField", Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION); + + return functionMap; + } + + private void assertLocation(GraphQLDirective graphQLDirective, GraphQLDirectiveContainer element, Introspection.DirectiveLocation... validLocations) { + boolean isSupported = false; + for (Introspection.DirectiveLocation validLocation : validLocations) { + if (graphQLDirective.validLocations().contains(validLocation)) { + isSupported = true; + } + } + if (!isSupported) { + throw getInvalidDirectiveLocationException(element, graphQLDirective, validLocations); + } + } + + private InvalidDirectiveLocationException getInvalidDirectiveLocationException(GraphQLDirectiveContainer element, GraphQLDirective graphQLDirective, Introspection.DirectiveLocation... validLocations) { + return new InvalidDirectiveLocationException("The element: '" + element.getName() + "' is annotated with the directive: '" + + graphQLDirective.getName() + "' which is not valid on the element location: '" + Arrays.toString(Arrays.stream(validLocations).map(Enum::name).toArray()) + "'", null); + } +} diff --git a/src/main/java/graphql/annotations/directives/DirectiveWirer.java b/src/main/java/graphql/annotations/directives/DirectiveWirer.java deleted file mode 100644 index 0ae85e15..00000000 --- a/src/main/java/graphql/annotations/directives/DirectiveWirer.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * 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 - * - * 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.directives; - -import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; -import graphql.introspection.Introspection; -import graphql.schema.*; - -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -public class DirectiveWirer { - @FunctionalInterface - interface WiringFunction { - GraphQLDirectiveContainer apply(GraphQLDirective a, GraphQLDirectiveContainer b, - AnnotationsDirectiveWiring wiring, GraphQLCodeRegistry.Builder codeRegistryBuilder, String parentName) - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException; - } - - private Map functionMap; - - public DirectiveWirer() { - functionMap = createFunctionsMap(); - } - - private void putInMap(Map map, Class clazz, String functionName, - Introspection.DirectiveLocation... locations) { - map.put(clazz, (d, e, wiring, codeRegistryBuilder, parentName) -> { - assertLocation(d, e, locations); - AnnotationsWiringEnvironmentImpl environment = - new AnnotationsWiringEnvironmentImpl(e, e.getDirective(d.getName()), parentName, codeRegistryBuilder); - return (GraphQLDirectiveContainer) wiring.getClass().getMethod(functionName, AnnotationsWiringEnvironment.class) - .invoke(wiring, environment); - }); - } - - private Map createFunctionsMap() { - Map functionMap = new HashMap<>(); - putInMap(functionMap, GraphQLFieldDefinition.class, "onField", Introspection.DirectiveLocation.FIELD, Introspection.DirectiveLocation.FIELD_DEFINITION); - putInMap(functionMap, GraphQLObjectType.class, "onObject", Introspection.DirectiveLocation.OBJECT); - putInMap(functionMap, GraphQLArgument.class, "onArgument", Introspection.DirectiveLocation.ARGUMENT_DEFINITION); - putInMap(functionMap, GraphQLInterfaceType.class, "onInterface", Introspection.DirectiveLocation.INTERFACE); - putInMap(functionMap, GraphQLUnionType.class, "onUnion", Introspection.DirectiveLocation.UNION); - putInMap(functionMap, GraphQLEnumType.class, "onEnum", Introspection.DirectiveLocation.ENUM); - putInMap(functionMap, GraphQLEnumValueDefinition.class, "onEnumValue", Introspection.DirectiveLocation.ENUM_VALUE); - putInMap(functionMap, GraphQLScalarType.class, "onScalar", Introspection.DirectiveLocation.SCALAR); - putInMap(functionMap, GraphQLInputObjectType.class, "onInputObjectType", Introspection.DirectiveLocation.INPUT_OBJECT); - putInMap(functionMap, GraphQLInputObjectField.class, "onInputObjectField", Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION); - - return functionMap; - } - - public GraphQLDirectiveContainer wire(GraphQLDirectiveContainer element, HashMap directiveWiringMap - , GraphQLCodeRegistry.Builder codeRegistryBuilder, String parentName) { - for (Map.Entry entry : directiveWiringMap.entrySet()) { - GraphQLDirective graphQLDirective = entry.getKey(); - AnnotationsDirectiveWiring wiring = entry.getValue(); - - Class aClass = element.getClass(); - try { - element = functionMap.get(aClass).apply(graphQLDirective, element, wiring, codeRegistryBuilder, parentName); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - throw new GraphQLAnnotationsException(e.getMessage(), e); - } - } - - return element; - } - - private void assertLocation(GraphQLDirective graphQLDirective, GraphQLDirectiveContainer element, Introspection.DirectiveLocation... validLocations) { - boolean isSupported = false; - for (Introspection.DirectiveLocation validLocation : validLocations) { - if (graphQLDirective.validLocations().contains(validLocation)) { - isSupported = true; - } - } - if (!isSupported) { - throw getInvalidDirectiveLocationException(element, graphQLDirective, validLocations); - } - } - - private InvalidDirectiveLocationException getInvalidDirectiveLocationException(GraphQLDirectiveContainer element, GraphQLDirective graphQLDirective, Introspection.DirectiveLocation... validLocations) { - return new InvalidDirectiveLocationException("The element: '" + element.getName() + "' is annotated with the directive: '" - + graphQLDirective.getName() + "' which is not valid on the element location: '" + Arrays.toString(Arrays.stream(validLocations).map(Enum::name).toArray()) + "'", null); - } - -} diff --git a/src/main/java/graphql/annotations/directives/TreeTransformerUtilWrapper.java b/src/main/java/graphql/annotations/directives/TreeTransformerUtilWrapper.java new file mode 100644 index 00000000..6628ad95 --- /dev/null +++ b/src/main/java/graphql/annotations/directives/TreeTransformerUtilWrapper.java @@ -0,0 +1,23 @@ +/** + * 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.directives; + +import graphql.util.TraversalControl; +import graphql.util.TraverserContext; +import graphql.util.TreeTransformerUtil; + +public class TreeTransformerUtilWrapper { + public TraversalControl changeNode(TraverserContext context, T changedNode) { + return TreeTransformerUtil.changeNode(context, changedNode); + } +} diff --git a/src/main/java/graphql/annotations/processor/GraphQLAnnotations.java b/src/main/java/graphql/annotations/processor/GraphQLAnnotations.java index 38122693..e094383c 100644 --- a/src/main/java/graphql/annotations/processor/GraphQLAnnotations.java +++ b/src/main/java/graphql/annotations/processor/GraphQLAnnotations.java @@ -129,18 +129,6 @@ public GraphQLObjectType object(Class object) throws GraphQLAnnotationsExcept } } - @Deprecated - public GraphQLObjectType object(Class object, DirectiveAndWiring... directives) throws GraphQLAnnotationsException { - Arrays.stream(directives).forEach(directiveAndWiring -> this.getContainer().getDirectiveRegistry().put(directiveAndWiring.getDirective().getName(), directiveAndWiring)); - try { - return this.graphQLObjectHandler.getGraphQLType(object, this.getContainer()); - } catch (GraphQLAnnotationsException e) { - this.getContainer().getProcessing().clear(); - this.getTypeRegistry().clear(); - throw e; - } - } - public GraphQLDirective directive(Class object) throws GraphQLAnnotationsException { if (!object.isAnnotationPresent(GraphQLDirectiveDefinition.class)){ throw new GraphQLAnnotationsException(String.format(NOT_PROPERLY_ANNOTATION_ERROR, object.getSimpleName()), null); @@ -158,11 +146,6 @@ public GraphQLDirective directive(Class object) throws GraphQLAnnotationsExce } } - @Deprecated - public GraphQLDirective directiveViaAnnotation(Class annotationClass) { - return this.directive(annotationClass); - } - public Set directives(Class directivesDeclarationClass) { Method[] methods = directivesDeclarationClass.getMethods(); Set directiveSet = new HashSet<>(); @@ -189,10 +172,6 @@ public void registerTypeFunction(TypeFunction typeFunction) { ((DefaultTypeFunction) container.getDefaultTypeFunction()).register(typeFunction); } - @Deprecated - public void register(TypeFunction typeFunction) { - this.registerTypeFunction(typeFunction); - } public Map getTypeRegistry() { return container.getTypeRegistry(); diff --git a/src/main/java/graphql/annotations/processor/directives/DirectiveArgumentCreator.java b/src/main/java/graphql/annotations/processor/directives/DirectiveArgumentCreator.java index 39ba355d..5fbbbbd1 100644 --- a/src/main/java/graphql/annotations/processor/directives/DirectiveArgumentCreator.java +++ b/src/main/java/graphql/annotations/processor/directives/DirectiveArgumentCreator.java @@ -15,7 +15,6 @@ package graphql.annotations.processor.directives; import graphql.annotations.processor.ProcessingElementsContainer; -import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; import graphql.annotations.processor.typeFunctions.TypeFunction; import graphql.schema.GraphQLArgument; import graphql.schema.GraphQLInputType; @@ -45,7 +44,7 @@ public GraphQLArgument getArgument(Field field, Class containingClass) { try { builder.defaultValue(getDefaultValue(field, containingClass)); } catch (IllegalAccessException | InstantiationException e) { - throw new GraphQLAnnotationsException(e); + builder.defaultValue(null); } return builder.build(); diff --git a/src/main/java/graphql/annotations/processor/retrievers/GraphQLFieldRetriever.java b/src/main/java/graphql/annotations/processor/retrievers/GraphQLFieldRetriever.java index cc8dc4ac..0fb4ba90 100644 --- a/src/main/java/graphql/annotations/processor/retrievers/GraphQLFieldRetriever.java +++ b/src/main/java/graphql/annotations/processor/retrievers/GraphQLFieldRetriever.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 @@ -17,8 +15,6 @@ import graphql.annotations.annotationTypes.GraphQLRelayMutation; import graphql.annotations.connection.GraphQLConnection; -import graphql.annotations.directives.DirectiveWirer; -import graphql.annotations.directives.DirectiveWiringMapRetriever; import graphql.annotations.processor.ProcessingElementsContainer; import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; import graphql.annotations.processor.retrievers.fieldBuilders.ArgumentBuilder; @@ -49,6 +45,7 @@ import java.util.Map; import java.util.stream.Collectors; +import static graphql.annotations.processor.util.GraphQLTypeNameResolver.getName; import static graphql.annotations.processor.util.ReflectionKit.newInstance; import static graphql.schema.FieldCoordinates.coordinates; import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition; @@ -92,10 +89,7 @@ public GraphQLFieldDefinition getField(String parentName, Method method, Process DataFetcher dataFetcher = new MethodDataFetcherBuilder(method, outputType, typeFunction, container, relayFieldDefinition, args, dataFetcherConstructor, isConnection).build(); container.getCodeRegistryBuilder().dataFetcher(coordinates(parentName, fieldName), dataFetcher); - - return (GraphQLFieldDefinition) new DirectiveWirer().wire(builder.build(), - new DirectiveWiringMapRetriever().getDirectiveWiringMap(method, container), - container.getCodeRegistryBuilder(), parentName); + return builder.build(); } public GraphQLFieldDefinition getField(String parentName, Field field, ProcessingElementsContainer container) throws GraphQLAnnotationsException { @@ -120,9 +114,7 @@ public GraphQLFieldDefinition getField(String parentName, Field field, Processin GraphQLDirective[] graphQLDirectives = new DirectivesBuilder(field, container).build(); builder.withDirectives(graphQLDirectives); - return (GraphQLFieldDefinition) new DirectiveWirer().wire(builder.build(), - new DirectiveWiringMapRetriever().getDirectiveWiringMap(field, container), - container.getCodeRegistryBuilder(), parentName); + return builder.build(); } public GraphQLInputObjectField getInputField(Method method, ProcessingElementsContainer container, String parentName) throws GraphQLAnnotationsException { @@ -131,10 +123,8 @@ public GraphQLInputObjectField getInputField(Method method, ProcessingElementsCo TypeFunction typeFunction = getTypeFunction(method, container); GraphQLInputType inputType = (GraphQLInputType) new MethodTypeBuilder(method, typeFunction, container, true).build(); builder.withDirectives(new DirectivesBuilder(method, container).build()); - return (GraphQLInputObjectField) new DirectiveWirer().wire(builder.type(inputType) - .description(new DescriptionBuilder(method).build()).build(), - new DirectiveWiringMapRetriever().getDirectiveWiringMap(method, container), container.getCodeRegistryBuilder(), parentName - ); + return builder.type(inputType) + .description(new DescriptionBuilder(method).build()).build(); } public GraphQLInputObjectField getInputField(Field field, ProcessingElementsContainer container, String parentName) throws GraphQLAnnotationsException { @@ -143,9 +133,8 @@ public GraphQLInputObjectField getInputField(Field field, ProcessingElementsCont TypeFunction typeFunction = getTypeFunction(field, container); GraphQLType graphQLType = typeFunction.buildType(true, field.getType(), field.getAnnotatedType(), container); builder.withDirectives(new DirectivesBuilder(field, container).build()); - return (GraphQLInputObjectField) new DirectiveWirer().wire(builder.type((GraphQLInputType) graphQLType) - .description(new DescriptionBuilder(field).build()).build(), - new DirectiveWiringMapRetriever().getDirectiveWiringMap(field, container), container.getCodeRegistryBuilder(), parentName); + return builder.type((GraphQLInputType) graphQLType) + .description(new DescriptionBuilder(field).build()).build(); } private GraphQLFieldDefinition handleRelayArguments(Method method, ProcessingElementsContainer container, GraphQLFieldDefinition.Builder builder, GraphQLOutputType outputType, List args) { @@ -154,10 +143,10 @@ private GraphQLFieldDefinition handleRelayArguments(Method method, ProcessingEle relayFieldDefinition = buildRelayMutation(method, container, builder, outputType, args); // Getting the data fetcher from the old field type and putting it as the new type - String newParentType = relayFieldDefinition.getType().getName(); + String newParentType = (getName(relayFieldDefinition.getType())); relayFieldDefinition.getType().getChildren().forEach(field -> { - DataFetcher dataFetcher = CodeRegistryUtil.getDataFetcher(container.getCodeRegistryBuilder(), outputType.getName(), (GraphQLFieldDefinition) field); - container.getCodeRegistryBuilder().dataFetcher(coordinates(newParentType, field.getName()), dataFetcher); + DataFetcher dataFetcher = CodeRegistryUtil.getDataFetcher(container.getCodeRegistryBuilder(), outputType, (GraphQLFieldDefinition) field); + container.getCodeRegistryBuilder().dataFetcher(coordinates(newParentType, getName(field)), dataFetcher); }); } else { @@ -217,10 +206,10 @@ private GraphQLOutputType getGraphQLConnection(AccessibleObject field, graphql.s } private GraphQLOutputType internalGetGraphQLConnection(AccessibleObject field, GraphQLList listType, Relay relay, Map typeRegistry) { - GraphQLOutputType wrappedType = (GraphQLOutputType) listType.getWrappedType(); + GraphQLType wrappedType = listType.getWrappedType(); String connectionName = field.getAnnotation(GraphQLConnection.class).name(); - connectionName = connectionName.isEmpty() ? wrappedType.getName() : connectionName; - GraphQLObjectType edgeType = getActualType(relay.edgeType(connectionName, wrappedType, null, Collections.emptyList()), typeRegistry); + connectionName = connectionName.isEmpty() ? getName(wrappedType) : connectionName; + GraphQLObjectType edgeType = getActualType(relay.edgeType(connectionName, (GraphQLOutputType) wrappedType, null, Collections.emptyList()), typeRegistry); return getActualType(relay.connectionType(connectionName, edgeType, Collections.emptyList()), typeRegistry); } diff --git a/src/main/java/graphql/annotations/processor/retrievers/GraphQLTypeRetriever.java b/src/main/java/graphql/annotations/processor/retrievers/GraphQLTypeRetriever.java index 94628f85..3bc15637 100644 --- a/src/main/java/graphql/annotations/processor/retrievers/GraphQLTypeRetriever.java +++ b/src/main/java/graphql/annotations/processor/retrievers/GraphQLTypeRetriever.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 @@ -16,13 +14,12 @@ import graphql.annotations.annotationTypes.GraphQLTypeResolver; import graphql.annotations.annotationTypes.GraphQLUnion; -import graphql.annotations.directives.DirectiveWirer; -import graphql.annotations.directives.DirectiveWiringMapRetriever; import graphql.annotations.processor.ProcessingElementsContainer; import graphql.annotations.processor.exceptions.CannotCastMemberException; import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; import graphql.annotations.processor.searchAlgorithms.SearchAlgorithm; import graphql.annotations.processor.typeBuilders.*; +import graphql.annotations.processor.util.GraphQLTypeNameResolver; import graphql.schema.*; import org.osgi.service.component.annotations.*; @@ -87,14 +84,7 @@ public GraphQLType getGraphQLType(Class object, ProcessingElementsContainer c } } - DirectiveWirer directiveWirer = new DirectiveWirer(); - - // wire the type with the directives and change the original type - type = directiveWirer.wire((GraphQLDirectiveContainer) type, - new DirectiveWiringMapRetriever().getDirectiveWiringMap(object, container), - container.getCodeRegistryBuilder(), null); - - container.getTypeRegistry().put(type.getName(), type); + container.getTypeRegistry().put(GraphQLTypeNameResolver.getName(type), type); container.getProcessing().pop(); return type; diff --git a/src/main/java/graphql/annotations/processor/retrievers/fieldBuilders/ArgumentBuilder.java b/src/main/java/graphql/annotations/processor/retrievers/fieldBuilders/ArgumentBuilder.java index 461851e4..08e5a495 100644 --- a/src/main/java/graphql/annotations/processor/retrievers/fieldBuilders/ArgumentBuilder.java +++ b/src/main/java/graphql/annotations/processor/retrievers/fieldBuilders/ArgumentBuilder.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 @@ -17,8 +15,6 @@ import graphql.annotations.annotationTypes.GraphQLDefaultValue; import graphql.annotations.annotationTypes.GraphQLDescription; import graphql.annotations.annotationTypes.GraphQLName; -import graphql.annotations.directives.DirectiveWirer; -import graphql.annotations.directives.DirectiveWiringMapRetriever; import graphql.annotations.processor.ProcessingElementsContainer; import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; import graphql.annotations.processor.typeFunctions.TypeFunction; @@ -81,9 +77,7 @@ private GraphQLArgument getArgument(Parameter parameter, graphql.schema.GraphQLI argumentBuilder.name(toGraphqlName(parameter.getName())); } argumentBuilder.withDirectives(new DirectivesBuilder(parameter, container).build()); - return (GraphQLArgument) new DirectiveWirer().wire(argumentBuilder.build(), - new DirectiveWiringMapRetriever().getDirectiveWiringMap(parameter, container), container.getCodeRegistryBuilder(), - inputType.getName()); + return argumentBuilder.build(); } } diff --git a/src/main/java/graphql/annotations/processor/util/CodeRegistryUtil.java b/src/main/java/graphql/annotations/processor/util/CodeRegistryUtil.java index f9e70aca..77d9c29b 100644 --- a/src/main/java/graphql/annotations/processor/util/CodeRegistryUtil.java +++ b/src/main/java/graphql/annotations/processor/util/CodeRegistryUtil.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 @@ -19,8 +17,6 @@ import java.util.function.BiFunction; -import static graphql.schema.GraphQLObjectType.newObject; - public class CodeRegistryUtil { /** * This util method helps you wrap your datafetcher with some lambda code @@ -31,21 +27,22 @@ public class CodeRegistryUtil { */ public static void wrapDataFetcher(GraphQLFieldDefinition fieldDefinition, AnnotationsWiringEnvironment environment, BiFunction mapFunction) { - DataFetcher originalDataFetcher = getDataFetcher(environment.getCodeRegistryBuilder(), environment.getParentName(), fieldDefinition); + String parentName = ((GraphQLNamedSchemaElement) environment.getParentElement()).getName(); + DataFetcher originalDataFetcher = getDataFetcher(environment.getCodeRegistryBuilder(), environment.getParentElement(), fieldDefinition); DataFetcher wrappedDataFetcher = DataFetcherFactories.wrapDataFetcher(originalDataFetcher, mapFunction); environment.getCodeRegistryBuilder() - .dataFetcher(FieldCoordinates.coordinates(environment.getParentName(), fieldDefinition.getName()), wrappedDataFetcher); + .dataFetcher(FieldCoordinates.coordinates(parentName, fieldDefinition.getName()), wrappedDataFetcher); } /** * this util method helps you retrieve the data fetcher from the code registry if you do not have the whole parent object (only parent name) * * @param codeRegistryBuilder the code registry builder - * @param parentName the parent name + * @param parentElement the parent name * @param fieldDefinition the field definition which the data fetcher is linked to * @return the data fetcher */ - public static DataFetcher getDataFetcher(GraphQLCodeRegistry.Builder codeRegistryBuilder, String parentName, GraphQLFieldDefinition fieldDefinition) { - return codeRegistryBuilder.getDataFetcher(newObject().name(parentName).build(), fieldDefinition); + public static DataFetcher getDataFetcher(GraphQLCodeRegistry.Builder codeRegistryBuilder, GraphQLSchemaElement parentElement, GraphQLFieldDefinition fieldDefinition) { + return codeRegistryBuilder.getDataFetcher((GraphQLFieldsContainer) parentElement, fieldDefinition); } } diff --git a/src/main/java/graphql/annotations/processor/util/GraphQLTypeNameResolver.java b/src/main/java/graphql/annotations/processor/util/GraphQLTypeNameResolver.java new file mode 100644 index 00000000..13051876 --- /dev/null +++ b/src/main/java/graphql/annotations/processor/util/GraphQLTypeNameResolver.java @@ -0,0 +1,35 @@ +/** + * 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.processor.util; + +import graphql.schema.*; + +public class GraphQLTypeNameResolver { + public static String getName(GraphQLSchemaElement graphQLSchemaElement) { + try { + return ((GraphQLNamedSchemaElement) graphQLSchemaElement).getName(); + } catch (Exception exception) { + if (graphQLSchemaElement instanceof GraphQLNonNull) { + return getName(((GraphQLNonNull) graphQLSchemaElement).getWrappedType()); + } else if (graphQLSchemaElement instanceof GraphQLList) { + GraphQLType iterator = (GraphQLType) graphQLSchemaElement; + do { + iterator = ((GraphQLList) iterator).getWrappedType(); + } while (iterator instanceof GraphQLList); + return getName(iterator); + } else { + throw new RuntimeException("Cannot determine name for schema element"); + } + } + } +} diff --git a/src/test/java/graphql/annotations/GraphQLDirectivesViaAnnotationDefinitionTest.java b/src/test/java/graphql/annotations/GraphQLDirectivesViaAnnotationDefinitionTest.java index 78241019..bb234144 100644 --- a/src/test/java/graphql/annotations/GraphQLDirectivesViaAnnotationDefinitionTest.java +++ b/src/test/java/graphql/annotations/GraphQLDirectivesViaAnnotationDefinitionTest.java @@ -36,7 +36,7 @@ import java.lang.annotation.Target; import java.util.Map; -import static graphql.schema.GraphQLSchema.newSchema; +import static graphql.annotations.AnnotationsSchemaCreator.newAnnotationsSchema; import static org.testng.Assert.*; public class GraphQLDirectivesViaAnnotationDefinitionTest { @@ -46,12 +46,7 @@ public class GraphQLDirectivesViaAnnotationDefinitionTest { @BeforeMethod public void setUp() { this.graphQLAnnotations = new GraphQLAnnotations(); - this.graphQLAnnotations.directive(Upper.class); - this.graphQLAnnotations.directive(Suffix.class); - this.graphQLAnnotations.directive(DirectiveWithList.class); - GraphQLObjectType object = this.graphQLAnnotations.object(Query.class); - GraphQLCodeRegistry codeRegistry = graphQLAnnotations.getContainer().getCodeRegistryBuilder().build(); - this.schema = newSchema().query(object).codeRegistry(codeRegistry).build(); + this.schema = newAnnotationsSchema().query(Query.class).directives(Upper.class, Suffix.class, DirectiveWithList.class).build(); } /** diff --git a/src/test/java/graphql/annotations/GraphQLDirectivesViaClassDefinitionTest.java b/src/test/java/graphql/annotations/GraphQLDirectivesViaClassDefinitionTest.java index f2030f80..30ff45ba 100644 --- a/src/test/java/graphql/annotations/GraphQLDirectivesViaClassDefinitionTest.java +++ b/src/test/java/graphql/annotations/GraphQLDirectivesViaClassDefinitionTest.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 @@ -16,14 +14,14 @@ import graphql.ExecutionResult; import graphql.GraphQL; -import graphql.annotations.annotationTypes.directives.definition.GraphQLDirectiveDefinition; -import graphql.annotations.annotationTypes.directives.activation.GraphQLDirectives; import graphql.annotations.annotationTypes.GraphQLField; import graphql.annotations.annotationTypes.GraphQLName; -import graphql.annotations.directives.AnnotationsDirectiveWiring; -import graphql.annotations.directives.AnnotationsWiringEnvironment; import graphql.annotations.annotationTypes.directives.activation.Directive; +import graphql.annotations.annotationTypes.directives.activation.GraphQLDirectives; import graphql.annotations.annotationTypes.directives.definition.DirectiveLocations; +import graphql.annotations.annotationTypes.directives.definition.GraphQLDirectiveDefinition; +import graphql.annotations.directives.AnnotationsDirectiveWiring; +import graphql.annotations.directives.AnnotationsWiringEnvironment; import graphql.annotations.processor.DirectiveAndWiring; import graphql.annotations.processor.GraphQLAnnotations; import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; @@ -33,14 +31,10 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; import java.util.Map; import static graphql.Scalars.GraphQLBoolean; -import static graphql.Scalars.GraphQLString; +import static graphql.annotations.AnnotationsSchemaCreator.newAnnotationsSchema; import static graphql.schema.GraphQLDirective.newDirective; import static graphql.schema.GraphQLSchema.newSchema; import static org.testng.Assert.*; @@ -123,7 +117,7 @@ public static String nameWithFalse() { public static class Query2 { @GraphQLField - @GraphQLDirectives(@Directive(name = "upperCase")) + @GraphQLDirectives(@Directive(name = "upperCaseNoDefault", argumentsValues = {"true"})) public static String nameWithNoArgs() { return "yarin"; } @@ -168,42 +162,32 @@ public static String nameWithInputObject(@GraphQLName("inputObject") InputObject } - - - - @Test public void queryNameWithInputObject_directivesProvidedToRegistry_wiringOfInputObjectIsActivated() { - GraphQLDirective suffixDirective = GraphQLDirective.newDirective().name("suffix").argument(builder -> builder.name("suffix").type(GraphQLString)) - .validLocations(Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION, Introspection.DirectiveLocation.FIELD_DEFINITION).build(); - - this.graphQLAnnotations.getContainer().getDirectiveRegistry().put(suffixDirective.getName(), new DirectiveAndWiring(suffixDirective, SuffixWiring.class)); - GraphQLObjectType object = this.graphQLAnnotations.object(Query5.class); - GraphQLCodeRegistry codeRegistry = graphQLAnnotations.getContainer().getCodeRegistryBuilder().build(); - GraphQLSchema schema = newSchema().query(object).codeRegistry(codeRegistry).build(); + GraphQLSchema schema = newAnnotationsSchema().query(Query5.class).directive(SuffixDirective.class).build(); GraphQLFieldDefinition nameWithInputObject = schema.getQueryType().getFieldDefinition("nameWithInputObject"); GraphQLInputObjectField field = ((GraphQLInputObjectType) nameWithInputObject.getArgument("inputObject").getType()).getField("acoolSuffix"); assertNotNull(field); } + @GraphQLName("suffix") + @GraphQLDirectiveDefinition(wiring = SuffixWiring.class) + @DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION, Introspection.DirectiveLocation.ARGUMENT_DEFINITION}) + class SuffixDirective { + @GraphQLName("suffix") + public String suffix; + } + @Test public void queryNameWithArgument_directivesProvidedToRegistry_wiringOfArgumentIsActivated() { - GraphQLDirective suffixDirective = GraphQLDirective.newDirective().name("suffix").argument(builder -> builder.name("suffix").type(GraphQLString)) - .validLocations(Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.ARGUMENT_DEFINITION).build(); - this.graphQLAnnotations.getContainer().getDirectiveRegistry().put(suffixDirective.getName(), new DirectiveAndWiring(suffixDirective, SuffixWiring.class)); - GraphQLObjectType object = this.graphQLAnnotations.object(Query4.class); - GraphQLCodeRegistry codeRegistry = graphQLAnnotations.getContainer().getCodeRegistryBuilder().build(); - GraphQLSchema schema = newSchema().query(object).codeRegistry(codeRegistry).build(); - + GraphQLSchema schema = newAnnotationsSchema().query(Query4.class).directive(SuffixDirective.class).build(); ExecutionResult result = GraphQL.newGraphQL(schema).build().execute("query { nameWithArgument(extensionArgcoolSuffixForArg: \"ext\") }"); assertTrue(result.getErrors().isEmpty()); } @Test(expectedExceptions = GraphQLAnnotationsException.class) public void queryName_noDirectivesProvidedToRegistry_exceptionIsThrown() throws Exception { - GraphQLObjectType object = this.graphQLAnnotations.object(Query.class); - GraphQLCodeRegistry codeRegistry = graphQLAnnotations.getContainer().getCodeRegistryBuilder().build(); - GraphQLSchema schema = newSchema().query(object).codeRegistry(codeRegistry).build(); + GraphQLSchema schema = newAnnotationsSchema().query(Query.class).build(); GraphQL.newGraphQL(schema).build().execute("query { name }"); } @@ -212,16 +196,22 @@ public void queryName_noDirectivesProvidedToRegistry_exceptionIsThrown() throws @DirectiveLocations(Introspection.DirectiveLocation.FIELD_DEFINITION) @GraphQLDirectiveDefinition(wiring = UpperWiring.class) public static class UpperCase { + boolean isActive = true; + } + + @GraphQLName("upperCaseNoDefault") + @DirectiveLocations(Introspection.DirectiveLocation.FIELD_DEFINITION) + @GraphQLDirectiveDefinition(wiring = UpperWiring.class) + public static class UpperCaseNoDefault { boolean isActive; } + @Test public void queryName_directivesProvidedToRegistry_wiringIsActivated() throws Exception { this.graphQLAnnotations.directive(UpperCase.class); - GraphQLObjectType object = this.graphQLAnnotations.object(Query.class); - GraphQLCodeRegistry codeRegistry = graphQLAnnotations.getContainer().getCodeRegistryBuilder().build(); - GraphQLSchema schema = newSchema().query(object).codeRegistry(codeRegistry).build(); + GraphQLSchema schema = newAnnotationsSchema().query(Query.class).directive(UpperCase.class).build(); ExecutionResult result = GraphQL.newGraphQL(schema).build().execute("query { name }"); assertTrue(result.getErrors().isEmpty()); @@ -244,12 +234,7 @@ public void queryNameWithFalse_directivesProvidedToRegistry_wiringIsActivated() @Test public void queryNameWithNoArgs_directivesProvidedToRegistry_wiringIsActivated() throws Exception { - GraphQLDirective upperCase = newDirective().name("upperCase").argument(builder -> builder.name("isActive").type(GraphQLBoolean).defaultValue(true)) - .validLocations(Introspection.DirectiveLocation.FIELD_DEFINITION).build(); - this.graphQLAnnotations.getContainer().getDirectiveRegistry().put(upperCase.getName(), new DirectiveAndWiring(upperCase, UpperWiring.class)); - GraphQLObjectType object = this.graphQLAnnotations.object(Query2.class); - GraphQLCodeRegistry codeRegistry = graphQLAnnotations.getContainer().getCodeRegistryBuilder().build(); - GraphQLSchema schema = newSchema().query(object).codeRegistry(codeRegistry).build(); + GraphQLSchema schema = newAnnotationsSchema().query(Query2.class).directive(UpperCaseNoDefault.class).build(); ExecutionResult result = GraphQL.newGraphQL(schema).build().execute("query { nameWithNoArgs }"); assertTrue(result.getErrors().isEmpty()); @@ -258,29 +243,14 @@ public void queryNameWithNoArgs_directivesProvidedToRegistry_wiringIsActivated() @Test(expectedExceptions = GraphQLAnnotationsException.class) public void queryNameWithNoArgs_noDefaultValue_exceptionIsThrown() throws Exception { - GraphQLDirective upperCase = newDirective().name("upperCase").argument(builder -> builder.name("isActive").type(GraphQLBoolean)) - .validLocations(Introspection.DirectiveLocation.FIELD_DEFINITION).build(); - this.graphQLAnnotations.getContainer().getDirectiveRegistry().put(upperCase.getName(), new DirectiveAndWiring(upperCase, UpperWiring.class)); - GraphQLObjectType object = this.graphQLAnnotations.object(Query2.class); - GraphQLCodeRegistry codeRegistry = graphQLAnnotations.getContainer().getCodeRegistryBuilder().build(); - - GraphQLSchema schema = newSchema().query(object).codeRegistry(codeRegistry).build(); + GraphQLSchema schema = newAnnotationsSchema().query(Query2.class).directive(UpperCase.class).build(); GraphQL.newGraphQL(schema).build().execute("query { nameWithNoArgs }"); } @Test public void queryName_chainedDirectives_wiringIsActivatedInCorrectOrder() throws Exception { - GraphQLDirective upperCase = newDirective().name("upperCase").argument(builder -> builder.name("isActive").type(GraphQLBoolean).defaultValue(true)) - .validLocations(Introspection.DirectiveLocation.FIELD_DEFINITION).build(); - GraphQLDirective suffixDirective = GraphQLDirective.newDirective().name("suffix").argument(builder -> builder.name("suffix").type(GraphQLString)) - .validLocations(Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.ARGUMENT_DEFINITION).build(); - this.graphQLAnnotations.getContainer().getDirectiveRegistry().put(upperCase.getName(), new DirectiveAndWiring(upperCase, UpperWiring.class)); - this.graphQLAnnotations.getContainer().getDirectiveRegistry().put(suffixDirective.getName(), new DirectiveAndWiring(suffixDirective, SuffixWiring.class)); - GraphQLObjectType object = this.graphQLAnnotations.object(Query3.class); - GraphQLCodeRegistry codeRegistry = graphQLAnnotations.getContainer().getCodeRegistryBuilder().build(); - - GraphQLSchema schema = newSchema().query(object).codeRegistry(codeRegistry).build(); + GraphQLSchema schema = newAnnotationsSchema().query(Query3.class).directives(SuffixDirective.class, UpperCase.class).build(); ExecutionResult result = GraphQL.newGraphQL(schema).build().execute("query { name }"); assertTrue(result.getErrors().isEmpty()); diff --git a/src/test/java/graphql/annotations/GraphQLDirectivesViaMethodDefinitionTest.java b/src/test/java/graphql/annotations/GraphQLDirectivesViaMethodDefinitionTest.java index 3272dd31..38059589 100644 --- a/src/test/java/graphql/annotations/GraphQLDirectivesViaMethodDefinitionTest.java +++ b/src/test/java/graphql/annotations/GraphQLDirectivesViaMethodDefinitionTest.java @@ -24,15 +24,13 @@ import graphql.annotations.annotationTypes.directives.definition.GraphQLDirectiveDefinition; import graphql.annotations.processor.GraphQLAnnotations; import graphql.introspection.Introspection; -import graphql.schema.GraphQLCodeRegistry; -import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Map; -import static graphql.schema.GraphQLSchema.newSchema; +import static graphql.annotations.AnnotationsSchemaCreator.newAnnotationsSchema; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -60,10 +58,7 @@ public static String name() { @BeforeMethod public void setUp() { this.graphQLAnnotations = new GraphQLAnnotations(); - this.graphQLAnnotations.directives(DirectivesContainer.class); - GraphQLObjectType object = this.graphQLAnnotations.object(DirectivesContainer.class); - GraphQLCodeRegistry codeRegistry = graphQLAnnotations.getContainer().getCodeRegistryBuilder().build(); - this.schema = newSchema().query(object).codeRegistry(codeRegistry).build(); + this.schema = newAnnotationsSchema().query(DirectivesContainer.class).directives(DirectivesContainer.class).build(); } diff --git a/src/test/java/graphql/annotations/GraphQLInputTest.java b/src/test/java/graphql/annotations/GraphQLInputTest.java index bf918ebc..3561a7b0 100644 --- a/src/test/java/graphql/annotations/GraphQLInputTest.java +++ b/src/test/java/graphql/annotations/GraphQLInputTest.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 @@ -22,10 +20,7 @@ import graphql.annotations.annotationTypes.GraphQLTypeResolver; import graphql.annotations.processor.GraphQLAnnotations; import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; -import graphql.schema.GraphQLInputObjectType; -import graphql.schema.GraphQLObjectType; -import graphql.schema.GraphQLSchema; -import graphql.schema.TypeResolver; +import graphql.schema.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -297,10 +292,10 @@ public void testInputAndOutputWithSameName() { // arrange + act GraphQLSchema schema = newSchema().query(this.graphQLAnnotations.object(QueryInputAndOutput.class)).build(); // assert - assertEquals(schema.getQueryType().getFieldDefinition("getHero").getType().getName(), "hero"); - assertEquals(schema.getQueryType().getFieldDefinition("getString").getArgument("input").getType().getName(), "Inputhero"); - assertEquals(((GraphQLInputObjectType) schema.getQueryType().getFieldDefinition("getString") - .getArgument("input").getType()).getField("skill").getType().getName(), "InputSkill"); + assertEquals(((GraphQLNamedType) (schema.getQueryType().getFieldDefinition("getHero").getType())).getName(), "hero"); + assertEquals(((GraphQLNamedType) schema.getQueryType().getFieldDefinition("getString").getArgument("input").getType()).getName(), "Inputhero"); + assertEquals(((GraphQLNamedType) ((GraphQLInputObjectType) schema.getQueryType().getFieldDefinition("getString") + .getArgument("input").getType()).getField("skill").getType()).getName(), "InputSkill"); } @Test @@ -308,8 +303,8 @@ public void testInputAndOutputSameClass() { // arrange + act GraphQLSchema schema = newSchema().query(this.graphQLAnnotations.object(QueryInputAndOutput2.class)).build(); // assert - assertEquals(schema.getQueryType().getFieldDefinition("getSkill").getType().getName(), "Skill"); - assertEquals(schema.getQueryType().getFieldDefinition("getA").getArgument("skill").getType().getName(), "InputSkill"); + assertEquals(((GraphQLNamedType) schema.getQueryType().getFieldDefinition("getSkill").getType()).getName(), "Skill"); + assertEquals(((GraphQLNamedType) schema.getQueryType().getFieldDefinition("getA").getArgument("skill").getType()).getName(), "InputSkill"); } @GraphQLName("A") @@ -366,11 +361,11 @@ public void testInputAndOutputWithSameNameWithChildrenWithSameName() { // arrange + act GraphQLSchema schema = newSchema().query(this.graphQLAnnotations.object(QuerySameNameWithChildren.class)).build(); // assert - assertEquals(schema.getQueryType().getFieldDefinition("getAout").getType().getName(), "A"); - assertEquals(schema.getQueryType().getFieldDefinition("getAout").getType().getClass(), GraphQLObjectType.class); - assertEquals(schema.getQueryType().getFieldDefinition("getBout").getType().getName(), "B"); + assertEquals(((GraphQLNamedType) schema.getQueryType().getFieldDefinition("getAout").getType()).getName(), "A"); + assertEquals(((GraphQLNamedType) schema.getQueryType().getFieldDefinition("getAout").getType()).getClass(), GraphQLObjectType.class); + assertEquals(((GraphQLNamedType) schema.getQueryType().getFieldDefinition("getBout").getType()).getName(), "B"); assertEquals(schema.getQueryType().getFieldDefinition("getBout").getType().getClass(), GraphQLObjectType.class); - assertEquals(schema.getQueryType().getFieldDefinition("getA").getArgument("input").getType().getName(), "InputA"); + assertEquals(((GraphQLNamedType) schema.getQueryType().getFieldDefinition("getA").getArgument("input").getType()).getName(), "InputA"); assertEquals(schema.getQueryType().getFieldDefinition("getA").getArgument("input").getType().getClass(), GraphQLInputObjectType.class); assertEquals(schema.getQueryType().getFieldDefinition("getB").getArgument("input").getType().getClass(), GraphQLInputObjectType.class); diff --git a/src/test/java/graphql/annotations/GraphQLInterfaceTest.java b/src/test/java/graphql/annotations/GraphQLInterfaceTest.java index f461fc9f..00c0ae57 100644 --- a/src/test/java/graphql/annotations/GraphQLInterfaceTest.java +++ b/src/test/java/graphql/annotations/GraphQLInterfaceTest.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 @@ -129,7 +127,7 @@ public void testUnion() { @Test public void testInterfaces() { GraphQLObjectType object = this.graphQLAnnotations.object(TestObject.class); - List ifaces = object.getInterfaces(); + List ifaces = object.getInterfaces(); assertEquals(ifaces.size(), 1); assertEquals(ifaces.get(0).getName(), "TestIface"); } diff --git a/src/test/java/graphql/annotations/GraphQLObjectTest.java b/src/test/java/graphql/annotations/GraphQLObjectTest.java index 1c74bf56..d3893a7d 100644 --- a/src/test/java/graphql/annotations/GraphQLObjectTest.java +++ b/src/test/java/graphql/annotations/GraphQLObjectTest.java @@ -306,8 +306,8 @@ public void fields() { assertEquals(fields.get(5).getName(), "privateTest"); assertEquals(fields.get(6).getName(), "publicTest"); - DataFetcher dataFetcher1 = CodeRegistryUtil.getDataFetcher(this.graphQLAnnotations.getContainer().getCodeRegistryBuilder(), "TestObject", fields.get(5)); - DataFetcher dataFetcher2 = CodeRegistryUtil.getDataFetcher(this.graphQLAnnotations.getContainer().getCodeRegistryBuilder(), "TestObject", fields.get(6)); + DataFetcher dataFetcher1 = CodeRegistryUtil.getDataFetcher(this.graphQLAnnotations.getContainer().getCodeRegistryBuilder(), object, fields.get(5)); + DataFetcher dataFetcher2 = CodeRegistryUtil.getDataFetcher(this.graphQLAnnotations.getContainer().getCodeRegistryBuilder(), object, fields.get(6)); assertEquals(dataFetcher1.getClass(), PropertyDataFetcher.class); assertEquals(dataFetcher2.getClass(), PropertyDataFetcher.class); diff --git a/src/test/java/graphql/annotations/connection/GraphQLConnectionTest.java b/src/test/java/graphql/annotations/connection/GraphQLConnectionTest.java index 94283ac7..1d11d455 100644 --- a/src/test/java/graphql/annotations/connection/GraphQLConnectionTest.java +++ b/src/test/java/graphql/annotations/connection/GraphQLConnectionTest.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 @@ -24,10 +22,7 @@ import graphql.annotations.processor.exceptions.GraphQLAnnotationsException; import graphql.annotations.processor.util.CustomRelay; import graphql.relay.Relay; -import graphql.schema.DataFetchingEnvironment; -import graphql.schema.GraphQLList; -import graphql.schema.GraphQLObjectType; -import graphql.schema.GraphQLSchema; +import graphql.schema.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -162,7 +157,7 @@ public void customRelayMethodList() { graphql.schema.GraphQLObjectType f = (GraphQLObjectType) schema.getType("ObjConnection"); assertTrue(f.getFieldDefinitions().size() == 4); assertTrue(f.getFieldDefinition("nodes").getType() instanceof GraphQLList); - assertEquals(((GraphQLList) f.getFieldDefinition("nodes").getType()).getWrappedType().getName(), "Obj"); + assertEquals(((GraphQLNamedType) ((GraphQLList) f.getFieldDefinition("nodes").getType()).getWrappedType()).getName(), "Obj"); GraphQLObjectType pageInfo = (GraphQLObjectType) schema.getType("PageInfo"); assertTrue(pageInfo.getFieldDefinition("additionalInfo") != null); diff --git a/src/test/java/graphql/annotations/connection/GraphQLSimpleConnectionTest.java b/src/test/java/graphql/annotations/connection/GraphQLSimpleConnectionTest.java index 43008ec8..3f948332 100644 --- a/src/test/java/graphql/annotations/connection/GraphQLSimpleConnectionTest.java +++ b/src/test/java/graphql/annotations/connection/GraphQLSimpleConnectionTest.java @@ -23,6 +23,7 @@ import graphql.annotations.processor.GraphQLAnnotations; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; +import graphql.schema.GraphQLNamedType; import graphql.schema.GraphQLSchema; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -50,7 +51,7 @@ public void init() { public void simpleConnection_buildSchema_TypeOfSimpleConnectionIsGraphQLList() throws Exception { GraphQLSchema schema = newAnnotationsSchema().query(MainConnection.class).build(); - String objsTypeName = schema.getQueryType().getFieldDefinition("objs").getType().getName(); + String objsTypeName = ((GraphQLNamedType)schema.getQueryType().getFieldDefinition("objs").getType()).getName(); assertThat(objsTypeName, is("ObjChunk")); } diff --git a/src/test/java/graphql/annotations/directives/DirectiveSchemaVisitorTest.java b/src/test/java/graphql/annotations/directives/DirectiveSchemaVisitorTest.java new file mode 100644 index 00000000..0c5fd39f --- /dev/null +++ b/src/test/java/graphql/annotations/directives/DirectiveSchemaVisitorTest.java @@ -0,0 +1,161 @@ +/** + * 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.directives; + +import graphql.introspection.Introspection; +import graphql.schema.*; +import graphql.util.TraversalControl; +import graphql.util.TraverserContext; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; +import static org.testng.AssertJUnit.assertEquals; + +public class DirectiveSchemaVisitorTest { + private DirectiveSchemaVisitor directiveSchemaVisitor; + private GraphQLCodeRegistry.Builder codeRegistryBuilder; + private HashMap directiveWiringMap = new HashMap<>(); + private AnnotationsDirectiveWiring wiringMock; + private TreeTransformerUtilWrapper transformerUtilWrapper; + + @BeforeMethod + public void setUp() { + codeRegistryBuilder = mock(GraphQLCodeRegistry.Builder.class); + wiringMock = mock(AnnotationsDirectiveWiring.class); + transformerUtilWrapper = mock(TreeTransformerUtilWrapper.class); + directiveWiringMap.put("upper", wiringMock); + directiveWiringMap.put("suffix", wiringMock); + directiveWiringMap.put("noWiringMock", null); + directiveSchemaVisitor = new DirectiveSchemaVisitor(directiveWiringMap, codeRegistryBuilder, transformerUtilWrapper); + } + + + @Test + public void visitGraphQLArgument_hasDirectives_wiringFunctionIsCalledAndNodeChanged() { + // Arrange + GraphQLArgument argument = mock(GraphQLArgument.class); + List directivesOnType = new ArrayList<>(); + GraphQLDirective directiveMock = mock(GraphQLDirective.class); + when(directiveMock.validLocations()).thenReturn(EnumSet.of(Introspection.DirectiveLocation.ARGUMENT_DEFINITION)); + when(directiveMock.getName()).thenReturn("upper"); + directivesOnType.add(directiveMock); + TraverserContext context = mock(TraverserContext.class); + when(argument.getDirectives()).thenReturn(directivesOnType); + // Act + directiveSchemaVisitor.visitGraphQLArgument(argument, context); + + // Assert + verify(wiringMock).onArgument(any()); + verify(transformerUtilWrapper).changeNode(eq(context), any()); + } + + @Test + public void visitGraphQLArgument_noDirectives_returnsContinue() { + // Arrange + GraphQLArgument argument = mock(GraphQLArgument.class); + TraverserContext context = mock(TraverserContext.class); + when(argument.getDirectives()).thenReturn(new ArrayList<>()); + // Act + TraversalControl traversalControl = directiveSchemaVisitor.visitGraphQLArgument(argument, context); + + // Assert + verifyZeroInteractions(wiringMock); + verifyZeroInteractions(transformerUtilWrapper); + assertEquals(traversalControl, TraversalControl.CONTINUE); + } + + @Test + public void visitGraphQLArgument_noWiringFunction_changedToSameNode() { + // Arrange + GraphQLArgument argument = mock(GraphQLArgument.class); + List directivesOnType = new ArrayList<>(); + GraphQLDirective directiveMock = mock(GraphQLDirective.class); + when(directiveMock.validLocations()).thenReturn(EnumSet.of(Introspection.DirectiveLocation.ARGUMENT_DEFINITION)); + when(directiveMock.getName()).thenReturn("noWiringMock"); + directivesOnType.add(directiveMock); + TraverserContext context = mock(TraverserContext.class); + when(argument.getDirectives()).thenReturn(directivesOnType); + + // Act + directiveSchemaVisitor.visitGraphQLArgument(argument, context); + + // Assert + verifyZeroInteractions(wiringMock); + verify(transformerUtilWrapper).changeNode(eq(context), eq(argument)); + } + + //// + + @Test + public void visitGraphQLFieldDefinition_hasDirectives_wiringFunctionIsCalledAndNodeChanged() { + // Arrange + GraphQLFieldDefinition type = mock(GraphQLFieldDefinition.class); + List directivesOnType = new ArrayList<>(); + GraphQLDirective directiveMock = mock(GraphQLDirective.class); + when(directiveMock.validLocations()).thenReturn(EnumSet.of(Introspection.DirectiveLocation.FIELD_DEFINITION)); + when(directiveMock.getName()).thenReturn("upper"); + directivesOnType.add(directiveMock); + TraverserContext context = mock(TraverserContext.class); + when(type.getDirectives()).thenReturn(directivesOnType); + // Act + directiveSchemaVisitor.visitGraphQLFieldDefinition(type, context); + + // Assert + verify(wiringMock).onField(any()); + verify(transformerUtilWrapper).changeNode(eq(context), any()); + } + + @Test + public void visitGraphQLFieldDefinition_noDirectives_returnsContinue() { + // Arrange + GraphQLFieldDefinition type = mock(GraphQLFieldDefinition.class); + TraverserContext context = mock(TraverserContext.class); + when(type.getDirectives()).thenReturn(new ArrayList<>()); + // Act + TraversalControl traversalControl = directiveSchemaVisitor.visitGraphQLFieldDefinition(type, context); + + // Assert + verifyZeroInteractions(wiringMock); + verifyZeroInteractions(transformerUtilWrapper); + assertEquals(traversalControl, TraversalControl.CONTINUE); + } + + @Test + public void visitGraphQLFieldDefinition_noWiringFunction_changedToSameNode() { + // Arrange + GraphQLFieldDefinition type = mock(GraphQLFieldDefinition.class); + List directivesOnType = new ArrayList<>(); + GraphQLDirective directiveMock = mock(GraphQLDirective.class); + when(directiveMock.validLocations()).thenReturn(EnumSet.of(Introspection.DirectiveLocation.FIELD_DEFINITION)); + when(directiveMock.getName()).thenReturn("noWiringMock"); + directivesOnType.add(directiveMock); + TraverserContext context = mock(TraverserContext.class); + when(type.getDirectives()).thenReturn(directivesOnType); + + // Act + directiveSchemaVisitor.visitGraphQLFieldDefinition(type, context); + + // Assert + verifyZeroInteractions(wiringMock); + verify(transformerUtilWrapper).changeNode(eq(context), eq(type)); + } + +} diff --git a/src/test/java/graphql/annotations/directives/DirectiveWirerTest.java b/src/test/java/graphql/annotations/directives/DirectiveWirerTest.java deleted file mode 100644 index 937a0d67..00000000 --- a/src/test/java/graphql/annotations/directives/DirectiveWirerTest.java +++ /dev/null @@ -1,517 +0,0 @@ -/** - * 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 - * - * 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.directives; - -import graphql.TypeResolutionEnvironment; -import graphql.introspection.Introspection; -import graphql.schema.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import java.util.HashMap; - -import static graphql.Scalars.GraphQLString; -import static graphql.schema.GraphQLDirective.newDirective; -import static org.mockito.Mockito.*; - -public class DirectiveWirerTest { - - private DirectiveWirer directiveWirer; - private String parentName = "parent"; - private GraphQLCodeRegistry.Builder builder; - - @BeforeMethod - public void setUp() throws Exception { - directiveWirer = new DirectiveWirer(); - builder = mock(GraphQLCodeRegistry.Builder.class); - } - - // GraphQLFieldDefinition - - @Test - public void wireFieldDefinition_validLocations_correctMethodIsCalled() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - AnnotationsDirectiveWiring lowerWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLFieldDefinition directiveContainer = GraphQLFieldDefinition.newFieldDefinition().name("bla") - .type(GraphQLString).build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - AnnotationsWiringEnvironmentImpl lowerCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("lowerCase"), parentName, builder); - - when(upperWiring.onField(upperCaseEnv)).thenReturn(directiveContainer); - when(lowerWiring.onField(lowerCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.FIELD_DEFINITION).build(); - GraphQLDirective lowerCase = newDirective().name("lowerCase").validLocations(Introspection.DirectiveLocation.FIELD).build(); - map.put(upperCase, upperWiring); - map.put(lowerCase, lowerWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onField(upperCaseEnv); - verify(lowerWiring).onField(lowerCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireFieldDefinition_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLFieldDefinition directiveContainer = GraphQLFieldDefinition.newFieldDefinition().name("bla") - .type(GraphQLString).build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onField(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.ENUM).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - - // GraphQLObjectType - - @Test - public void wireGraphQLObjectType_validLocations_correctMethodIsCalled() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLObjectType directiveContainer = GraphQLObjectType.newObject().name("asdf").build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onObject(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.OBJECT).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onObject(upperCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireGraphQLObjectType_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLObjectType directiveContainer = GraphQLObjectType.newObject().name("asdf00").build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onObject(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - - // GraphQLArgument - - @Test - public void wireGraphQLArgument_validLocations_correctMethodIsCalled() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLArgument directiveContainer = GraphQLArgument.newArgument().name("asdf").type(GraphQLString).build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onArgument(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.ARGUMENT_DEFINITION).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onArgument(upperCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireGraphQLArgument_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLArgument directiveContainer = GraphQLArgument.newArgument().name("asdf0").type(GraphQLString).build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onArgument(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - - // GraphQLInterfaceType - - @Test - public void wireGraphQLInterfaceType_validLocations_correctMethodIsCalled() { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLInterfaceType directiveContainer = GraphQLInterfaceType.newInterface().name("asdf").build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onInterface(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.INTERFACE).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onInterface(upperCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireGraphQLInterfaceType_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLInterfaceType directiveContainer = GraphQLInterfaceType.newInterface().name("asdf").build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onInterface(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - - // GraphQLUnionType - - @Test - public void wireGraphQLUnionType_validLocations_correctMethodIsCalled() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLUnionType directiveContainer = GraphQLUnionType.newUnionType().name("asdf") - .possibleType(GraphQLObjectType.newObject().name("Asdfaaaa").build()).build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onUnion(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.UNION).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onUnion(upperCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireGraphQLUnionType_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLUnionType directiveContainer = GraphQLUnionType.newUnionType().name("asdf") - .possibleType(GraphQLObjectType.newObject().name("Asdfaaaa").build()).build(); - - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onUnion(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - - // GraphQLEnumType - - @Test - public void wireGraphQLEnumType_validLocations_correctMethodIsCalled() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLEnumType directiveContainer = GraphQLEnumType.newEnum().name("asdf").value("asdfasdf").build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onEnum(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.ENUM).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onEnum(upperCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireGraphQLEnumType_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLEnumType directiveContainer = GraphQLEnumType.newEnum().name("asdf").value("asdfasdf").build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onEnum(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - - // GraphQLEnumValueDefinition - - @Test - public void wireGraphQLEnumValueDefinition_validLocations_correctMethodIsCalled() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLEnumValueDefinition directiveContainer = GraphQLEnumValueDefinition.newEnumValueDefinition().name("asdf").build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onEnumValue(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.ENUM_VALUE).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onEnumValue(upperCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireGraphQLEnumValueDefinition_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLEnumValueDefinition directiveContainer = GraphQLEnumValueDefinition.newEnumValueDefinition().name("asdf").build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onEnumValue(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - - // GraphQLScalarType - - @Test - public void wireGraphQLScalarType_validLocations_correctMethodIsCalled() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLScalarType directiveContainer = GraphQLString; - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onScalar(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.SCALAR).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onScalar(upperCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireGraphQLScalarType_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLScalarType directiveContainer = GraphQLString; - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onScalar(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - - // GraphQLInputObjectType - - @Test - public void wireGraphQLInputObjectType_validLocations_correctMethodIsCalled() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLInputObjectType directiveContainer = GraphQLInputObjectType.newInputObject().name("asdf") - .build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onInputObjectType(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.INPUT_OBJECT).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onInputObjectType(upperCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireGraphQLInputObjectType_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLInputObjectType directiveContainer = GraphQLInputObjectType.newInputObject().name("asdf") - .build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onInputObjectType(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - - // GraphQLInputObjectField - - @Test - public void wireGraphQLInputObjectField_validLocations_correctMethodIsCalled() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLInputObjectField directiveContainer = GraphQLInputObjectField.newInputObjectField().name("asdf") - .type(GraphQLInputObjectType.newInputObject().name("dfdf").build()).build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl( - directiveContainer, directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onInputObjectField(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").validLocations(Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION).build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - - // Assert - - verify(upperWiring).onInputObjectField(upperCaseEnv); - } - - @Test(expectedExceptions = InvalidDirectiveLocationException.class) - public void wireGraphQLInputObjectField_invalidLocations_exceptionIsThrown() throws Exception { - // Arrange - AnnotationsDirectiveWiring upperWiring = mock(AnnotationsDirectiveWiring.class); - - GraphQLInputObjectField directiveContainer = GraphQLInputObjectField.newInputObjectField().name("asdf") - .type(GraphQLInputObjectType.newInputObject().name("dfdf").build()).build(); - - AnnotationsWiringEnvironmentImpl upperCaseEnv = new AnnotationsWiringEnvironmentImpl(directiveContainer, - directiveContainer.getDirective("upperCase"), parentName, builder); - - when(upperWiring.onInputObjectField(upperCaseEnv)).thenReturn(directiveContainer); - - HashMap map = new HashMap<>(); - GraphQLDirective upperCase = newDirective().name("upperCase").build(); - map.put(upperCase, upperWiring); - - // Act - directiveWirer.wire(directiveContainer, map, builder, parentName); - } - -}