From cb3d2bca626804f0dcf0db737dc6880ee6f560b5 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Wed, 1 Mar 2023 15:24:17 +1300 Subject: [PATCH 001/112] big refactor. Cleaned up libraries and added oneOf support --- pom.xml | 401 +++++----- .../fleetpin/graphql/builder/Authorizer.java | 4 +- .../graphql/builder/AuthorizerSchema.java | 183 ++--- .../graphql/builder/DirectiveCaller.java | 6 +- .../graphql/builder/DirectiveOperation.java | 9 +- .../graphql/builder/DirectivesSchema.java | 206 ++--- .../graphql/builder/DurationCoercing.java | 22 +- .../graphql/builder/EntityHolder.java | 221 ++++++ .../graphql/builder/EntityProcessor.java | 728 ++++++------------ .../fleetpin/graphql/builder/EntityUtil.java | 66 ++ .../fleetpin/graphql/builder/EnumEntity.java | 57 ++ .../graphql/builder/GraphqlLongCoercing.java | 160 ++-- .../graphql/builder/InputBuilder.java | 259 +++++++ .../graphql/builder/InstantCoercing.java | 28 +- .../graphql/builder/LocalDateCoercing.java | 22 +- .../builder/LocalDateTimeCoercing.java | 22 +- .../graphql/builder/MonthDayCoercing.java | 70 +- .../graphql/builder/ObjectEntity.java | 53 ++ .../graphql/builder/RestrictType.java | 28 +- .../graphql/builder/RestrictTypeFactory.java | 9 +- .../graphql/builder/SDLDirective.java | 9 +- .../graphql/builder/SDLProcessor.java | 256 +++--- .../graphql/builder/ScalarEntity.java | 59 ++ .../graphql/builder/SchemaBuilder.java | 482 ++++-------- .../graphql/builder/SchemaConfiguration.java | 16 +- .../fleetpin/graphql/builder/TypeBuilder.java | 350 +++++++++ .../fleetpin/graphql/builder/TypeMeta.java | 244 +++--- .../fleetpin/graphql/builder/UnionType.java | 72 ++ .../graphql/builder/YearMonthCoercing.java | 70 +- .../graphql/builder/ZoneIdCoercing.java | 22 +- .../graphql/builder/annotations/Context.java | 1 - .../builder/annotations/Directive.java | 4 +- .../graphql/builder/annotations/Entity.java | 1 - .../annotations/GraphQLDeprecated.java | 2 +- .../annotations/GraphQLDescription.java | 2 +- .../builder/annotations/GraphQLIgnore.java | 4 +- .../graphql/builder/annotations/Id.java | 4 +- .../builder/annotations/InputIgnore.java | 4 +- .../graphql/builder/annotations/Mutation.java | 1 - .../graphql/builder/annotations/OneOf.java | 33 + .../graphql/builder/annotations/Query.java | 1 - .../graphql/builder/annotations/Restrict.java | 4 +- .../builder/annotations/Restricts.java | 26 +- .../graphql/builder/annotations/Scalar.java | 4 +- .../builder/annotations/SchemaOption.java | 4 +- .../builder/annotations/Subscription.java | 1 - .../graphql/builder/annotations/Union.java | 25 + .../builder/mapper/InputTypeBuilder.java | 8 + .../builder/mapper/ObjectFieldBuilder.java | 75 ++ .../graphql/builder/mapper/OneOfBuilder.java | 46 ++ .../builder/mapper/RecordFieldBuilder.java | 63 ++ .../CompletionStageMappingPublisher.java | 114 --- .../graphql/builder/DirectiveTest.java | 64 +- .../fleetpin/graphql/builder/MetaTest.java | 14 +- .../graphql/builder/ParameterParsingTest.java | 92 +-- .../builder/ParameterTypeParsingTest.java | 106 +-- .../graphql/builder/PublishRestrictions.java | 11 +- .../builder/TypeGenericInputParsingTest.java | 400 +++++----- .../builder/TypeGenericParsingTest.java | 622 +++++++-------- .../builder/TypeInheritanceParsingTest.java | 558 ++++++++------ .../graphql/builder/TypeParsingTest.java | 467 +++++------ .../fleetpin/graphql/builder/UnionTest.java | 74 ++ .../graphql/builder/generics/Animal.java | 125 ++- .../graphql/builder/generics/Cat.java | 35 +- .../graphql/builder/generics/CatFamily.java | 23 +- .../builder/generics/CatFamilyFur.java | 23 +- .../graphql/builder/generics/CatFur.java | 22 +- .../graphql/builder/generics/Dog.java | 42 +- .../graphql/builder/generics/DogFur.java | 30 +- .../graphql/builder/generics/Fur.java | 22 +- .../graphql/builder/inputgenerics/Animal.java | 37 +- .../builder/inputgenerics/AnimalInput.java | 54 +- .../inputgenerics/AnimalOuterWrapper.java | 54 +- .../builder/inputgenerics/AnimalWrapper.java | 52 +- .../graphql/builder/inputgenerics/Cat.java | 90 ++- .../builder/inputgenerics/CatAnimalInput.java | 16 +- .../graphql/builder/parameter/Parameter.java | 23 +- .../builder/parameter/TypeInputParameter.java | 21 +- .../builder/publishRestrictions/Test.java | 20 +- .../restrictions/EntityRestrictions.java | 8 +- .../restrictions/RestrictionTypesTest.java | 77 +- .../parameter/RestrictedEntity.java | 46 +- .../graphql/builder/type/Circular.java | 36 +- .../builder/type/DeprecatedObject.java | 5 +- .../builder/type/DescriptionObject.java | 5 +- .../graphql/builder/type/SimpleType.java | 147 ++-- .../graphql/builder/type/UnionType.java | 33 + .../builder/type/directive/Capture.java | 69 +- .../builder/type/directive/CaptureType.java | 37 +- .../graphql/builder/type/directive/Cat.java | 55 +- .../builder/type/directive/CatSchema.java | 14 +- .../builder/type/inheritance/Animal.java | 54 +- .../graphql/builder/type/inheritance/Cat.java | 81 +- .../graphql/builder/type/inheritance/Dog.java | 53 +- 94 files changed, 4669 insertions(+), 3709 deletions(-) create mode 100644 src/main/java/com/fleetpin/graphql/builder/EntityHolder.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/EntityUtil.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/EnumEntity.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/InputBuilder.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/UnionType.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/annotations/Union.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java delete mode 100644 src/main/java/graphql/execution/reactive/CompletionStageMappingPublisher.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/UnionTest.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/type/UnionType.java diff --git a/pom.xml b/pom.xml index 491e29d..2452094 100644 --- a/pom.xml +++ b/pom.xml @@ -1,179 +1,222 @@ - - 4.0.0 - com.fleetpin - graphql-builder - 1.0.2-SNAPSHOT - - GraphQL Builder - Builds a graphql schema from a model using reflection - https://github.com/fleetpin/graphql-builder - - - 5.6.0 - UTF-8 - - - - - sonatype - central snapshot - https://oss.sonatype.org/content/repositories/snapshots - - - sonatype - central release - https://oss.sonatype.org/service/local/staging/deploy/maven2 - - - - - https://github.com/fleetpin/graphql-builder - scm:git:https://github.com/fleetpin/graphql-builder.git - scm:git:https://github.com/fleetpin/graphql-builder.git - HEAD - - - - - Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - - Ashley Taylor - ashley.taylor@fleetpin.co.nz - Fleetpin - http://www.fleetpin.co.nz - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - -parameters - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - - - - - - com.graphql-java - graphql-java - 18.1 - - - org.reflections - reflections - 0.9.11 - - - com.fasterxml.jackson.core - jackson-databind - 2.12.6.1 - - - com.fasterxml.jackson.module - jackson-module-parameter-names - 2.10.1 - - - - com.fasterxml.jackson.datatype - jackson-datatype-jdk8 - 2.10.1 - - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - 2.10.1 - - - io.reactivex.rxjava3 - rxjava - 3.0.0-RC7 - - - - org.junit.jupiter - junit-jupiter - ${junit.jupiter.version} - test - - - - - - - sonatype - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.1 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - sonatype - https://oss.sonatype.org/ - true - - - - - - - + + 4.0.0 + com.fleetpin + graphql-builder + 1.0.2-SNAPSHOT + + GraphQL Builder + Builds a graphql schema from a model using reflection + https://github.com/fleetpin/graphql-builder + + + 5.6.0 + UTF-8 + 2.14.2 + 1.9.0 + 1.0.0 + + + + + sonatype + central snapshot + https://oss.sonatype.org/content/repositories/snapshots + + + sonatype + central release + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + + + https://github.com/fleetpin/graphql-builder + scm:git:https://github.com/fleetpin/graphql-builder.git + scm:git:https://github.com/fleetpin/graphql-builder.git + HEAD + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + Ashley Taylor + ashley.taylor@fleetpin.co.nz + Fleetpin + http://www.fleetpin.co.nz + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + -parameters + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + + com.hubspot.maven.plugins + prettier-maven-plugin + 0.15 + + 1.4.0 + 160 + 4 + true + true + true + + + + validate + + + + + org.pitest + pitest-maven + ${pitest.version} + + + org.pitest + pitest-junit5-plugin + ${pitest-junit5-plugin.version} + + + + 4 + false + false + + STRONGER + + + + + + + + + com.graphql-java + graphql-java + 20.0 + + + org.reflections + reflections + 0.10.2 + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + + com.fasterxml.jackson.module + jackson-module-parameter-names + ${jackson.version} + test + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + ${jackson.version} + test + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + test + + + io.reactivex.rxjava3 + rxjava + 3.1.6 + + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + + + + + sonatype + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + sonatype + https://oss.sonatype.org/ + true + + + + + + + diff --git a/src/main/java/com/fleetpin/graphql/builder/Authorizer.java b/src/main/java/com/fleetpin/graphql/builder/Authorizer.java index 87bf50a..9b85eb5 100644 --- a/src/main/java/com/fleetpin/graphql/builder/Authorizer.java +++ b/src/main/java/com/fleetpin/graphql/builder/Authorizer.java @@ -12,6 +12,4 @@ package com.fleetpin.graphql.builder; -public interface Authorizer { - -} +public interface Authorizer {} diff --git a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java index dc7fedc..225949c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java @@ -12,6 +12,7 @@ package com.fleetpin.graphql.builder; +import graphql.schema.DataFetcher; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; @@ -24,12 +25,8 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; -import graphql.schema.DataFetcher; - public class AuthorizerSchema { - - private final Set basePackages; private final Map targets; @@ -41,7 +38,7 @@ private AuthorizerSchema(Set basePackages, Map targe public static AuthorizerSchema build(Set basePackage, Set> authorizers) throws ReflectiveOperationException { Map targets = new HashMap<>(); - for(var type: authorizers) { + for (var type : authorizers) { Authorizer auth = type.getDeclaredConstructor().newInstance(); targets.put(type.getPackageName(), auth); } @@ -51,17 +48,17 @@ public static AuthorizerSchema build(Set basePackage, Set wrap(DataFetcher fetcher, Method method) { Authorizer wrapper = getAuthorizer(method.getDeclaringClass()); - if(wrapper == null) { - return fetcher; + if (wrapper == null) { + return fetcher; } Set parameterNames = new HashSet<>(); - - for(var parameter: method.getParameters()) { + + for (var parameter : method.getParameters()) { parameterNames.add(parameter.getName()); } - - + int longest = 0; - + Method[] targets = wrapper.getClass().getMethods(); - for(Method target: targets) { + for (Method target : targets) { boolean valid = false; valid |= target.getReturnType() == Boolean.TYPE; - - - if(target.getReturnType().isAssignableFrom(CompletableFuture.class)) { + + if (target.getReturnType().isAssignableFrom(CompletableFuture.class)) { Type genericType = ((ParameterizedType) target.getGenericReturnType()).getActualTypeArguments()[0]; - if(Boolean.class.isAssignableFrom((Class) genericType)) { + if (Boolean.class.isAssignableFrom((Class) genericType)) { valid = true; } } - if(!valid) { + if (!valid) { continue; } - if(target.getDeclaringClass().equals(Object.class)) { + if (target.getDeclaringClass().equals(Object.class)) { continue; } int matched = 0; - for(var parameter: target.getParameters()) { - if(parameterNames.contains(parameter.getName())) { + for (var parameter : target.getParameters()) { + if (parameterNames.contains(parameter.getName())) { matched++; } } - if(matched > longest) { + if (matched > longest) { longest = matched; } } - + List toRun = new ArrayList<>(); - for(Method target: targets) { - - + for (Method target : targets) { boolean valid = false; valid |= target.getReturnType() == Boolean.TYPE; - - - if(target.getReturnType().isAssignableFrom(CompletableFuture.class)) { + + if (target.getReturnType().isAssignableFrom(CompletableFuture.class)) { Type genericType = ((ParameterizedType) target.getGenericReturnType()).getActualTypeArguments()[0]; - if(Boolean.class.isAssignableFrom((Class) genericType)) { + if (Boolean.class.isAssignableFrom((Class) genericType)) { valid = true; } } - if(!valid) { + if (!valid) { continue; } - if(target.getDeclaringClass().equals(Object.class)) { + if (target.getDeclaringClass().equals(Object.class)) { continue; } - + int matched = 0; - for(var parameter: target.getParameters()) { - if(parameterNames.contains(parameter.getName())) { + for (var parameter : target.getParameters()) { + if (parameterNames.contains(parameter.getName())) { matched++; } } - if(matched == longest) { + if (matched == longest) { toRun.add(target); } } - - if(toRun.isEmpty()) { + + if (toRun.isEmpty()) { throw new RuntimeException("No authorizer found for " + method); } - - - - - - - + return env -> { - for(Method authorizer: toRun) { + for (Method authorizer : toRun) { Object[] args = new Object[authorizer.getParameterCount()]; - - for(int i = 0; i < args.length; i++) { - if(authorizer.getParameterTypes()[i].isAssignableFrom(env.getClass())) { + + for (int i = 0; i < args.length; i++) { + if (authorizer.getParameterTypes()[i].isAssignableFrom(env.getClass())) { args[i] = env; - }else if(authorizer.getParameterTypes()[i].isAssignableFrom(env.getContext().getClass())) { + } else if (authorizer.getParameterTypes()[i].isAssignableFrom(env.getContext().getClass())) { args[i] = env.getContext(); - }else { + } else { args[i] = env.getArgument(authorizer.getParameters()[i].getName()); } } try { Object allow = authorizer.invoke(wrapper, args); - if(allow instanceof Boolean) { - if((Boolean) allow) { - return fetcher.get(env); - }else { - throw new RuntimeException("Invalid access"); + if (allow instanceof Boolean) { + if ((Boolean) allow) { + return fetcher.get(env); + } else { + throw new RuntimeException("Invalid access"); } - }else { + } else { //only other type that passes checks above CompletableFuture allowed = (CompletableFuture) allow; - - return allowed.handle((r, e) -> { - if(e != null) { - if(e.getCause() instanceof Exception) { - e = e.getCause(); - } - if(e instanceof RuntimeException) { - throw (RuntimeException) e; - } - throw new RuntimeException(e); - } - if(r) { - try { - return fetcher.get(env); - } catch (Throwable e1) { - if(e1.getCause() instanceof Exception) { - e1 = e1.getCause(); + + return allowed + .handle((r, e) -> { + if (e != null) { + if (e.getCause() instanceof Exception) { + e = e.getCause(); } - if(e1 instanceof RuntimeException) { - throw (RuntimeException) e1; + if (e instanceof RuntimeException) { + throw (RuntimeException) e; } - throw new RuntimeException(e1); - } - }else { - throw new RuntimeException("Invalid access"); - } - }).thenCompose(a -> { - if(a instanceof CompletableFuture) { - return (CompletableFuture) a; - }else { - return CompletableFuture.completedFuture(a); - } - - }); + throw new RuntimeException(e); + } + if (r) { + try { + return fetcher.get(env); + } catch (Throwable e1) { + if (e1.getCause() instanceof Exception) { + e1 = e1.getCause(); + } + if (e1 instanceof RuntimeException) { + throw (RuntimeException) e1; + } + throw new RuntimeException(e1); + } + } else { + throw new RuntimeException("Invalid access"); + } + }) + .thenCompose(a -> { + if (a instanceof CompletableFuture) { + return (CompletableFuture) a; + } else { + return CompletableFuture.completedFuture(a); + } + }); } - }catch (InvocationTargetException e) { - if(e.getCause() instanceof Exception) { + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Exception) { throw (Exception) e.getCause(); - }else { + } else { throw e; } } } return fetcher.get(env); - }; } - } diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java index 9ad61e9..4881964 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java @@ -12,12 +12,10 @@ package com.fleetpin.graphql.builder; -import java.lang.annotation.Annotation; - import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; +import java.lang.annotation.Annotation; -public interface DirectiveCaller extends DirectiveOperation{ +public interface DirectiveCaller extends DirectiveOperation { public Object process(T annotation, DataFetchingEnvironment env, DataFetcher fetcher) throws Exception; - } diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java index 7504649..0493f0b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java @@ -14,14 +14,11 @@ import java.lang.annotation.Annotation; - /** - * Implementations are either + * Implementations are either * DirectiveOperator is used to wrap a method call and modify it. Can be used for things like restrictions * SchemaDirective is used to add directive information to the graphql schema - * + * * */ -public interface DirectiveOperation { - -} +public interface DirectiveOperation {} diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java index 266ab44..177585c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java @@ -1,5 +1,11 @@ package com.fleetpin.graphql.builder; +import com.fleetpin.graphql.builder.annotations.Directive; +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import graphql.schema.GraphQLAppliedDirective; +import graphql.schema.GraphQLDirective; +import io.reactivex.rxjava3.core.Flowable; /* * 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 @@ -12,8 +18,6 @@ * the License. */ - - import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; @@ -29,20 +33,13 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Flow; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; - +import org.reactivestreams.FlowAdapters; import org.reactivestreams.Publisher; -import com.fleetpin.graphql.builder.annotations.Directive; - -import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; -import graphql.schema.GraphQLAppliedDirective; -import graphql.schema.GraphQLDirective; -import io.reactivex.rxjava3.core.Flowable; - class DirectivesSchema { private final Collection> global; @@ -50,177 +47,194 @@ class DirectivesSchema { private final Map, SDLDirective> schemaDirective; private Map, SDLProcessor> sdlProcessors; - private DirectivesSchema(Collection> global, Map, DirectiveCaller> targets, Map, SDLDirective> schemaDirective) { + private DirectivesSchema( + Collection> global, + Map, DirectiveCaller> targets, + Map, SDLDirective> schemaDirective + ) { this.global = global; this.targets = targets; this.schemaDirective = schemaDirective; } + //TODO:mess of exceptions public static DirectivesSchema build(List> globalDirectives, Set> dierctiveTypes) throws ReflectiveOperationException { Map, DirectiveCaller> targets = new HashMap<>(); Map, SDLDirective> graphqlDirective = new HashMap<>(); - for(Class directiveType: dierctiveTypes) { - if(!directiveType.isAnnotationPresent(Directive.class)) { + for (Class directiveType : dierctiveTypes) { + if (!directiveType.isAnnotationPresent(Directive.class)) { continue; } - if(!directiveType.isAnnotation()) { + if (!directiveType.isAnnotation()) { //TODO:better error management throw new RuntimeException("@Directive Annotation must only be placed on annotations"); } - + var directive = directiveType.getAnnotation(Directive.class); Class> caller = directive.value(); //TODO: if target implents other things this won't lineup right Class target = (Class) ((ParameterizedType) caller.getGenericInterfaces()[0]).getActualTypeArguments()[0]; - if(!target.equals(directiveType)) { + if (!target.equals(directiveType)) { //TODO:better errors - throw new RuntimeException("Annotation missmatch"); + throw new RuntimeException("Annotation missmatch"); } - - - if(DirectiveCaller.class.isAssignableFrom(caller)) { + + if (DirectiveCaller.class.isAssignableFrom(caller)) { //TODO error for no zero args constructor var callerInstance = (DirectiveCaller) caller.getConstructor().newInstance(); targets.put((Class) directiveType, callerInstance); - }else { + } else { var callerInstance = (SDLDirective) caller.getConstructor().newInstance(); graphqlDirective.put((Class) directiveType, callerInstance); } } - + return new DirectivesSchema(globalDirectives, targets, graphqlDirective); } + private DirectiveCaller get(Annotation annotation) { return targets.get(annotation.annotationType()); } + private DataFetcher wrap(DirectiveCaller directive, T annotation, DataFetcher fetcher) { return env -> { return directive.process(annotation, env, fetcher); }; } - + public Stream getSchemaDirective() { return sdlProcessors.values().stream().map(f -> f.getDirective()); } - + private DataFetcher wrap(RestrictTypeFactory directive, DataFetcher fetcher) { //TODO: hate having this cache here would love to scope against the env object but nothing to hook into dataload caused global leak Map> cache = Collections.synchronizedMap(new WeakHashMap<>()); - - + return env -> { - return cache.computeIfAbsent(env, key -> directive.create(key).thenApply(t -> t)).thenCompose(restrict -> { - try { - Object response = fetcher.get(env); - if(response instanceof CompletionStage) { - return ((CompletionStage) response).thenCompose(r -> applyRestrict(restrict, r )); - } - return applyRestrict(restrict, response); - } catch (Exception e) { - if(e instanceof RuntimeException) { - throw (RuntimeException) e; + return cache + .computeIfAbsent(env, key -> directive.create(key).thenApply(t -> t)) + .thenCompose(restrict -> { + try { + Object response = fetcher.get(env); + if (response instanceof CompletionStage) { + return ((CompletionStage) response).thenCompose(r -> applyRestrict(restrict, r)); + } + return applyRestrict(restrict, response); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + throw new RuntimeException(e); } - throw new RuntimeException(e); - } - }); + }); }; } public boolean target(Method method, TypeMeta meta) { - - for(var global: this.global) { + for (var global : this.global) { //TODO: extract class - if(global.extractType().isAssignableFrom(meta.getType())) { + if (global.extractType().isAssignableFrom(meta.getType())) { return true; } } - for(Annotation annotation: method.getAnnotations()) { - if(get(annotation) != null) { + for (Annotation annotation : method.getAnnotations()) { + if (get(annotation) != null) { return true; } } return false; } - public DataFetcher wrap(Method method, TypeMeta meta, DataFetcher fetcher) { - for(var g: global) { - if(g.extractType().isAssignableFrom(meta.getType())) { + + public DataFetcher wrap(Method method, TypeMeta meta, DataFetcher fetcher) { + for (var g : global) { + if (g.extractType().isAssignableFrom(meta.getType())) { fetcher = wrap(g, fetcher); } } - for(Annotation annotation: method.getAnnotations()) { + for (Annotation annotation : method.getAnnotations()) { DirectiveCaller directive = (DirectiveCaller) get(annotation); - if(directive != null) { - fetcher = wrap(directive, annotation, fetcher); + if (directive != null) { + fetcher = wrap(directive, annotation, fetcher); } } return fetcher; } - + private CompletableFuture applyRestrict(RestrictType restrict, Object response) { - if(response instanceof List) { - return restrict.filter((List)response); - }else if(response instanceof Publisher) { - return CompletableFuture.completedFuture(Flowable.fromPublisher((Publisher) response).flatMap(entry -> { - return Flowable.fromCompletionStage(restrict.allow(entry)).filter(t -> t == Boolean.TRUE).map(t -> entry); - })); - }else if(response instanceof Optional) { + if (response instanceof List) { + return restrict.filter((List) response); + } else if (response instanceof Publisher) { + return CompletableFuture.completedFuture( + Flowable + .fromPublisher((Publisher) response) + .flatMap(entry -> { + return Flowable.fromCompletionStage(restrict.allow(entry)).filter(t -> t == Boolean.TRUE).map(t -> entry); + }) + ); + } else if (response instanceof Optional) { var optional = (Optional) response; - if(optional.isEmpty()) { + if (optional.isEmpty()) { return CompletableFuture.completedFuture(response); } var target = optional.get(); - if(target instanceof List) { - return restrict.filter((List)target); - }else { - return restrict.allow(target).thenApply(allow -> { - if(allow == Boolean.TRUE) { + if (target instanceof List) { + return restrict.filter((List) target); + } else { + return restrict + .allow(target) + .thenApply(allow -> { + if (allow == Boolean.TRUE) { + return response; + } else { + return Optional.empty(); + } + }); + } + } else { + return restrict + .allow(response) + .thenApply(allow -> { + if (allow == Boolean.TRUE) { return response; - }else { - return Optional.empty(); + } else { + return null; } }); - } - }else { - return restrict.allow(response).thenApply(allow -> { - if(allow == Boolean.TRUE) { - return response; - }else { - return null; - } - }); } } - + private static CompletableFuture> all(List> toReturn) { - return CompletableFuture.allOf(toReturn.toArray(CompletableFuture[]::new)) - .thenApply(__ -> toReturn.stream().map(m -> { - try { - return m.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - }).collect(Collectors.toList())); + return CompletableFuture + .allOf(toReturn.toArray(CompletableFuture[]::new)) + .thenApply(__ -> + toReturn + .stream() + .map(m -> { + try { + return m.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.toList()) + ); } - public void addSchemaDirective(AnnotatedElement element, Class location, Consumer builder) { - for(Annotation annotation: element.getAnnotations()) { - var processor = this.sdlProcessors.get(annotation.annotationType()); - if(processor != null) { + for (Annotation annotation : element.getAnnotations()) { + var processor = this.sdlProcessors.get(annotation.annotationType()); + if (processor != null) { processor.apply(annotation, location, builder); } } - } + public void processSDL(EntityProcessor entityProcessor) { - Map, SDLProcessor> sdlProcessors = new HashMap<>(); - + this.schemaDirective.forEach((k, v) -> { - sdlProcessors.put(k, SDLProcessor.build(entityProcessor, v)); - }); + sdlProcessors.put(k, SDLProcessor.build(entityProcessor, v)); + }); this.sdlProcessors = sdlProcessors; - } - } diff --git a/src/main/java/com/fleetpin/graphql/builder/DurationCoercing.java b/src/main/java/com/fleetpin/graphql/builder/DurationCoercing.java index f6391ca..1e2c62e 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DurationCoercing.java +++ b/src/main/java/com/fleetpin/graphql/builder/DurationCoercing.java @@ -12,12 +12,11 @@ package com.fleetpin.graphql.builder; -import java.time.Duration; - import graphql.schema.Coercing; import graphql.schema.CoercingParseLiteralException; import graphql.schema.CoercingParseValueException; import graphql.schema.CoercingSerializeException; +import java.time.Duration; public class DurationCoercing implements Coercing { @@ -35,16 +34,13 @@ public Duration parseValue(Object input) throws CoercingParseValueException { public Duration parseLiteral(Object input) throws CoercingParseLiteralException { return convertImpl(input); } - - - private Duration convertImpl(Object input) { - if (input instanceof Duration) { - return (Duration) input; - } else if (input instanceof String) { - return Duration.parse((String) input); - } - return null; - } - + private Duration convertImpl(Object input) { + if (input instanceof Duration) { + return (Duration) input; + } else if (input instanceof String) { + return Duration.parse((String) input); + } + return null; + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java new file mode 100644 index 0000000..c8692d9 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java @@ -0,0 +1,221 @@ +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.TypeMeta.Flag; +import com.fleetpin.graphql.builder.annotations.Id; +import com.fleetpin.graphql.builder.annotations.Union; +import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; +import graphql.Scalars; +import graphql.schema.GraphQLInputType; +import graphql.schema.GraphQLList; +import graphql.schema.GraphQLNamedInputType; +import graphql.schema.GraphQLNamedOutputType; +import graphql.schema.GraphQLNamedType; +import graphql.schema.GraphQLNonNull; +import graphql.schema.GraphQLOutputType; +import graphql.schema.GraphQLTypeReference; +import graphql.schema.GraphQLUnionType; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public abstract class EntityHolder { + + private GraphQLNamedOutputType type; + private GraphQLNamedInputType inputType; + private InputTypeBuilder resolver; + + final GraphQLOutputType getType(TypeMeta meta, Annotation[] annotations) { + if (type == null) { + type = new GraphQLTypeReference(EntityUtil.getName(meta)); + type = buildType(); + } + GraphQLOutputType toReturn = getTypeInner(annotations); + return toType(meta, toReturn); + } + + private GraphQLOutputType toType(TypeMeta meta, GraphQLOutputType toReturn) { + boolean required = true; + for (var flag : meta.getFlags()) { + if (flag == Flag.OPTIONAL) { + required = false; + } + if (flag == Flag.ARRAY) { + if (required) { + toReturn = GraphQLNonNull.nonNull(toReturn); + } + toReturn = GraphQLList.list(toReturn); + required = true; + } + } + if (required) { + toReturn = GraphQLNonNull.nonNull(toReturn); + } + return toReturn; + } + + public final GraphQLNamedOutputType getInnerType(TypeMeta meta) { + if (type == null) { + type = new GraphQLTypeReference(EntityUtil.getName(meta)); + type = buildType(); + } + return type; + } + + private GraphQLNamedOutputType getTypeInner(Annotation[] annotations) { + if (annotations == null) { + return type; + } + for (Annotation an : annotations) { + if (an.annotationType().equals(Id.class)) { + return Scalars.GraphQLID; + } + } + return type; + } + + protected abstract GraphQLNamedOutputType buildType(); + + public final GraphQLInputType getInputType(TypeMeta meta, Annotation[] annotations) { + if (inputType == null) { + inputType = new GraphQLTypeReference(buildInputName()); + inputType = buildInput(); + } + GraphQLInputType toReturn = getInputTypeInner(annotations); + + boolean required = true; + for (var flag : meta.getFlags()) { + if (flag == Flag.OPTIONAL) { + required = false; + } + if (flag == Flag.ARRAY) { + if (required) { + toReturn = GraphQLNonNull.nonNull(toReturn); + } + toReturn = GraphQLList.list(toReturn); + required = true; + } + } + if (required) { + toReturn = GraphQLNonNull.nonNull(toReturn); + } + return toReturn; + } + + private GraphQLInputType getInputTypeInner(Annotation[] annotations) { + for (Annotation an : annotations) { + if (an.annotationType().equals(Id.class)) { + return Scalars.GraphQLID; + } + } + return inputType; + } + + protected abstract GraphQLNamedInputType buildInput(); + + protected abstract String buildInputName(); + + public Stream types() { + List types = new ArrayList<>(2); + if (type != null) { + types.add(type); + } + if (inputType != null && inputType != type) { + types.add(inputType); + } + return types.stream(); + } + + protected abstract InputTypeBuilder buildResolver(); + + public final InputTypeBuilder getResolver(TypeMeta meta) { + if (resolver == null) { + resolver = resolverPointer(); + resolver = buildResolver(); + } + return process(meta.getTypes().iterator(), resolver); + } + + private InputTypeBuilder resolverPointer() { + return (obj, graphQLContext, locale) -> this.resolver.convert(obj, graphQLContext, locale); + } + + private static InputTypeBuilder process(Iterator> iterator, InputTypeBuilder resolver) { + if (iterator.hasNext()) { + var type = iterator.next(); + + if (List.class.isAssignableFrom(type)) { + return processList(iterator, resolver); + } + if (Optional.class.isAssignableFrom(type)) { + return processOptional(iterator, resolver); + } + // if(type.isArray()) { + // return processArray(iterator); + // } + + if (iterator.hasNext()) { + throw new RuntimeException("Unsupported type " + type); + } + + if (type.isEnum()) { + return processEnum((Class) type); + } + return resolver; + } + throw new RuntimeException("No type"); + } + + private static InputTypeBuilder processEnum(Class type) { + var constants = type.getEnumConstants(); + var map = new HashMap(); + for (var c : constants) { + map.put(c.name(), c); + } + + return (obj, context, locale) -> { + if (type.isInstance(obj)) { + return obj; + } + return map.get(obj); + }; + } + + private static InputTypeBuilder processOptional(Iterator> iterator, InputTypeBuilder resolver) { + var mapper = process(iterator, resolver); + return (obj, context, locale) -> { + if (obj instanceof Optional) { + if (((Optional) obj).isEmpty()) { + return obj; + } else { + obj = ((Optional) obj).get(); + } + } + if (obj == null) { + return Optional.empty(); + } + return Optional.of(mapper.convert(obj, context, locale)); + }; + } + + private static InputTypeBuilder processList(Iterator> iterator, InputTypeBuilder resolver) { + var mapper = process(iterator, resolver); + return (obj, context, locale) -> { + if (obj instanceof Collection) { + var collection = (Collection) obj; + + var toReturn = new ArrayList<>(collection.size()); + for (var c : collection) { + toReturn.add(mapper.convert(c, context, locale)); + } + return toReturn; + } else { + throw new RuntimeException("Expected a Collection got " + obj.getClass()); + } + }; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java index 29d0efe..a012aa0 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java @@ -1,503 +1,225 @@ -package com.fleetpin.graphql.builder; - -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.Arrays; -import java.util.Map; -import java.util.function.Consumer; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.GraphQLDeprecated; -import com.fleetpin.graphql.builder.annotations.GraphQLDescription; -import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; -import com.fleetpin.graphql.builder.annotations.InputIgnore; -import com.fleetpin.graphql.builder.annotations.Scalar; -import com.fleetpin.graphql.builder.annotations.SchemaOption; - -import graphql.Scalars; -import graphql.schema.Coercing; -import graphql.schema.DataFetcher; -import graphql.schema.FieldCoordinates; -import graphql.schema.GraphQLAppliedDirective; -import graphql.schema.GraphQLCodeRegistry; -import graphql.schema.GraphQLEnumType; -import graphql.schema.GraphQLFieldDefinition; -import graphql.schema.GraphQLInputObjectField; -import graphql.schema.GraphQLInputObjectType; -import graphql.schema.GraphQLInterfaceType; -import graphql.schema.GraphQLObjectType; -import graphql.schema.GraphQLObjectType.Builder; -import graphql.schema.GraphQLScalarType; -import graphql.schema.GraphQLType; -import graphql.schema.GraphQLTypeReference; -import graphql.schema.idl.TypeRuntimeWiring; - -class EntityProcessor { - - private final Map additionalTypes; - private final GraphQLCodeRegistry.Builder codeRegistry; - private final DirectivesSchema directives; - - - public EntityProcessor(Map additionalTypes, GraphQLCodeRegistry.Builder codeRegistry, DirectivesSchema diretives) { - this.additionalTypes = additionalTypes; - this.codeRegistry = codeRegistry; - this.directives = diretives; - } - - - public static Class extraOptionalType(Type type) { - if(type instanceof Class) { - return (Class) type; - }else if(type instanceof ParameterizedType){ - return extraOptionalType(((ParameterizedType) type).getActualTypeArguments()[0]); - } - throw new RuntimeException("extraction failure for " + type.getClass()); - } - - private void addType(TypeMeta meta, boolean input) { - Class type = meta.getType(); - Type genericType = meta.getGenericType(); - if(genericType == null) { - genericType = type; - } - try { - if(type.isAnnotationPresent(Scalar.class)) { - GraphQLScalarType.Builder scalarType = GraphQLScalarType.newScalar(); - String typeName = getName(meta); - scalarType.name(typeName); - - var description = type.getAnnotation(GraphQLDescription.class); - if(description != null) { - scalarType.description(description.value()); - } - - Class coerecing = type.getAnnotation(Scalar.class).value(); - scalarType.coercing(coerecing.getDeclaredConstructor().newInstance()); - - addDirectives(type, type, scalarType::withAppliedDirective); - var built = scalarType.build(); - if(additionalTypes.put(built.getName(), built) != null) { - throw new RuntimeException(built.getName() + "defined more than once"); - } - } - - if(type.isAnnotationPresent(Entity.class)) { - //special handling - if(type.isEnum()) { - graphql.schema.GraphQLEnumType.Builder enumType = GraphQLEnumType.newEnum(); - String typeName = getName(meta); - enumType.name(typeName); - - var description = type.getAnnotation(GraphQLDescription.class); - if(description != null) { - enumType.description(description.value()); - } - - Object[] enums = type.getEnumConstants(); - for(Object e: enums) { - Enum a = (Enum) e; - if(type.getDeclaredField(e.toString()).isAnnotationPresent(GraphQLIgnore.class)) { - continue; - } - enumType.value(a.name(), a); - } - addDirectives(type, type, enumType::withAppliedDirective); - GraphQLEnumType built = enumType.build(); - if(additionalTypes.put(built.getName(), built) != null) { - throw new RuntimeException(built.getName() + "defined more than once"); - } - return; - } - - SchemaOption schemaType = SchemaOption.BOTH; - Entity graphTypeAnnotation = type.getAnnotation(Entity.class); - if(graphTypeAnnotation != null) { - schemaType = graphTypeAnnotation.value(); - } - - Builder graphType = GraphQLObjectType.newObject(); - String typeName = getName(meta); - graphType.name(typeName); - - - - GraphQLInterfaceType.Builder interfaceBuilder = GraphQLInterfaceType.newInterface(); - interfaceBuilder.name(typeName); - - GraphQLInputObjectType.Builder graphInputType = GraphQLInputObjectType.newInputObject(); - if(schemaType == SchemaOption.INPUT) { - graphInputType.name(typeName); - }else { - graphInputType.name(typeName + "Input"); - } - - { - GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); - field.name("__typename"); - field.type(Scalars.GraphQLString); - graphInputType.field(field); - } - { - var description = type.getAnnotation(GraphQLDescription.class); - if(description != null) { - graphType.description(description.value()); - graphInputType.description(description.value()); - interfaceBuilder.description(description.value()); - } - } - - - TypeRuntimeWiring.Builder runtime = new TypeRuntimeWiring.Builder(); - runtime.typeName(typeName); - for(Method method: type.getMethods()) { - try { - if(method.isSynthetic()) { - continue; - } - if(method.getDeclaringClass().equals(Object.class)) { - continue; - } - if(method.isAnnotationPresent(GraphQLIgnore.class)) { - continue; - } - //will also be on implementing class - if(Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { - continue; - } - if(Modifier.isStatic(method.getModifiers())) { - continue; - }else { - //getter type - if(!input && method.getName().matches("(get|is)[A-Z].*")) { - String name; - if(method.getName().startsWith("get")) { - name = method.getName().substring("get".length(), "get".length() + 1).toLowerCase() + method.getName().substring("get".length() + 1); - }else { - name = method.getName().substring("is".length(), "is".length() + 1).toLowerCase() + method.getName().substring("is".length() + 1); - } - - GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); - field.name(name); - addDirectives(method, type, field::withAppliedDirective); - var deprecated = method.getAnnotation(GraphQLDeprecated.class); - if(deprecated != null) { - field.deprecate(deprecated.value()); - } - var description = method.getAnnotation(GraphQLDescription.class); - if(description != null) { - field.description(description.value()); - } - - TypeMeta innerMeta = new TypeMeta(this, meta, method.getReturnType(), method.getGenericReturnType()); - field.type(SchemaBuilder.getType(innerMeta, method.getAnnotations())); - graphType.field(field); - interfaceBuilder.field(field); - - if(method.getParameterCount() > 0 || directives.target(method, innerMeta)) { - codeRegistry.dataFetcher(FieldCoordinates.coordinates(typeName, name), buildDirectiveWrapper(directives, method, innerMeta)); - } - }else if(input && method.getName().matches("set[A-Z].*")) { - if(method.getParameterCount() == 1 && !method.isAnnotationPresent(InputIgnore.class)) { - String name = method.getName().substring("set".length(), "set".length() + 1).toLowerCase() + method.getName().substring("set".length() + 1); - GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); - field.name(name); - addDirectives(method, type, field::withAppliedDirective); - TypeMeta innerMeta = new TypeMeta(this, meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); - field.type(SchemaBuilder.getInputType(innerMeta, method.getParameterAnnotations()[0])); - graphInputType.field(field); - } - } - } - }catch(RuntimeException e) { - e.printStackTrace(); - throw new RuntimeException("Failed to process method " + method, e); - } - } - - boolean unmappedGenerics = meta.hasUnmappedGeneric(); - boolean interfaceable = type.isInterface() || Modifier.isAbstract(type.getModifiers()); - if(!input && (interfaceable || unmappedGenerics)) { - addDirectives(type, type, interfaceBuilder::withAppliedDirective); - GraphQLInterfaceType built = interfaceBuilder.build(); - if(additionalTypes.put(built.getName(), built) != null) { - throw new RuntimeException(built.getName() + "defined more than once"); - } - - codeRegistry.typeResolver(built.getName(), env -> { - if(type.isInstance(env.getObject())) { - - var name = typeNameLookup(env.getObject()); - var t = additionalTypes.get(name); - if(!(t instanceof GraphQLObjectType)) { - t = additionalTypes.get(name + "_DIRECT"); - } - try { - return (GraphQLObjectType) t; - }catch (ClassCastException e) { - throw e; - } - } - return null; - }); - if(interfaceable) { - return; - } - } - if(unmappedGenerics) { - graphType.withInterface(GraphQLTypeReference.typeRef(typeName)); - graphType.name(typeName + "_DIRECT"); - } - Class parent = type.getSuperclass(); - while(!input && parent != null) { - if(parent.isAnnotationPresent(Entity.class)) { - TypeMeta innerMeta = new TypeMeta(this, meta, parent, type.getGenericSuperclass()); - String interfaceName = process(innerMeta); - graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName)); - - if(!parent.equals(type.getGenericSuperclass())) { - innerMeta = new TypeMeta(this, meta, parent, parent); - interfaceName = process(innerMeta); - graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName)); - } - - var genericMeta = new TypeMeta(this, null, parent, parent); - if(!getName(innerMeta).equals(getName(genericMeta))) { - interfaceName = process(genericMeta); - graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName)); - } - - } - parent = parent.getSuperclass(); - } - //generics - if(!input) { - TypeMeta innerMeta = new TypeMeta(this, meta, type, type); - if(!getName(innerMeta).equals(typeName)) { - String interfaceName = process(innerMeta); - graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName)); - } - innerMeta = new TypeMeta(this, null, type, type); - if(!getName(innerMeta).equals(typeName)) { - String interfaceName = process(innerMeta); - graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName)); - } - } - - if(!input && (schemaType == SchemaOption.BOTH || schemaType == SchemaOption.TYPE)) { - addDirectives(type, type, graphType::withAppliedDirective); - GraphQLObjectType built = graphType.build(); - if(additionalTypes.put(built.getName(), built) != null) { - throw new RuntimeException(built.getName() + "defined more than once"); - } - codeRegistry.typeResolver(built.getName(), env -> { - if(type.isInstance(env.getObject())) { - return built; - } - return null; - }); - } - if(input && (schemaType == SchemaOption.BOTH || schemaType == SchemaOption.INPUT)) { - addDirectives(type, type, graphInputType::withAppliedDirective); - GraphQLInputObjectType inputBuild = graphInputType.build(); - if(additionalTypes.put(inputBuild.getName(), inputBuild) != null) { - throw new RuntimeException(inputBuild.getName() + " defined more than once"); - } - } - } - }catch (ReflectiveOperationException | RuntimeException e) { - throw new RuntimeException("Failed to build schema for class " + type, e); - } - } - - - - - private void addDirectives(AnnotatedElement element, Class location, Consumer builder) { - this.directives.addSchemaDirective(element, location, builder); - } - - - - private static DataFetcher buildDirectiveWrapper(DirectivesSchema diretives, Method method, TypeMeta meta) { - DataFetcher fetcher = env -> { - Object[] args = new Object[method.getParameterCount()]; - for(int i = 0; i < args.length; i++) { - - if(method.getParameterTypes()[i].isAssignableFrom(env.getClass())) { - args[i] = env; - }else if(method.getParameterTypes()[i].isAssignableFrom(env.getContext().getClass())) { - args[i] = env.getContext(); - }else { - Object obj = env.getArgument(method.getParameters()[i].getName()); - args[i] = obj; - } - } - try { - return method.invoke(env.getSource(), args); - }catch (Exception e) { - System.out.println(method); - System.out.println((Object) env.getSource()); - System.out.println(Arrays.toString(args)); - if(e.getCause() instanceof Exception) { - throw (Exception) e.getCause(); - }else { - throw e; - } - - } - }; - - fetcher = diretives.wrap(method, meta, fetcher); - return fetcher; - - } - - - private String getName(TypeMeta meta) { - var type = meta.getType(); - - String name = null; - - if(type.isEnum()) { - name = type.getSimpleName(); - } - if(type.isAnnotationPresent(Scalar.class)) { - name = type.getSimpleName(); - } - if(type.isAnnotationPresent(Entity.class)) { - name = type.getSimpleName(); - } - var genericType = meta.getGenericType(); - - for(int i = 0; i < type.getTypeParameters().length; i++) { - if(genericType instanceof ParameterizedType) { - var t = ((ParameterizedType) genericType).getActualTypeArguments()[i]; - if(t instanceof Class) { - String extra = ((Class) t).getSimpleName(); - name += "_" + extra; - - }else if(t instanceof TypeVariable){ - var variable = (TypeVariable) t; - Class extra = meta.resolveToType(variable); - if(extra != null) { - name += "_" + extra.getSimpleName(); - } - } - }else { - Class extra = meta.resolveToType(type.getTypeParameters()[i]); - if(extra != null) { - name += "_" + extra.getSimpleName(); - } - } - } - return name; - } - - private String typeNameLookup(Object obj) { - var type = obj.getClass(); - String name = null; - - if(type.isEnum()) { - name = type.getSimpleName(); - } - if(type.isAnnotationPresent(Scalar.class)) { - name = type.getSimpleName(); - } - if(type.isAnnotationPresent(Entity.class)) { - name = type.getSimpleName(); - } - - for(var t: type.getTypeParameters()) { - t.getTypeName(); - for(var method: type.getMethods()) { - var methodType = method.getGenericReturnType(); - if(methodType instanceof TypeVariable) { - var typeVariable = ((TypeVariable) methodType); - if(typeVariable.equals(t)) { - //maybe we should dig through private fields first - try { - name += "_" + typeNameLookup(method.invoke(obj)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Could not infre type with regard to generics."); - } //TODO: might have arguments might be a future which would make impossible to resolve - } - } - } - } - - return name; - } - - public String process(TypeMeta meta) { - - TypeMeta rawMeta = new TypeMeta(this, null,meta.getType(), meta.getType()); - String rawName = getName(rawMeta); - - if(rawName != null && !this.additionalTypes.containsKey(rawName)) { - this.additionalTypes.put(rawName, null); // so we don't go around in circles if depend on self - addType(rawMeta, false); - } - - String name = getName(meta); - if(name != null && !this.additionalTypes.containsKey(name)) { - this.additionalTypes.put(name, null); // so we don't go around in circles if depend on self - addType(meta, false); - } - return name; - } - - private String getNameInput(TypeMeta meta) { - var type = meta.getType(); - String name = null; - if(type.isEnum()) { - name = type.getSimpleName(); - } - - if(type.isAnnotationPresent(Scalar.class)) { - name = type.getSimpleName(); - } - - if(type.isAnnotationPresent(Entity.class)) { - if(type.getAnnotation(Entity.class).value() == SchemaOption.BOTH) { - name = type.getSimpleName() + "Input"; - }else { - name = type.getSimpleName(); - } - } - - var genericType = meta.getGenericType(); - - if(genericType instanceof ParameterizedType) { - var parameterizedTypes = ((ParameterizedType) genericType).getActualTypeArguments(); - - for(var t: parameterizedTypes) { - if(t instanceof Class) { - String extra = ((Class) t).getSimpleName(); - name += "_" + extra; - - } - } - } - - return name; - } - - - public String processInput(TypeMeta meta) { - String name = getNameInput(meta); - if(name != null && !this.additionalTypes.containsKey(name)) { - this.additionalTypes.put(name, null); // so we don't go around in circles if depend on self - addType(meta, true); - } - return name; - } - - - -} +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.Scalar; +import com.fleetpin.graphql.builder.annotations.Union; +import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; +import graphql.Scalars; +import graphql.schema.GraphQLAppliedDirective; +import graphql.schema.GraphQLCodeRegistry; +import graphql.schema.GraphQLInputType; +import graphql.schema.GraphQLOutputType; +import graphql.schema.GraphQLType; +import graphql.schema.GraphQLUnionType; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Type; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.MonthDay; +import java.time.YearMonth; +import java.time.ZoneId; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public class EntityProcessor { + + private final GraphQLCodeRegistry.Builder codeRegistry; + private final DirectivesSchema directives; + + private final Map entities; + + EntityProcessor(GraphQLCodeRegistry.Builder codeRegistry, DirectivesSchema diretives) { + this.entities = new HashMap<>(); + addDefaults(); + + this.codeRegistry = codeRegistry; + this.directives = diretives; + } + + private void addDefaults() { + put(Boolean.class, new ScalarEntity(Scalars.GraphQLBoolean)); + put(Boolean.TYPE, new ScalarEntity(Scalars.GraphQLBoolean)); + + put(Float.class, new ScalarEntity(Scalars.GraphQLFloat)); + put(Float.TYPE, new ScalarEntity(Scalars.GraphQLFloat)); + + put(Double.class, new ScalarEntity(Scalars.GraphQLFloat)); + put(Double.TYPE, new ScalarEntity(Scalars.GraphQLFloat)); + + put(Integer.class, new ScalarEntity(Scalars.GraphQLInt)); + put(Integer.TYPE, new ScalarEntity(Scalars.GraphQLInt)); + + put(String.class, new ScalarEntity(Scalars.GraphQLString)); + + put(Long.class, new ScalarEntity(SchemaBuilder.LONG_SCALAR)); + put(Long.TYPE, new ScalarEntity(SchemaBuilder.LONG_SCALAR)); + + put(Instant.class, new ScalarEntity(SchemaBuilder.INSTANT_SCALAR)); + put(LocalDate.class, new ScalarEntity(SchemaBuilder.DATE_SCALAR)); + put(ZoneId.class, new ScalarEntity(SchemaBuilder.ZONE_ID_SCALAR)); + put(Duration.class, new ScalarEntity(SchemaBuilder.DURATION_SCALAR)); + put(MonthDay.class, new ScalarEntity(SchemaBuilder.MONTH_DAY_SCALAR)); + put(YearMonth.class, new ScalarEntity(SchemaBuilder.YEAR_MONTH_SCALAR)); + } + + private void put(Class type, ScalarEntity entity) { + var name = EntityUtil.getName(new TypeMeta(null, type, type)); + entities.put(name, entity); + } + + Set getAdditionalTypes() { + return entities.values().stream().flatMap(s -> s.types()).collect(Collectors.toSet()); + } + + public EntityHolder getEntity(Class type) { + return getEntity(new TypeMeta(null, type, type)); + } + + EntityHolder getEntity(TypeMeta meta) { + String name = EntityUtil.getName(meta); + return entities.computeIfAbsent( + name, + __ -> { + Class type = meta.getType(); + Type genericType = meta.getGenericType(); + if (genericType == null) { + genericType = type; + } + try { + if (type.isAnnotationPresent(Scalar.class)) { + return new ScalarEntity(directives, meta); + } + if (type.isEnum()) { + return new EnumEntity(directives, meta); + } else { + return new ObjectEntity(this, meta); + } + } catch (ReflectiveOperationException | RuntimeException e) { + throw new RuntimeException("Failed to build schema for class " + type, e); + } + } + ); + } + + public GraphQLOutputType getType(TypeMeta meta, Annotation[] annotations) { + for (var annotation : annotations) { + if (annotation instanceof Union) { + var union = (Union) annotation; + return getUnionType(meta, union); + } + } + + return getEntity(meta).getType(meta, annotations); + } + + private GraphQLOutputType getUnionType(TypeMeta meta, Union union) { + var name = UnionType.name(union); + + return entities + .computeIfAbsent( + name, + __ -> { + try { + return new UnionType(this, union); + } catch (RuntimeException e) { + throw new RuntimeException("Failed to build schema for union " + union, e); + } + } + ) + .getType(meta, null); + } + + public GraphQLInputType getInputType(TypeMeta meta, Annotation[] annotations) { + return getEntity(meta).getInputType(meta, annotations); + } + + void addSchemaDirective(AnnotatedElement element, Class location, Consumer builder) { + this.directives.addSchemaDirective(element, location, builder); + } + + DirectivesSchema getDirectives() { + return directives; + } + + GraphQLCodeRegistry.Builder getCodeRegistry() { + return codeRegistry; + } + + public InputTypeBuilder getResolver(TypeMeta meta) { + return getEntity(meta).getResolver(meta); + } + + public InputTypeBuilder getResolver(Class type) { + var meta = new TypeMeta(null, type, type); + return getEntity(meta).getResolver(meta); + } + // + // + // private static Class extraOptionalType(Type type) { + // if (type instanceof Class) { + // return (Class) type; + // } else if (type instanceof ParameterizedType) { + // return extraOptionalType(((ParameterizedType) type).getActualTypeArguments()[0]); + // } + // throw new RuntimeException("extraction failure for " + type.getClass()); + // } + + // + // private String getNameInput(TypeMeta meta) { + // var type = meta.getType(); + // String name = null; + // if (type.isEnum()) { + // name = type.getSimpleName(); + // }else if (type.isAnnotationPresent(Scalar.class)) { + // name = type.getSimpleName(); + // }else if (type.isAnnotationPresent(OneOf.class)) { + // name = type.getSimpleName(); + // }else if (type.isAnnotationPresent(Entity.class)) { + // if (type.getAnnotation(Entity.class).value() == SchemaOption.BOTH) { + // name = type.getSimpleName() + "Input"; + // } else { + // name = type.getSimpleName(); + // } + // }else { + // name = type.getSimpleName() + "Input"; + // } + // + // var genericType = meta.getGenericType(); + // + // if (genericType instanceof ParameterizedType) { + // var parameterizedTypes = ((ParameterizedType) genericType).getActualTypeArguments(); + // + // for (var t : parameterizedTypes) { + // if (t instanceof Class) { + // String extra = ((Class) t).getSimpleName(); + // name += "_" + extra; + // } + // } + // } + // + // return name; + // } + + // public String processInput(TypeMeta meta) { + // String name = getNameInput(meta); + // if (name != null && !this.additionalTypes.containsKey(name) && !inputTypeBuilders.containsKey(meta.getGenericType())) { + // this.additionalTypes.put(name, null); // so we don't go around in circles if depend on self + // addType(meta, true); + // } + // return name; + // } + // + // public ObjectBuilder getResolver(TypeMeta meta) { + // processInput(meta); + // return InputTypeBuilder.process(inputTypeBuilders, meta.getTypes().iterator()); + // } + // + // public EntitiesHolder getEntities() { + // return entities; + // } + +} diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java new file mode 100644 index 0000000..6921174 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java @@ -0,0 +1,66 @@ +package com.fleetpin.graphql.builder; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.TypeVariable; + +public class EntityUtil { + + private static final Method IS_RECORD_METHOD; + + static { + Class classClass = Class.class; + Method isRecord; + try { + isRecord = classClass.getMethod("isRecord"); + } catch (NoSuchMethodException e) { + isRecord = null; + } + IS_RECORD_METHOD = isRecord; + } + + static boolean isRecord(Class type) { + if (IS_RECORD_METHOD == null) { + return false; + } + try { + return (Boolean) IS_RECORD_METHOD.invoke(type); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + static String getName(TypeMeta meta) { + var type = meta.getType(); + + String name = type.getSimpleName(); + + var genericType = meta.getGenericType(); + + for (int i = 0; i < type.getTypeParameters().length; i++) { + if (genericType instanceof ParameterizedType) { + var t = ((ParameterizedType) genericType).getActualTypeArguments()[i]; + if (t instanceof Class) { + String extra = ((Class) t).getSimpleName(); + name += "_" + extra; + } else if (t instanceof TypeVariable) { + var variable = (TypeVariable) t; + Class extra = meta.resolveToType(variable); + if (extra != null) { + name += "_" + extra.getSimpleName(); + } + } + } else { + Class extra = meta.resolveToType(type.getTypeParameters()[i]); + if (extra != null) { + name += "_" + extra.getSimpleName(); + } + } + } + if (meta.isDirect()) { + name += "_DIRECT"; + } + + return name; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java new file mode 100644 index 0000000..e95c669 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java @@ -0,0 +1,57 @@ +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; +import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; +import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; +import graphql.schema.GraphQLEnumType; +import graphql.schema.GraphQLNamedInputType; +import graphql.schema.GraphQLNamedOutputType; + +public class EnumEntity extends EntityHolder { + + private final GraphQLEnumType enumType; + + public EnumEntity(DirectivesSchema directives, TypeMeta meta) throws ReflectiveOperationException { + graphql.schema.GraphQLEnumType.Builder enumType = GraphQLEnumType.newEnum(); + String typeName = EntityUtil.getName(meta); + enumType.name(typeName); + + var type = meta.getType(); + + var description = type.getAnnotation(GraphQLDescription.class); + if (description != null) { + enumType.description(description.value()); + } + + Object[] enums = type.getEnumConstants(); + for (Object e : enums) { + Enum a = (Enum) e; + if (type.getDeclaredField(e.toString()).isAnnotationPresent(GraphQLIgnore.class)) { + continue; + } + enumType.value(a.name(), a); + } + directives.addSchemaDirective(type, type, enumType::withAppliedDirective); + this.enumType = enumType.build(); + } + + @Override + protected GraphQLNamedInputType buildInput() { + return enumType; + } + + @Override + protected GraphQLNamedOutputType buildType() { + return enumType; + } + + @Override + protected String buildInputName() { + return enumType.getName(); + } + + @Override + protected InputTypeBuilder buildResolver() { + return null; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/GraphqlLongCoercing.java b/src/main/java/com/fleetpin/graphql/builder/GraphqlLongCoercing.java index 89b7a35..64942d3 100644 --- a/src/main/java/com/fleetpin/graphql/builder/GraphqlLongCoercing.java +++ b/src/main/java/com/fleetpin/graphql/builder/GraphqlLongCoercing.java @@ -1,85 +1,75 @@ -package com.fleetpin.graphql.builder; - -import static graphql.Assert.assertNotNull; - -import java.math.BigDecimal; -import java.math.BigInteger; - -import graphql.Internal; -import graphql.language.IntValue; -import graphql.language.Value; -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; - - -public class GraphqlLongCoercing implements Coercing { - - - private Long convertImpl(Object input) { - if (input instanceof Long) { - return (Long) input; - } else if (isNumberIsh(input)) { - BigDecimal value; - try { - value = new BigDecimal(input.toString()); - } catch (NumberFormatException e) { - return null; - } - try { - return value.longValueExact(); - } catch (ArithmeticException e) { - return null; - } - } else { - return null; - } - } - - @Override - public Long serialize(Object input) { - Long result = convertImpl(input); - if (result == null) { - throw new CoercingSerializeException( - "Expected type 'Long' but was '" + typeName(input) + "'." - ); - } - return result; - } - - @Override - public Long parseValue(Object input) { - Long result = convertImpl(input); - if (result == null) { - throw new CoercingParseValueException( - "Expected type 'Int' but was '" + typeName(input) + "'." - ); - } - return result; - } - - @Override - public Long parseLiteral(Object input) { - if (!(input instanceof IntValue)) { - throw new CoercingParseLiteralException( - "Expected AST type 'IntValue' but was '" + typeName(input) + "'." - ); - } - BigInteger value = ((IntValue) input).getValue(); - return value.longValue(); - } - - - private boolean isNumberIsh(Object input) { - return input instanceof Number || input instanceof String; - } - - private String typeName(Object input) { - if (input == null) { - return "null"; - } - - return input.getClass().getSimpleName(); - } -} +package com.fleetpin.graphql.builder; + +import static graphql.Assert.assertNotNull; + +import graphql.Internal; +import graphql.language.IntValue; +import graphql.language.Value; +import graphql.schema.Coercing; +import graphql.schema.CoercingParseLiteralException; +import graphql.schema.CoercingParseValueException; +import graphql.schema.CoercingSerializeException; +import java.math.BigDecimal; +import java.math.BigInteger; + +public class GraphqlLongCoercing implements Coercing { + + private Long convertImpl(Object input) { + if (input instanceof Long) { + return (Long) input; + } else if (isNumberIsh(input)) { + BigDecimal value; + try { + value = new BigDecimal(input.toString()); + } catch (NumberFormatException e) { + return null; + } + try { + return value.longValueExact(); + } catch (ArithmeticException e) { + return null; + } + } else { + return null; + } + } + + @Override + public Long serialize(Object input) { + Long result = convertImpl(input); + if (result == null) { + throw new CoercingSerializeException("Expected type 'Long' but was '" + typeName(input) + "'."); + } + return result; + } + + @Override + public Long parseValue(Object input) { + Long result = convertImpl(input); + if (result == null) { + throw new CoercingParseValueException("Expected type 'Int' but was '" + typeName(input) + "'."); + } + return result; + } + + @Override + public Long parseLiteral(Object input) { + if (!(input instanceof IntValue)) { + throw new CoercingParseLiteralException("Expected AST type 'IntValue' but was '" + typeName(input) + "'."); + } + BigInteger value = ((IntValue) input).getValue(); + return value.longValue(); + } + + private boolean isNumberIsh(Object input) { + return input instanceof Number || input instanceof String; + } + + private String typeName(Object input) { + if (input == null) { + return "null"; + } + + return input.getClass().getSimpleName(); + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java new file mode 100644 index 0000000..c9e8047 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java @@ -0,0 +1,259 @@ +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; +import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; +import com.fleetpin.graphql.builder.annotations.InputIgnore; +import com.fleetpin.graphql.builder.annotations.OneOf; +import com.fleetpin.graphql.builder.annotations.SchemaOption; +import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; +import com.fleetpin.graphql.builder.mapper.ObjectFieldBuilder; +import com.fleetpin.graphql.builder.mapper.ObjectFieldBuilder.FieldMapper; +import com.fleetpin.graphql.builder.mapper.OneOfBuilder; +import com.fleetpin.graphql.builder.mapper.RecordFieldBuilder; +import com.fleetpin.graphql.builder.mapper.RecordFieldBuilder.RecordMapper; +import graphql.schema.GraphQLInputObjectField; +import graphql.schema.GraphQLInputObjectType; +import graphql.schema.GraphQLInputObjectType.Builder; +import graphql.schema.GraphQLNamedInputType; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; + +public abstract class InputBuilder { + + protected final EntityProcessor entityProcessor; + protected final TypeMeta meta; + + public InputBuilder(EntityProcessor entityProcessor, TypeMeta meta) { + this.entityProcessor = entityProcessor; + this.meta = meta; + } + + protected String buildName() { + var schemaType = SchemaOption.BOTH; + Entity graphTypeAnnotation = meta.getType().getAnnotation(Entity.class); + if (graphTypeAnnotation != null) { + schemaType = graphTypeAnnotation.value(); + } + + String typeName = EntityUtil.getName(meta); + + String inputName = typeName; + if (schemaType != SchemaOption.INPUT) { + inputName += "Input"; + } + return inputName; + } + + public GraphQLNamedInputType buildInput() { + GraphQLInputObjectType.Builder graphInputType = GraphQLInputObjectType.newInputObject(); + var type = meta.getType(); + + graphInputType.name(buildName()); + + { + var description = type.getAnnotation(GraphQLDescription.class); + if (description != null) { + graphInputType.description(description.value()); + } + } + + processFields(graphInputType); + + entityProcessor.addSchemaDirective(type, type, graphInputType::withAppliedDirective); + return graphInputType.build(); + } + + abstract void processFields(Builder graphInputType); + + protected abstract InputTypeBuilder resolve(); + + public static class OneOfInputBuilder extends InputBuilder { + + public OneOfInputBuilder(EntityProcessor entityProcessor, TypeMeta meta) { + super(entityProcessor, meta); + } + + @Override + void processFields(Builder graphInputType) { + var oneOf = meta.getType().getAnnotation(OneOf.class); + for (var oneOfType : oneOf.value()) { + var name = oneOfType.name(); + GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); + field.name(name); + TypeMeta innerMeta = new TypeMeta(meta, oneOfType.type(), oneOfType.type()); + innerMeta.optional(); + var type = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, new Annotation[0]); + field.type(type); + graphInputType.field(field); + } + } + + @Override + protected InputTypeBuilder resolve() { + var oneOf = meta.getType().getAnnotation(OneOf.class); + return new OneOfBuilder(entityProcessor, meta.getType(), oneOf); + } + } + + public static class ObjectType extends InputBuilder { + + public ObjectType(EntityProcessor entityProcessor, TypeMeta meta) { + super(entityProcessor, meta); + } + + @Override + void processFields(Builder graphInputType) { + for (Method method : meta.getType().getMethods()) { + try { + if (method.isSynthetic()) { + continue; + } + if (method.getDeclaringClass().equals(Object.class)) { + continue; + } + if (method.isAnnotationPresent(GraphQLIgnore.class)) { + continue; + } + //will also be on implementing class + if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { + continue; + } + if (Modifier.isStatic(method.getModifiers())) { + continue; + } else { + //getter type + if (method.getName().matches("set[A-Z].*")) { + if (method.getParameterCount() == 1 && !method.isAnnotationPresent(InputIgnore.class)) { + String name = + method.getName().substring("set".length(), "set".length() + 1).toLowerCase() + + method.getName().substring("set".length() + 1); + GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); + field.name(name); + entityProcessor.addSchemaDirective(method, meta.getType(), field::withAppliedDirective); + TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); + var entity = entityProcessor.getEntity(innerMeta); + var inputType = entity.getInputType(innerMeta, method.getParameterAnnotations()[0]); + field.type(inputType); + graphInputType.field(field); + } + } + } + } catch (RuntimeException e) { + throw new RuntimeException("Failed to process method " + method, e); + } + } + } + + @Override + public InputTypeBuilder resolve() { + var fieldMappers = new ArrayList(); + + for (Method method : meta.getType().getMethods()) { + try { + if (method.isSynthetic()) { + continue; + } + if (method.getDeclaringClass().equals(Object.class)) { + continue; + } + if (method.isAnnotationPresent(GraphQLIgnore.class)) { + continue; + } + //will also be on implementing class + if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { + continue; + } + if (Modifier.isStatic(method.getModifiers())) { + continue; + } else { + //getter type + if (method.getName().matches("set[A-Z].*")) { + if (method.getParameterCount() == 1 && !method.isAnnotationPresent(InputIgnore.class)) { + String name = + method.getName().substring("set".length(), "set".length() + 1).toLowerCase() + + method.getName().substring("set".length() + 1); + TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); + fieldMappers.add(FieldMapper.build(entityProcessor, innerMeta, name, method)); + } + } + } + } catch (RuntimeException e) { + throw new RuntimeException("Failed to process method " + method, e); + } + } + + return new ObjectFieldBuilder(meta.getType(), fieldMappers); + } + } + + public static class Record extends InputBuilder { + + public Record(EntityProcessor entityProcessor, TypeMeta meta) { + super(entityProcessor, meta); + } + + @Override + void processFields(Builder graphInputType) { + for (var field : this.meta.getType().getDeclaredFields()) { + try { + if (field.isSynthetic()) { + continue; + } + if (field.isAnnotationPresent(GraphQLIgnore.class)) { + continue; + } + if (Modifier.isStatic(field.getModifiers())) { + continue; + } else { + //getter type + if (!field.isAnnotationPresent(InputIgnore.class)) { + String name = field.getName(); + GraphQLInputObjectField.Builder fieldBuilder = GraphQLInputObjectField.newInputObjectField(); + fieldBuilder.name(name); + entityProcessor.addSchemaDirective(field, meta.getType(), fieldBuilder::withAppliedDirective); + TypeMeta innerMeta = new TypeMeta(meta, field.getType(), field.getGenericType()); + var entity = entityProcessor.getEntity(innerMeta); + var inputType = entity.getInputType(innerMeta, field.getAnnotations()); + fieldBuilder.type(inputType); + graphInputType.field(fieldBuilder); + } + } + } catch (RuntimeException e) { + throw new RuntimeException("Failed to process method " + field, e); + } + } + } + + @Override + protected InputTypeBuilder resolve() { + var fieldMappers = new ArrayList(); + + for (var field : this.meta.getType().getDeclaredFields()) { + try { + if (field.isSynthetic()) { + continue; + } + if (field.isAnnotationPresent(GraphQLIgnore.class)) { + continue; + } + if (Modifier.isStatic(field.getModifiers())) { + continue; + } else { + //getter type + if (!field.isAnnotationPresent(InputIgnore.class)) { + TypeMeta innerMeta = new TypeMeta(meta, field.getType(), field.getGenericType()); + var resolver = entityProcessor.getResolver(innerMeta); + fieldMappers.add(new RecordMapper(field.getName(), field.getType(), resolver)); + } + } + } catch (RuntimeException e) { + throw new RuntimeException("Failed to process method " + field, e); + } + } + return new RecordFieldBuilder(meta.getType(), fieldMappers); + } + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/InstantCoercing.java b/src/main/java/com/fleetpin/graphql/builder/InstantCoercing.java index bfe409b..cf31dfb 100644 --- a/src/main/java/com/fleetpin/graphql/builder/InstantCoercing.java +++ b/src/main/java/com/fleetpin/graphql/builder/InstantCoercing.java @@ -12,12 +12,11 @@ package com.fleetpin.graphql.builder; -import java.time.Instant; - import graphql.schema.Coercing; import graphql.schema.CoercingParseLiteralException; import graphql.schema.CoercingParseValueException; import graphql.schema.CoercingSerializeException; +import java.time.Instant; public class InstantCoercing implements Coercing { @@ -35,19 +34,16 @@ public Instant parseValue(Object input) throws CoercingParseValueException { public Instant parseLiteral(Object input) throws CoercingParseLiteralException { return convertImpl(input); } - - - private Instant convertImpl(Object input) { - if (input instanceof Instant) { - return (Instant) input; - } else if (input instanceof String) { - return Instant.parse((String) input); - } - if (input instanceof Long) { - return Instant.ofEpochMilli((long) input); - } - return null; - } - + private Instant convertImpl(Object input) { + if (input instanceof Instant) { + return (Instant) input; + } else if (input instanceof String) { + return Instant.parse((String) input); + } + if (input instanceof Long) { + return Instant.ofEpochMilli((long) input); + } + return null; + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/LocalDateCoercing.java b/src/main/java/com/fleetpin/graphql/builder/LocalDateCoercing.java index b3d0f0f..eb5af8a 100644 --- a/src/main/java/com/fleetpin/graphql/builder/LocalDateCoercing.java +++ b/src/main/java/com/fleetpin/graphql/builder/LocalDateCoercing.java @@ -12,12 +12,11 @@ package com.fleetpin.graphql.builder; -import java.time.LocalDate; - import graphql.schema.Coercing; import graphql.schema.CoercingParseLiteralException; import graphql.schema.CoercingParseValueException; import graphql.schema.CoercingSerializeException; +import java.time.LocalDate; public class LocalDateCoercing implements Coercing { @@ -35,16 +34,13 @@ public LocalDate parseValue(Object input) throws CoercingParseValueException { public LocalDate parseLiteral(Object input) throws CoercingParseLiteralException { return convertImpl(input); } - - - private LocalDate convertImpl(Object input) { - if (input instanceof LocalDate) { - return (LocalDate) input; - } else if (input instanceof String) { - return LocalDate.parse((String) input); - } - return null; - } - + private LocalDate convertImpl(Object input) { + if (input instanceof LocalDate) { + return (LocalDate) input; + } else if (input instanceof String) { + return LocalDate.parse((String) input); + } + return null; + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/LocalDateTimeCoercing.java b/src/main/java/com/fleetpin/graphql/builder/LocalDateTimeCoercing.java index 1bfc48e..51da76c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/LocalDateTimeCoercing.java +++ b/src/main/java/com/fleetpin/graphql/builder/LocalDateTimeCoercing.java @@ -12,12 +12,11 @@ package com.fleetpin.graphql.builder; -import java.time.LocalDateTime; - import graphql.schema.Coercing; import graphql.schema.CoercingParseLiteralException; import graphql.schema.CoercingParseValueException; import graphql.schema.CoercingSerializeException; +import java.time.LocalDateTime; public class LocalDateTimeCoercing implements Coercing { @@ -35,16 +34,13 @@ public LocalDateTime parseValue(Object input) throws CoercingParseValueException public LocalDateTime parseLiteral(Object input) throws CoercingParseLiteralException { return convertImpl(input); } - - - private LocalDateTime convertImpl(Object input) { - if (input instanceof LocalDateTime) { - return (LocalDateTime) input; - } else if (input instanceof String) { - return LocalDateTime.parse((String) input); - } - return null; - } - + private LocalDateTime convertImpl(Object input) { + if (input instanceof LocalDateTime) { + return (LocalDateTime) input; + } else if (input instanceof String) { + return LocalDateTime.parse((String) input); + } + return null; + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/MonthDayCoercing.java b/src/main/java/com/fleetpin/graphql/builder/MonthDayCoercing.java index 152cdfa..18bb5de 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MonthDayCoercing.java +++ b/src/main/java/com/fleetpin/graphql/builder/MonthDayCoercing.java @@ -1,36 +1,34 @@ -package com.fleetpin.graphql.builder; - -import java.time.MonthDay; - -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; - -public class MonthDayCoercing implements Coercing { - - @Override - public MonthDay serialize(Object dataFetcherResult) throws CoercingSerializeException { - return convertImpl(dataFetcherResult); - } - - @Override - public MonthDay parseValue(Object input) throws CoercingParseValueException { - return convertImpl(input); - } - - @Override - public MonthDay parseLiteral(Object input) throws CoercingParseLiteralException { - return convertImpl(input); - } - - - private MonthDay convertImpl(Object input) { - if (input instanceof MonthDay) { - return (MonthDay) input; - } else if (input instanceof String) { - return MonthDay.parse((String) input); - } - return null; - } -} \ No newline at end of file +package com.fleetpin.graphql.builder; + +import graphql.schema.Coercing; +import graphql.schema.CoercingParseLiteralException; +import graphql.schema.CoercingParseValueException; +import graphql.schema.CoercingSerializeException; +import java.time.MonthDay; + +public class MonthDayCoercing implements Coercing { + + @Override + public MonthDay serialize(Object dataFetcherResult) throws CoercingSerializeException { + return convertImpl(dataFetcherResult); + } + + @Override + public MonthDay parseValue(Object input) throws CoercingParseValueException { + return convertImpl(input); + } + + @Override + public MonthDay parseLiteral(Object input) throws CoercingParseLiteralException { + return convertImpl(input); + } + + private MonthDay convertImpl(Object input) { + if (input instanceof MonthDay) { + return (MonthDay) input; + } else if (input instanceof String) { + return MonthDay.parse((String) input); + } + return null; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java b/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java new file mode 100644 index 0000000..2598817 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java @@ -0,0 +1,53 @@ +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.OneOf; +import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; +import graphql.schema.GraphQLNamedInputType; +import graphql.schema.GraphQLNamedOutputType; + +public class ObjectEntity extends EntityHolder { + + private InputBuilder inputBuilder; + + private TypeBuilder typeBuilder; + + public ObjectEntity(EntityProcessor entityProcessor, TypeMeta meta) { + if (meta.getType().isAnnotationPresent(OneOf.class)) { + inputBuilder = new InputBuilder.OneOfInputBuilder(entityProcessor, meta); + } else if (EntityUtil.isRecord(meta.getType())) { + inputBuilder = new InputBuilder.Record(entityProcessor, meta); + } else { + inputBuilder = new InputBuilder.ObjectType(entityProcessor, meta); + } + + if (EntityUtil.isRecord(meta.getType())) { + typeBuilder = new TypeBuilder.Record(entityProcessor, meta); + } else { + typeBuilder = new TypeBuilder.ObjectType(entityProcessor, meta); + } + } + + @Override + protected GraphQLNamedInputType buildInput() { + return inputBuilder.buildInput(); + } + + @Override + public InputTypeBuilder buildResolver() { + return inputBuilder.resolve(); + } + + @Override + protected GraphQLNamedOutputType buildType() { + try { + return typeBuilder.buildType(); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + protected String buildInputName() { + return inputBuilder.buildName(); + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/RestrictType.java b/src/main/java/com/fleetpin/graphql/builder/RestrictType.java index 0a24a90..3402cff 100644 --- a/src/main/java/com/fleetpin/graphql/builder/RestrictType.java +++ b/src/main/java/com/fleetpin/graphql/builder/RestrictType.java @@ -17,28 +17,26 @@ import java.util.concurrent.CompletableFuture; public interface RestrictType { - public CompletableFuture allow(T obj); - + public default CompletableFuture> filter(List list) { - boolean[] keep = new boolean[list.size()]; - + CompletableFuture[] all = new CompletableFuture[list.size()]; - for(int i = 0; i < list.size(); i++) { + for (int i = 0; i < list.size(); i++) { int offset = i; all[i] = allow(list.get(i)).thenAccept(allow -> keep[offset] = allow); } - return CompletableFuture.allOf(all).thenApply(__ -> { - List toReturn = new ArrayList<>(); - for(int i = 0; i < list.size(); i++) { - if(keep[i]) { - toReturn.add(list.get(i)); + return CompletableFuture + .allOf(all) + .thenApply(__ -> { + List toReturn = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) { + if (keep[i]) { + toReturn.add(list.get(i)); + } } - } - return toReturn; - }); + return toReturn; + }); } - - } diff --git a/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java b/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java index 577de13..422fa88 100644 --- a/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java +++ b/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java @@ -12,19 +12,18 @@ package com.fleetpin.graphql.builder; +import graphql.schema.DataFetchingEnvironment; import java.lang.reflect.ParameterizedType; import java.util.concurrent.CompletableFuture; -import graphql.schema.DataFetchingEnvironment; - public interface RestrictTypeFactory { public CompletableFuture> create(DataFetchingEnvironment context); default Class extractType() { - for(var inter: getClass().getGenericInterfaces()) { - if(inter instanceof ParameterizedType) { + for (var inter : getClass().getGenericInterfaces()) { + if (inter instanceof ParameterizedType) { var param = (ParameterizedType) inter; - if(RestrictTypeFactory.class.equals(param.getRawType())) { + if (RestrictTypeFactory.class.equals(param.getRawType())) { return (Class) param.getActualTypeArguments()[0]; } } diff --git a/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java b/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java index 7194c1d..5c2eb30 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java +++ b/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java @@ -12,19 +12,16 @@ package com.fleetpin.graphql.builder; +import graphql.introspection.Introspection.DirectiveLocation; import java.lang.annotation.Annotation; import java.util.List; -import graphql.introspection.Introspection.DirectiveLocation; - -public interface SDLDirective extends DirectiveOperation{ - +public interface SDLDirective extends DirectiveOperation { public List validLocations(); - + public K build(T annotation, Class location); public default boolean repeatable() { return false; } - } diff --git a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java b/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java index 68db9d9..821cac2 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java @@ -1,127 +1,129 @@ -package com.fleetpin.graphql.builder; - -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; - -import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; -import com.fleetpin.graphql.builder.annotations.InputIgnore; - -import graphql.schema.GraphQLAppliedDirective; -import graphql.schema.GraphQLAppliedDirectiveArgument; -import graphql.schema.GraphQLArgument; -import graphql.schema.GraphQLDirective; - -/** - * SDL directives are applied directly to the schema. They can be used to export - * extra information about methods By default graphql will not export this - * information but it can be enabled with new - * IntrospectionWithDirectivesSupport() - */ -public class SDLProcessor { - - private final SDLDirective factory; - private final GraphQLDirective directive; - private final List> builders; - - public SDLProcessor(SDLDirective factory, GraphQLDirective directive, List> builders) { - this.factory = (SDLDirective) factory; - this.directive = directive; - this.builders = builders; - } - - public static SDLProcessor build(EntityProcessor entityProcessor, SDLDirective directive) { - - for(var inter: directive.getClass().getAnnotatedInterfaces()) { - if(inter.getType() instanceof ParameterizedType) { - var type = (ParameterizedType) inter.getType(); - if(type.getRawType() instanceof Class) { - var rawType = (Class) type.getRawType(); - if(SDLDirective.class.isAssignableFrom(rawType)) { - var annotation = (Class) type.getActualTypeArguments()[0]; - var arguments = (Class) type.getActualTypeArguments()[1]; - - var builder = GraphQLDirective.newDirective().name(annotation.getSimpleName()); - for(var location: directive.validLocations()) { - builder.validLocation(location); - } - builder.repeatable(directive.repeatable()); - List> builders = new ArrayList<>(); - for(Method method: arguments.getMethods()) { - try { - if(method.isSynthetic()) { - continue; - } - if(method.getDeclaringClass().equals(Object.class)) { - continue; - } - if(method.isAnnotationPresent(GraphQLIgnore.class)) { - continue; - } - //will also be on implementing class - if(Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { - continue; - } - if(Modifier.isStatic(method.getModifiers())) { - continue; - }else { - if(method.getName().matches("(get|is)[A-Z].*") && method.getParameterCount() == 0 ) { - String name; - if(method.getName().startsWith("get")) { - name = method.getName().substring("get".length(), "get".length() + 1).toLowerCase() + method.getName().substring("get".length() + 1); - }else { - name = method.getName().substring("is".length(), "is".length() + 1).toLowerCase() + method.getName().substring("is".length() + 1); - } - GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); - argument.name(name); - TypeMeta innerMeta = new TypeMeta(entityProcessor, null, method.getReturnType(), method.getGenericReturnType()); - var argumentType = SchemaBuilder.getInputType(innerMeta, method.getAnnotations()); - argument.type(argumentType); - builder.argument(argument); - builders.add(object -> { - try { - return GraphQLAppliedDirectiveArgument.newArgument().name(name).type(argumentType).valueProgrammatic(method.invoke(object)).build(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }); - } - } - }catch(RuntimeException e) { - throw new RuntimeException("Failed to process method " + method, e); - } - } - return new SDLProcessor(directive, builder.build(), builders); - } - } - } - } - return null; - } - - public void apply(Annotation annotation, Class location, Consumer builder) { - var built = factory.build(annotation, location); - if (built != null) { - // need to call the methods building out the arguments - var arguments = GraphQLAppliedDirective.newDirective(); - arguments.name(directive.getName()); - for(var b: this.builders) { - arguments.argument(b.apply(built)); - } - builder.accept(arguments.build()); - } - - } - - public GraphQLDirective getDirective() { - return directive; - } - -} +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; +import graphql.schema.GraphQLAppliedDirective; +import graphql.schema.GraphQLAppliedDirectiveArgument; +import graphql.schema.GraphQLArgument; +import graphql.schema.GraphQLDirective; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * SDL directives are applied directly to the schema. They can be used to export + * extra information about methods By default graphql will not export this + * information but it can be enabled with new + * IntrospectionWithDirectivesSupport() + */ +public class SDLProcessor { + + private final SDLDirective factory; + private final GraphQLDirective directive; + private final List> builders; + + public SDLProcessor(SDLDirective factory, GraphQLDirective directive, List> builders) { + this.factory = (SDLDirective) factory; + this.directive = directive; + this.builders = builders; + } + + public static SDLProcessor build(EntityProcessor entityProcessor, SDLDirective directive) { + for (var inter : directive.getClass().getAnnotatedInterfaces()) { + if (inter.getType() instanceof ParameterizedType) { + var type = (ParameterizedType) inter.getType(); + if (type.getRawType() instanceof Class) { + var rawType = (Class) type.getRawType(); + if (SDLDirective.class.isAssignableFrom(rawType)) { + var annotation = (Class) type.getActualTypeArguments()[0]; + var arguments = (Class) type.getActualTypeArguments()[1]; + + var builder = GraphQLDirective.newDirective().name(annotation.getSimpleName()); + for (var location : directive.validLocations()) { + builder.validLocation(location); + } + builder.repeatable(directive.repeatable()); + List> builders = new ArrayList<>(); + for (Method method : arguments.getMethods()) { + try { + if (method.isSynthetic()) { + continue; + } + if (method.getDeclaringClass().equals(Object.class)) { + continue; + } + if (method.isAnnotationPresent(GraphQLIgnore.class)) { + continue; + } + //will also be on implementing class + if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { + continue; + } + if (Modifier.isStatic(method.getModifiers())) { + continue; + } else { + if (method.getName().matches("(get|is)[A-Z].*") && method.getParameterCount() == 0) { + String name; + if (method.getName().startsWith("get")) { + name = + method.getName().substring("get".length(), "get".length() + 1).toLowerCase() + + method.getName().substring("get".length() + 1); + } else { + name = + method.getName().substring("is".length(), "is".length() + 1).toLowerCase() + + method.getName().substring("is".length() + 1); + } + GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); + argument.name(name); + TypeMeta innerMeta = new TypeMeta(null, method.getReturnType(), method.getGenericReturnType()); + var argumentType = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, method.getAnnotations()); + argument.type(argumentType); + builder.argument(argument); + builders.add(object -> { + try { + return GraphQLAppliedDirectiveArgument + .newArgument() + .name(name) + .type(argumentType) + .valueProgrammatic(method.invoke(object)) + .build(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }); + } + } + } catch (RuntimeException e) { + throw new RuntimeException("Failed to process method " + method, e); + } + } + return new SDLProcessor(directive, builder.build(), builders); + } + } + } + } + return null; + } + + public void apply(Annotation annotation, Class location, Consumer builder) { + var built = factory.build(annotation, location); + if (built != null) { + // need to call the methods building out the arguments + var arguments = GraphQLAppliedDirective.newDirective(); + arguments.name(directive.getName()); + for (var b : this.builders) { + arguments.argument(b.apply(built)); + } + builder.accept(arguments.build()); + } + } + + public GraphQLDirective getDirective() { + return directive; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java b/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java new file mode 100644 index 0000000..3545f17 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java @@ -0,0 +1,59 @@ +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; +import com.fleetpin.graphql.builder.annotations.Scalar; +import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; +import graphql.schema.Coercing; +import graphql.schema.GraphQLNamedInputType; +import graphql.schema.GraphQLNamedOutputType; +import graphql.schema.GraphQLScalarType; + +public class ScalarEntity extends EntityHolder { + + private final GraphQLScalarType scalar; + + public ScalarEntity(GraphQLScalarType scalar) { + this.scalar = scalar; + } + + // + + public ScalarEntity(DirectivesSchema directives, TypeMeta meta) throws ReflectiveOperationException { + GraphQLScalarType.Builder scalarType = GraphQLScalarType.newScalar(); + String typeName = EntityUtil.getName(meta); + scalarType.name(typeName); + + var type = meta.getType(); + + var description = type.getAnnotation(GraphQLDescription.class); + if (description != null) { + scalarType.description(description.value()); + } + + Class coerecing = type.getAnnotation(Scalar.class).value(); + scalarType.coercing(coerecing.getDeclaredConstructor().newInstance()); + + directives.addSchemaDirective(type, type, scalarType::withAppliedDirective); + this.scalar = scalarType.build(); + } + + @Override + protected GraphQLNamedInputType buildInput() { + return scalar; + } + + @Override + protected GraphQLNamedOutputType buildType() { + return scalar; + } + + @Override + protected String buildInputName() { + return scalar.getName(); + } + + @Override + public InputTypeBuilder buildResolver() { + return scalar.getCoercing()::serialize; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index 7c754e6..c7f12f6 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -12,523 +12,303 @@ package com.fleetpin.graphql.builder; -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.MonthDay; -import java.time.YearMonth; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import org.reflections.Reflections; -import org.reflections.scanners.MethodAnnotationsScanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; - -import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; -import com.fleetpin.graphql.builder.TypeMeta.Flag; import com.fleetpin.graphql.builder.annotations.Context; import com.fleetpin.graphql.builder.annotations.Directive; import com.fleetpin.graphql.builder.annotations.Entity; import com.fleetpin.graphql.builder.annotations.GraphQLDeprecated; import com.fleetpin.graphql.builder.annotations.GraphQLDescription; -import com.fleetpin.graphql.builder.annotations.Id; import com.fleetpin.graphql.builder.annotations.Mutation; import com.fleetpin.graphql.builder.annotations.Query; import com.fleetpin.graphql.builder.annotations.Restrict; import com.fleetpin.graphql.builder.annotations.Restricts; -import com.fleetpin.graphql.builder.annotations.Scalar; import com.fleetpin.graphql.builder.annotations.SchemaOption; import com.fleetpin.graphql.builder.annotations.Subscription; - import graphql.GraphQLContext; -import graphql.Scalars; +import graphql.language.UnionTypeDefinition; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.FieldCoordinates; import graphql.schema.GraphQLArgument; import graphql.schema.GraphQLCodeRegistry; import graphql.schema.GraphQLFieldDefinition; -import graphql.schema.GraphQLInputType; -import graphql.schema.GraphQLList; -import graphql.schema.GraphQLNonNull; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLObjectType.Builder; -import graphql.schema.GraphQLOutputType; import graphql.schema.GraphQLScalarType; import graphql.schema.GraphQLSchema; -import graphql.schema.GraphQLType; -import graphql.schema.GraphQLTypeReference; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; public class SchemaBuilder { - public final static ObjectMapper MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).registerModule(new ParameterNamesModule()) - .registerModule(new Jdk8Module()) - .registerModule(new JavaTimeModule()) - .disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS).disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS).disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) - .setVisibility(PropertyAccessor.FIELD, Visibility.ANY); - - private static final GraphQLScalarType INSTANT_SCALAR = GraphQLScalarType.newScalar().name("DateTime").coercing(new InstantCoercing()).build(); - private static final GraphQLScalarType DATE_SCALAR = GraphQLScalarType.newScalar().name("Date").coercing(new LocalDateCoercing()).build(); - private static final GraphQLScalarType DURATION_SCALAR = GraphQLScalarType.newScalar().name("Duration").coercing(new DurationCoercing()).build(); - private static final GraphQLScalarType ZONE_ID_SCALAR = GraphQLScalarType.newScalar().name("Timezone").coercing(new ZoneIdCoercing()).build(); - private static final GraphQLScalarType MONTH_DAY_SCALAR = GraphQLScalarType.newScalar().name("MonthDay").coercing(new MonthDayCoercing()).build(); - private static final GraphQLScalarType YEAR_MONTH_SCALAR = GraphQLScalarType.newScalar().name("YearMonth").coercing(new YearMonthCoercing()).build(); - private static final GraphQLScalarType LONG_SCALAR = GraphQLScalarType.newScalar().name("Long").coercing(new GraphqlLongCoercing()).build(); + + protected static final GraphQLScalarType INSTANT_SCALAR = GraphQLScalarType.newScalar().name("DateTime").coercing(new InstantCoercing()).build(); + protected static final GraphQLScalarType DATE_SCALAR = GraphQLScalarType.newScalar().name("Date").coercing(new LocalDateCoercing()).build(); + protected static final GraphQLScalarType DURATION_SCALAR = GraphQLScalarType.newScalar().name("Duration").coercing(new DurationCoercing()).build(); + protected static final GraphQLScalarType ZONE_ID_SCALAR = GraphQLScalarType.newScalar().name("Timezone").coercing(new ZoneIdCoercing()).build(); + protected static final GraphQLScalarType MONTH_DAY_SCALAR = GraphQLScalarType.newScalar().name("MonthDay").coercing(new MonthDayCoercing()).build(); + protected static final GraphQLScalarType YEAR_MONTH_SCALAR = GraphQLScalarType.newScalar().name("YearMonth").coercing(new YearMonthCoercing()).build(); + protected static final GraphQLScalarType LONG_SCALAR = GraphQLScalarType.newScalar().name("Long").coercing(new GraphqlLongCoercing()).build(); private final DirectivesSchema diretives; private final AuthorizerSchema authorizer; private final GraphQLCodeRegistry.Builder codeRegistry; - private final Map additionalTypes; - private final Builder graphQuery; private final Builder graphMutations; private final Builder graphSubscriptions; - + private final EntityProcessor entityProcessor; - private SchemaBuilder(DirectivesSchema diretives, AuthorizerSchema authorizer) { this.diretives = diretives; this.authorizer = authorizer; - + this.graphQuery = GraphQLObjectType.newObject(); graphQuery.name("Query"); this.graphMutations = GraphQLObjectType.newObject(); graphMutations.name("Mutations"); this.graphSubscriptions = GraphQLObjectType.newObject(); graphSubscriptions.name("Subscriptions"); - this.additionalTypes = new HashMap<>(); this.codeRegistry = GraphQLCodeRegistry.newCodeRegistry(); - this.entityProcessor = new EntityProcessor(additionalTypes, codeRegistry, diretives); - - - diretives.processSDL(entityProcessor); + this.entityProcessor = new EntityProcessor(codeRegistry, diretives); + diretives.processSDL(entityProcessor); } - + private SchemaBuilder process(Set endPoints) throws ReflectiveOperationException { - for(var method: endPoints) { - if(!Modifier.isStatic(method.getModifiers())) { + for (var method : endPoints) { + if (!Modifier.isStatic(method.getModifiers())) { throw new RuntimeException("End point must be a static method"); } //TODO:query vs mutation GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); - + var deprecated = method.getAnnotation(GraphQLDeprecated.class); - if(deprecated != null) { + if (deprecated != null) { field.deprecate(deprecated.value()); } - + var description = method.getAnnotation(GraphQLDescription.class); - if(description != null) { + if (description != null) { field.description(description.value()); } - + field.name(method.getName()); - - TypeMeta meta = new TypeMeta(entityProcessor, null, method.getReturnType(), method.getGenericReturnType()); - field.type(getType(meta, method.getAnnotations())); - for(int i = 0; i < method.getParameterCount(); i++) { + + TypeMeta meta = new TypeMeta(null, method.getReturnType(), method.getGenericReturnType()); + var type = entityProcessor.getType(meta, method.getAnnotations()); + field.type(type); + for (int i = 0; i < method.getParameterCount(); i++) { GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); - if(isContext(method.getParameterTypes()[i])) { + if (isContext(method.getParameterTypes()[i])) { continue; } - - TypeMeta inputMeta = new TypeMeta(this.entityProcessor, null, method.getParameterTypes()[i], method.getGenericParameterTypes()[i]); - argument.type(getInputType(inputMeta, method.getParameterAnnotations()[i]));//TODO:dirty cast + + TypeMeta inputMeta = new TypeMeta(null, method.getParameterTypes()[i], method.getGenericParameterTypes()[i]); + argument.type(entityProcessor.getInputType(inputMeta, method.getParameterAnnotations()[i])); //TODO:dirty cast argument.name(method.getParameters()[i].getName()); //TODO: argument.defaultValue(defaultValue) field.argument(argument); } diretives.addSchemaDirective(method, method.getDeclaringClass(), field::withAppliedDirective); - if(method.isAnnotationPresent(Query.class)) { + if (method.isAnnotationPresent(Query.class)) { graphQuery.field(field); - DataFetcher fetcher = buildFetcher(diretives, authorizer, method, meta); codeRegistry.dataFetcher(graphQuery.build(), field.build(), fetcher); - }else if(method.isAnnotationPresent(Mutation.class)) { + } else if (method.isAnnotationPresent(Mutation.class)) { graphMutations.field(field); DataFetcher fetcher = buildFetcher(diretives, authorizer, method, meta); codeRegistry.dataFetcher(FieldCoordinates.coordinates("Mutations", method.getName()), fetcher); - }else if(method.isAnnotationPresent(Subscription.class)) { + } else if (method.isAnnotationPresent(Subscription.class)) { graphSubscriptions.field(field); DataFetcher fetcher = buildFetcher(diretives, authorizer, method, meta); codeRegistry.dataFetcher(FieldCoordinates.coordinates("Subscriptions", method.getName()), fetcher); } - - } return this; } - + private SchemaBuilder processTypes(Set> types) { - for(var type: types) { - TypeMeta meta = new TypeMeta(this.entityProcessor, null, type, type); - + for (var type : types) { + TypeMeta meta = new TypeMeta(null, type, type); + var annotation = type.getAnnotation(Entity.class); - if(annotation.value() != SchemaOption.INPUT) { - this.entityProcessor.process(meta); + if (annotation.value() != SchemaOption.INPUT) { + this.entityProcessor.getEntity(meta).getInnerType(meta); } - } return this; } private GraphQLSchema build(Set> schemaConfiguration) { - codeRegistry.typeResolver("ID", env -> { - return null; - }); - var builder = GraphQLSchema.newSchema().codeRegistry(codeRegistry.build()).additionalTypes(new HashSet<>(additionalTypes.values())); - + var builder = GraphQLSchema.newSchema().codeRegistry(codeRegistry.build()).additionalTypes(entityProcessor.getAdditionalTypes()); + var query = graphQuery.build(); builder.query(query); - + var mutations = graphMutations.build(); - if(!mutations.getFields().isEmpty()) { + if (!mutations.getFields().isEmpty()) { builder.mutation(mutations); } var subscriptions = graphSubscriptions.build(); - if(!subscriptions.getFields().isEmpty()) { + if (!subscriptions.getFields().isEmpty()) { builder.subscription(subscriptions); } - - + builder.build(); - + diretives.getSchemaDirective().forEach(directive -> builder.additionalDirective(directive)); - - - for(var schema: schemaConfiguration) { + + for (var schema : schemaConfiguration) { this.diretives.addSchemaDirective(schema, schema, builder::withSchemaAppliedDirective); } - - return builder.build(); + return builder.build(); } private static boolean isContext(Class class1) { - return class1.isAssignableFrom(GraphQLContext.class) || class1.isAssignableFrom(DataFetchingEnvironment.class) || class1.isAnnotationPresent(Context.class); + return ( + class1.isAssignableFrom(GraphQLContext.class) || class1.isAssignableFrom(DataFetchingEnvironment.class) || class1.isAnnotationPresent(Context.class) + ); + } + + private DataFetcher buildFetcher(DirectivesSchema diretives, AuthorizerSchema authorizer, Method method, TypeMeta meta) { + DataFetcher fetcher = buildDataFetcher(meta, method); + fetcher = diretives.wrap(method, meta, fetcher); + + if (authorizer != null) { + fetcher = authorizer.wrap(fetcher, method); + } + return fetcher; } - private static DataFetcher buildFetcher(DirectivesSchema diretives, AuthorizerSchema authorizer, Method method, TypeMeta meta) { + private DataFetcher buildDataFetcher(TypeMeta meta, Method method) { + Function[] resolvers = new Function[method.getParameterCount()]; + + for (int i = 0; i < resolvers.length; i++) { + Class type = method.getParameterTypes()[i]; + var name = method.getParameters()[i].getName(); + var generic = method.getGenericParameterTypes()[i]; + var argMeta = new TypeMeta(meta, type, generic); + resolvers[i] = buildResolver(name, argMeta); + } + DataFetcher fetcher = env -> { try { - Object[] args = new Object[method.getParameterCount()]; - for(int i = 0; i < args.length; i++) { - Class type = method.getParameterTypes()[i]; - if(type.isAssignableFrom(env.getClass())) { - args[i] = env; - }else if(type.isAssignableFrom(env.getContext().getClass())) { - args[i] = env.getContext(); - }else { - Object obj = env.getArgument(method.getParameters()[i].getName()); - //if they don't match use json to make them - - if(obj instanceof List) { - var genericType = method.getGenericParameterTypes()[i]; - args[i] = MAPPER.convertValue(obj, new TypeReference() { - @Override - public Type getType() { - return genericType; - } - }); - }else if(type.isInstance(obj) ) { - args[i] = obj; - }else { - if(Optional.class.isAssignableFrom(type)) { - if(obj == null) { - args[i] = Optional.empty(); - }else { - var genericType = method.getGenericParameterTypes()[i]; - var t = ((ParameterizedType) genericType).getActualTypeArguments()[0]; - args[i] = Optional.of(MAPPER.convertValue(obj, new TypeReference() { - @Override - public Type getType() { - return t; - } - })); - } - }else { - var t = method.getParameters()[i].getParameterizedType(); - args[i] = MAPPER.convertValue(obj, new TypeReference() { - @Override - public Type getType() { - return t; - } - }); - } - } - } + Object[] args = new Object[resolvers.length]; + for (int i = 0; i < resolvers.length; i++) { + args[i] = resolvers[i].apply(env); } - return method.invoke(null, args); - }catch (InvocationTargetException e) { - e.printStackTrace(); - if(e.getCause() instanceof Exception) { + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Exception) { throw (Exception) e.getCause(); - }else { + } else { throw e; } - - }catch (Exception e) { - e.printStackTrace(); + } catch (Exception e) { throw e; } }; - fetcher = diretives.wrap(method, meta, fetcher); - if(authorizer != null) { - fetcher = authorizer.wrap(fetcher, method); - } return fetcher; } - - - static GraphQLOutputType getType(TypeMeta meta, Annotation[] annotations) { - - - GraphQLOutputType toReturn = getTypeInner(meta.getType(), annotations, meta.getName()); - boolean required = true; - for(var flag: meta.getFlags()) { - if(flag == Flag.OPTIONAL) { - required = false; - } - if(flag == Flag.ARRAY) { - if(required) { - toReturn = GraphQLNonNull.nonNull(toReturn); - } - toReturn = GraphQLList.list(toReturn); - required = true; - } + private Function buildResolver(String name, TypeMeta argMeta) { + var type = argMeta.getType(); + if (type.isAssignableFrom(DataFetchingEnvironment.class)) { + return env -> env; } - if(required) { - toReturn = GraphQLNonNull.nonNull(toReturn); + if (type.isAssignableFrom(GraphQLContext.class)) { + return env -> env.getGraphQlContext(); } - return toReturn; - } - - - private static GraphQLOutputType getTypeInner(Class type, Annotation[] annotations, String name) { - for(Annotation an: annotations) { - if(an.annotationType().equals(Id.class)) { - return Scalars.GraphQLID; - } - } + var resolver = entityProcessor.getResolver(argMeta); - if(type.equals(Boolean.class) || type.equals(Boolean.TYPE)) { - return Scalars.GraphQLBoolean; - }else if(type.equals(Float.class) || type.equals(Float.TYPE)) { - return Scalars.GraphQLFloat; - }else if(type.equals(Double.class) || type.equals(Double.TYPE)) { - return Scalars.GraphQLFloat; - }else if(type.equals(Integer.class) || type.equals(Integer.TYPE)) { - return Scalars.GraphQLInt; - }else if(type.equals(Long.class) || type.equals(Integer.TYPE)) { - return LONG_SCALAR; - }else if(type.equals(Long.class) || type.equals(Long.TYPE)) { - return Scalars.GraphQLInt; - }else if(type.equals(Short.class) || type.equals(Short.TYPE)) { - return Scalars.GraphQLInt; - }else if(type.equals(String.class)) { - return Scalars.GraphQLString; - }else if(type.equals(Instant.class)) { - return INSTANT_SCALAR; - }else if(type.equals(LocalDate.class)) { - return DATE_SCALAR; - }else if(type.equals(ZoneId.class)) { - return ZONE_ID_SCALAR; - }else if(type.equals(Duration.class)) { - return DURATION_SCALAR; - }else if(type.equals(MonthDay.class)) { - return MONTH_DAY_SCALAR; - }else if(type.equals(YearMonth.class)) { - return YEAR_MONTH_SCALAR; - } - - - if(type.isEnum()) { - return GraphQLTypeReference.typeRef(name); - } - - if(type.isAnnotationPresent(Entity.class)) { - return GraphQLTypeReference.typeRef(name); - } - - if(type.isAnnotationPresent(Scalar.class)) { - return GraphQLTypeReference.typeRef(name); - } - - throw new RuntimeException("Unsupport type " + type); - } - - - static GraphQLInputType getInputType(TypeMeta meta, Annotation[] annotations) { - - GraphQLInputType toReturn = getInputTypeInner(meta.getType(), annotations, meta.getInputName()); - - boolean required = true; - for(var flag: meta.getFlags()) { - if(flag == Flag.OPTIONAL) { - required = false; + return env -> { + var localContext = env.getLocalContext(); + if (localContext != null && type.isAssignableFrom(localContext.getClass())) { + return localContext; } - if(flag == Flag.ARRAY) { - if(required) { - toReturn = GraphQLNonNull.nonNull(toReturn); - } - toReturn = GraphQLList.list(toReturn); - required = true; - } - } - if(required) { - toReturn = GraphQLNonNull.nonNull(toReturn); - } - return toReturn; - } - - private static GraphQLInputType getInputTypeInner(Class type, Annotation[] annotations, String name) { - for(Annotation an: annotations) { - if(an.annotationType().equals(Id.class)) { - return Scalars.GraphQLID; + var context = env.getContext(); + if (context != null && type.isAssignableFrom(context.getClass())) { + return context; } - } - if(type.equals(Boolean.class) || type.equals(Boolean.TYPE)) { - return Scalars.GraphQLBoolean; - }else if(type.equals(Float.class) || type.equals(Float.TYPE)) { - return Scalars.GraphQLFloat; - }else if(type.equals(Double.class) || type.equals(Double.TYPE)) { - return Scalars.GraphQLFloat; - }else if(type.equals(Integer.class) || type.equals(Integer.TYPE)) { - return Scalars.GraphQLInt; - }else if(type.equals(Long.class) || type.equals(Long.TYPE)) { - return LONG_SCALAR; - }else if(type.equals(Short.class) || type.equals(Short.TYPE)) { - return Scalars.GraphQLInt; - }else if(type.equals(String.class)) { - return Scalars.GraphQLString; - }else if(type.equals(Instant.class)) { - return INSTANT_SCALAR; - }else if(type.equals(LocalDate.class)) { - return DATE_SCALAR; - }else if(type.equals(ZoneId.class)) { - return ZONE_ID_SCALAR; - }else if(type.equals(Duration.class)) { - return DURATION_SCALAR; - }else if(type.equals(MonthDay.class)) { - return MONTH_DAY_SCALAR; - }else if(type.equals(YearMonth.class)) { - return YEAR_MONTH_SCALAR; - } - - - - - if(type.isEnum()) { - return GraphQLTypeReference.typeRef(name); - } - - if(type.isAnnotationPresent(Scalar.class)) { - return GraphQLTypeReference.typeRef(name); - } - - - if(type.isAnnotationPresent(Entity.class)) { - return GraphQLTypeReference.typeRef(name); - } - - throw new RuntimeException("Unsupport type " + type); + context = env.getGraphQlContext().get(name); + if (context != null && type.isAssignableFrom(context.getClass())) { + return context; + } + var arg = env.getArgument(name); + return resolver.convert(arg, env.getGraphQlContext(), env.getLocale()); + }; } public static GraphQLSchema build(String... classPath) throws ReflectiveOperationException { - - - Reflections reflections = new Reflections(classPath, new SubTypesScanner(), new MethodAnnotationsScanner(), new TypeAnnotationsScanner()); + Reflections reflections = new Reflections(classPath, Scanners.SubTypes, Scanners.MethodsAnnotated, Scanners.TypesAnnotated); Set> authorizers = reflections.getSubTypesOf(Authorizer.class); //want to make everything split by package AuthorizerSchema authorizer = AuthorizerSchema.build(new HashSet<>(Arrays.asList(classPath)), authorizers); Set> schemaConfiguration = reflections.getSubTypesOf(SchemaConfiguration.class); - - + Set> dierctivesTypes = reflections.getTypesAnnotatedWith(Directive.class); - + Set> restrict = reflections.getTypesAnnotatedWith(Restrict.class); Set> restricts = reflections.getTypesAnnotatedWith(Restricts.class); List> globalRestricts = new ArrayList<>(); - - for(var r: restrict) { + + for (var r : restrict) { Restrict annotation = r.getAnnotation(Restrict.class); var factoryClass = annotation.value(); var factory = factoryClass.getConstructor().newInstance(); - if(!factory.extractType().isAssignableFrom(r)) { + if (!factory.extractType().isAssignableFrom(r)) { throw new RuntimeException("Restrict annotation does match class applied to targets" + factory.extractType() + " but was on class " + r); } globalRestricts.add(factory); } - - for(var r: restricts) { + + for (var r : restricts) { Restricts annotations = r.getAnnotation(Restricts.class); - for (Restrict annotation: annotations.value()) { + for (Restrict annotation : annotations.value()) { var factoryClass = annotation.value(); var factory = factoryClass.getConstructor().newInstance(); - - if(!factory.extractType().isAssignableFrom(r)) { + + if (!factory.extractType().isAssignableFrom(r)) { throw new RuntimeException("Restrict annotation does match class applied to targets" + factory.extractType() + " but was on class " + r); } globalRestricts.add(factory); } } - + DirectivesSchema diretivesSchema = DirectivesSchema.build(globalRestricts, dierctivesTypes); - + Set> types = reflections.getTypesAnnotatedWith(Entity.class); - - Set> scalars = reflections.getTypesAnnotatedWith(Scalar.class); - + var mutations = reflections.getMethodsAnnotatedWith(Mutation.class); var subscriptions = reflections.getMethodsAnnotatedWith(Subscription.class); var queries = reflections.getMethodsAnnotatedWith(Query.class); - + var endPoints = new HashSet<>(mutations); endPoints.addAll(subscriptions); endPoints.addAll(queries); - + types.removeIf(t -> t.getDeclaredAnnotation(Entity.class) == null); types.removeIf(t -> t.isAnonymousClass()); - scalars.removeIf(t -> t.isAnonymousClass()); - - return new SchemaBuilder(diretivesSchema, authorizer).process(endPoints).processTypes(types).build(schemaConfiguration); - } - - - + return new SchemaBuilder(diretivesSchema, authorizer).processTypes(types).process(endPoints).build(schemaConfiguration); + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java b/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java index 7a2240b..9f740b4 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java @@ -1,9 +1,7 @@ -package com.fleetpin.graphql.builder; - -/** - * Used to add extra information to the schema. Currently only supports directives. That are inferred using the annotation - * - */ -public interface SchemaConfiguration { - -} +package com.fleetpin.graphql.builder; + +/** + * Used to add extra information to the schema. Currently only supports directives. That are inferred using the annotation + * + */ +public interface SchemaConfiguration {} diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java new file mode 100644 index 0000000..9c4336a --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java @@ -0,0 +1,350 @@ +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.GraphQLDeprecated; +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; +import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; +import com.fleetpin.graphql.builder.annotations.Scalar; +import graphql.schema.DataFetcher; +import graphql.schema.FieldCoordinates; +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLInterfaceType; +import graphql.schema.GraphQLNamedOutputType; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLObjectType.Builder; +import graphql.schema.GraphQLTypeReference; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.TypeVariable; + +public abstract class TypeBuilder { + + protected final EntityProcessor entityProcessor; + protected final TypeMeta meta; + + public TypeBuilder(EntityProcessor entityProcessor, TypeMeta meta) { + this.entityProcessor = entityProcessor; + this.meta = meta; + } + + public GraphQLNamedOutputType buildType() throws ReflectiveOperationException { + Builder graphType = GraphQLObjectType.newObject(); + String typeName = EntityUtil.getName(meta); + graphType.name(typeName); + + GraphQLInterfaceType.Builder interfaceBuilder = GraphQLInterfaceType.newInterface(); + interfaceBuilder.name(typeName); + var type = meta.getType(); + { + var description = type.getAnnotation(GraphQLDescription.class); + if (description != null) { + graphType.description(description.value()); + interfaceBuilder.description(description.value()); + } + } + + processFields(typeName, graphType, interfaceBuilder); + + boolean unmappedGenerics = meta.hasUnmappedGeneric(); + + if (unmappedGenerics) { + var name = EntityUtil.getName(meta.notDirect()); + graphType.withInterface(GraphQLTypeReference.typeRef(name)); + if (meta.isDirect()) { + interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(name)); + } + } + Class parent = type.getSuperclass(); + while (parent != null) { + if (parent.isAnnotationPresent(Entity.class)) { + TypeMeta innerMeta = new TypeMeta(meta, parent, type.getGenericSuperclass()); + var interfaceName = entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); + graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + + if (!parent.equals(type.getGenericSuperclass())) { + innerMeta = new TypeMeta(meta, parent, parent); + interfaceName = entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); + graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + } + + var genericMeta = new TypeMeta(null, parent, parent); + if (!EntityUtil.getName(innerMeta).equals(EntityUtil.getName(genericMeta))) { + interfaceName = entityProcessor.getEntity(genericMeta).getInnerType(genericMeta); + graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + } + } + parent = parent.getSuperclass(); + } + //generics + TypeMeta innerMeta = new TypeMeta(meta, type, type); + if (!EntityUtil.getName(innerMeta).equals(typeName)) { + var interfaceName = entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); + graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + } + innerMeta = new TypeMeta(null, type, type); + if (!EntityUtil.getName(innerMeta).equals(typeName)) { + var interfaceName = entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); + graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + } + + boolean interfaceable = type.isInterface() || Modifier.isAbstract(type.getModifiers()); + if (!meta.isDirect() && (interfaceable || unmappedGenerics)) { + entityProcessor.addSchemaDirective(type, type, interfaceBuilder::withAppliedDirective); + GraphQLInterfaceType built = interfaceBuilder.build(); + + entityProcessor + .getCodeRegistry() + .typeResolver( + built.getName(), + env -> { + if (type.isInstance(env.getObject())) { + var meta = new TypeMeta(null, env.getObject().getClass(), env.getObject().getClass()); + var t = entityProcessor.getEntity(meta).getInnerType(null); + if (!(t instanceof GraphQLObjectType)) { + t = entityProcessor.getEntity(meta.direct()).getInnerType(null); + } + try { + return (GraphQLObjectType) t; + } catch (ClassCastException e) { + throw e; + } + } + return null; + } + ); + + if (unmappedGenerics && !meta.isDirect()) { + var directType = meta.direct(); + entityProcessor.getEntity(directType).getInnerType(directType); + } + return built; + } + + entityProcessor.addSchemaDirective(type, type, graphType::withAppliedDirective); + var built = graphType.build(); + entityProcessor + .getCodeRegistry() + .typeResolver( + built.getName(), + env -> { + if (type.isInstance(env.getObject())) { + return built; + } + return null; + } + ); + return built; + } + + protected abstract void processFields(String typeName, Builder graphType, graphql.schema.GraphQLInterfaceType.Builder interfaceBuilder) + throws ReflectiveOperationException; + + private static DataFetcher buildDirectiveWrapper(DirectivesSchema diretives, Method method, TypeMeta meta) { + DataFetcher fetcher = env -> { + Object[] args = new Object[method.getParameterCount()]; + for (int i = 0; i < args.length; i++) { + if (method.getParameterTypes()[i].isAssignableFrom(env.getClass())) { + args[i] = env; + } else if (method.getParameterTypes()[i].isAssignableFrom(env.getContext().getClass())) { + args[i] = env.getContext(); + } else { + Object obj = env.getArgument(method.getParameters()[i].getName()); + args[i] = obj; + } + } + try { + return method.invoke(env.getSource(), args); + } catch (Exception e) { + if (e.getCause() instanceof Exception) { + throw (Exception) e.getCause(); + } else { + throw e; + } + } + }; + + fetcher = diretives.wrap(method, meta, fetcher); + return fetcher; + } + + private String typeNameLookup(Object obj) { + var type = obj.getClass(); + String name = null; + + if (type.isEnum()) { + name = type.getSimpleName(); + } + if (type.isAnnotationPresent(Scalar.class)) { + name = type.getSimpleName(); + } + if (type.isAnnotationPresent(Entity.class)) { + name = type.getSimpleName(); + } + + for (var t : type.getTypeParameters()) { + for (var method : type.getMethods()) { + var methodType = method.getGenericReturnType(); + if (methodType instanceof TypeVariable) { + var typeVariable = ((TypeVariable) methodType); + if (typeVariable.equals(t)) { + //maybe we should dig through private fields first + try { + name += "_" + typeNameLookup(method.invoke(obj)); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Could not infer type with regard to generics."); + } //TODO: might have arguments might be a future which would make impossible to resolve + } + } + } + } + + return name; + } + + public static class ObjectType extends TypeBuilder { + + public ObjectType(EntityProcessor entityProcessor, TypeMeta meta) { + super(entityProcessor, meta); + } + + @Override + protected void processFields(String typeName, Builder graphType, graphql.schema.GraphQLInterfaceType.Builder interfaceBuilder) + throws ReflectiveOperationException { + var type = meta.getType(); + for (Method method : type.getMethods()) { + try { + if (method.isSynthetic()) { + continue; + } + if (method.getDeclaringClass().equals(Object.class)) { + continue; + } + if (method.isAnnotationPresent(GraphQLIgnore.class)) { + continue; + } + //will also be on implementing class + if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { + continue; + } + if (Modifier.isStatic(method.getModifiers())) { + continue; + } else { + //getter type + if (method.getName().matches("(get|is)[A-Z].*")) { + String name; + if (method.getName().startsWith("get")) { + name = + method.getName().substring("get".length(), "get".length() + 1).toLowerCase() + + method.getName().substring("get".length() + 1); + } else { + name = + method.getName().substring("is".length(), "is".length() + 1).toLowerCase() + method.getName().substring("is".length() + 1); + } + + GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); + field.name(name); + entityProcessor.addSchemaDirective(method, type, field::withAppliedDirective); + var deprecated = method.getAnnotation(GraphQLDeprecated.class); + if (deprecated != null) { + field.deprecate(deprecated.value()); + } + var description = method.getAnnotation(GraphQLDescription.class); + if (description != null) { + field.description(description.value()); + } + + TypeMeta innerMeta = new TypeMeta(meta, method.getReturnType(), method.getGenericReturnType()); + + field.type(entityProcessor.getType(innerMeta, method.getAnnotations())); + graphType.field(field); + interfaceBuilder.field(field); + + var directives = entityProcessor.getDirectives(); + var codeRegistry = entityProcessor.getCodeRegistry(); + + if (method.getParameterCount() > 0 || directives.target(method, innerMeta)) { + codeRegistry.dataFetcher(FieldCoordinates.coordinates(typeName, name), buildDirectiveWrapper(directives, method, innerMeta)); + } + } + } + } catch (RuntimeException e) { + throw new RuntimeException("Failed to process method " + method, e); + } + } + } + } + + public static class Record extends TypeBuilder { + + public Record(EntityProcessor entityProcessor, TypeMeta meta) { + super(entityProcessor, meta); + } + + @Override + protected void processFields(String typeName, Builder graphType, graphql.schema.GraphQLInterfaceType.Builder interfaceBuilder) + throws ReflectiveOperationException { + var type = meta.getType(); + + for (var field : type.getDeclaredFields()) { + try { + if (field.isSynthetic()) { + continue; + } + if (field.getDeclaringClass().equals(Object.class)) { + continue; + } + if (field.isAnnotationPresent(GraphQLIgnore.class)) { + continue; + } + //will also be on implementing class + if (Modifier.isAbstract(field.getModifiers()) || field.getDeclaringClass().isInterface()) { + continue; + } + if (Modifier.isStatic(field.getModifiers())) { + continue; + } else { + var method = type.getMethod(field.getName()); + if (method.isAnnotationPresent(GraphQLIgnore.class)) { + continue; + } + //getter type + String name = field.getName(); + + GraphQLFieldDefinition.Builder fieldBuilder = GraphQLFieldDefinition.newFieldDefinition(); + fieldBuilder.name(name); + entityProcessor.addSchemaDirective(method, type, fieldBuilder::withAppliedDirective); + var deprecated = field.getAnnotation(GraphQLDeprecated.class); + if (deprecated != null) { + fieldBuilder.deprecate(deprecated.value()); + } + var description = field.getAnnotation(GraphQLDescription.class); + if (description != null) { + fieldBuilder.description(description.value()); + } + + TypeMeta innerMeta = new TypeMeta(meta, method.getReturnType(), method.getGenericReturnType()); + fieldBuilder.type(entityProcessor.getType(innerMeta, method.getAnnotations())); + graphType.field(fieldBuilder); + interfaceBuilder.field(fieldBuilder); + + var directives = entityProcessor.getDirectives(); + + if (method.getParameterCount() > 0 || directives.target(method, innerMeta)) { + entityProcessor + .getCodeRegistry() + .dataFetcher(FieldCoordinates.coordinates(typeName, name), buildDirectiveWrapper(directives, method, innerMeta)); + } + } + } catch (RuntimeException e) { + throw new RuntimeException("Failed to process method " + field, e); + } + } + } + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java index 762b663..6e410c6 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java @@ -22,82 +22,85 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; - import org.reactivestreams.Publisher; - public class TypeMeta { - enum Flag{ASYNC, ARRAY, OPTIONAL, SUBSCRIPTION} + enum Flag { + ASYNC, + ARRAY, + OPTIONAL, + SUBSCRIPTION, + DIRECT, + } private List flags; private Type genericType; private Class type; + private boolean direct; - private final EntityProcessor entityProcessor; private final TypeMeta parent; - TypeMeta(EntityProcessor entityProcessor, TypeMeta parent, Class type, Type genericType) { + private List> types; + + TypeMeta(TypeMeta parent, Class type, Type genericType) { this.parent = parent; - this.entityProcessor = entityProcessor; flags = new ArrayList<>(); + types = new ArrayList<>(); process(type, genericType); Collections.reverse(flags); } private void processGeneric(TypeMeta target, TypeVariable type) { var owningClass = target.genericType; - if(owningClass == null) { + if (owningClass == null) { owningClass = target.type; } - if(owningClass instanceof Class) { - findType(target, type, (Class) owningClass); - }else if(owningClass instanceof ParameterizedType) { + if (owningClass instanceof Class) { + findType(target, type, (Class) owningClass); + } else if (owningClass instanceof ParameterizedType) { var pt = (ParameterizedType) owningClass; - if(!matchType(target, type.getTypeName(), pt, true)) { + if (!matchType(target, type.getTypeName(), pt, true)) { throw new UnsupportedOperationException("Does not handle type " + owningClass); } - }else { + } else { throw new UnsupportedOperationException("Does not handle type " + owningClass); } - - } private boolean matchType(TypeMeta target, String typeName, ParameterizedType type, boolean parent) { var raw = (Class) type.getRawType(); - while(raw != null) { - for(int i = 0; i < raw.getTypeParameters().length; i++) { + while (raw != null) { + for (int i = 0; i < raw.getTypeParameters().length; i++) { var arg = type.getActualTypeArguments()[i]; var param = raw.getTypeParameters()[i]; - if(param.getTypeName().equals(typeName)) { - if(arg instanceof TypeVariable) { - if(parent) { + if (param.getTypeName().equals(typeName)) { + if (arg instanceof TypeVariable) { + if (parent) { processGeneric(target.parent, (TypeVariable) arg); - }else { + } else { processGeneric(target, (TypeVariable) arg); } return true; - }else if(arg instanceof WildcardType) { - for(var bound: param.getBounds()) { - if(bound instanceof ParameterizedType) { - process((Class) ((ParameterizedType)bound).getRawType(), bound); - }else if(bound instanceof TypeVariable) { - processGeneric(target, (TypeVariable)bound); - }else { + } else if (arg instanceof WildcardType) { + for (var bound : param.getBounds()) { + if (bound instanceof ParameterizedType) { + process((Class) ((ParameterizedType) bound).getRawType(), bound); + } else if (bound instanceof TypeVariable) { + processGeneric(target, (TypeVariable) bound); + } else { process((Class) bound, null); } } return true; - }else { + } else { var klass = (Class) arg; process(klass, klass); return true; } } - } raw = raw.getSuperclass(); } @@ -107,115 +110,118 @@ private boolean matchType(TypeMeta target, String typeName, ParameterizedType ty private void findType(TypeMeta target, TypeVariable type, Class start) { var startClass = (Class) start; var genericDeclaration = type.getGenericDeclaration(); - if(start.equals(genericDeclaration) ) { + if (start.equals(genericDeclaration)) { //we don't have any implementing logic we are at this level so take the bounds - for(var bound: type.getBounds()) { - if(bound instanceof ParameterizedType) { - process((Class) ((ParameterizedType)bound).getRawType(), bound); - }else if(bound instanceof TypeVariable) { - processGeneric(target, (TypeVariable)bound); - }else { + for (var bound : type.getBounds()) { + if (bound instanceof ParameterizedType) { + process((Class) ((ParameterizedType) bound).getRawType(), bound); + } else if (bound instanceof TypeVariable) { + processGeneric(target, (TypeVariable) bound); + } else { process((Class) bound, null); } } } - if(startClass.getSuperclass() != null && startClass.getSuperclass().equals(genericDeclaration)) { + if (startClass.getSuperclass() != null && startClass.getSuperclass().equals(genericDeclaration)) { var generic = (ParameterizedType) startClass.getGenericSuperclass(); - if(matchType(target, type.getTypeName(), generic, false)) { + if (matchType(target, type.getTypeName(), generic, false)) { return; } } - for(var inter: startClass.getGenericInterfaces()) { - if(inter instanceof ParameterizedType) { + for (var inter : startClass.getGenericInterfaces()) { + if (inter instanceof ParameterizedType) { var generic = (ParameterizedType) inter; - if(generic.getRawType().equals(genericDeclaration)) { - if(matchType(target, type.getTypeName(), generic, false)) { + if (generic.getRawType().equals(genericDeclaration)) { + if (matchType(target, type.getTypeName(), generic, false)) { return; } } } } - if(startClass.getSuperclass() != null) { + if (startClass.getSuperclass() != null) { findType(target, type, startClass.getSuperclass()); } - for(var inter: startClass.getInterfaces()) { + for (var inter : startClass.getInterfaces()) { findType(target, type, inter); } } private void process(Class type, Type genericType) { - - if(type.isArray()) { + if (type.isArray()) { flags.add(Flag.ARRAY); + types.add(type); process(type.getComponentType(), null); return; } - if(Collection.class.isAssignableFrom(type)) { + if (Collection.class.isAssignableFrom(type)) { flags.add(Flag.ARRAY); + types.add(type); genericType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; - if(genericType instanceof ParameterizedType) { - process((Class) ((ParameterizedType)genericType).getRawType(), genericType); - }else if(genericType instanceof TypeVariable) { - processGeneric(parent, (TypeVariable)genericType); - }else { + if (genericType instanceof ParameterizedType) { + process((Class) ((ParameterizedType) genericType).getRawType(), genericType); + } else if (genericType instanceof TypeVariable) { + processGeneric(parent, (TypeVariable) genericType); + } else { process((Class) genericType, null); } return; } - if(CompletableFuture.class.isAssignableFrom(type)) { + if (CompletableFuture.class.isAssignableFrom(type)) { flags.add(Flag.ASYNC); + types.add(type); genericType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; - if(genericType instanceof ParameterizedType) { - process((Class) ((ParameterizedType)genericType).getRawType(), genericType); - }else if(genericType instanceof TypeVariable) { - processGeneric(parent, (TypeVariable)genericType); - }else { + if (genericType instanceof ParameterizedType) { + process((Class) ((ParameterizedType) genericType).getRawType(), genericType); + } else if (genericType instanceof TypeVariable) { + processGeneric(parent, (TypeVariable) genericType); + } else { process((Class) genericType, null); } return; - } - if(Optional.class.isAssignableFrom(type)) { + if (Optional.class.isAssignableFrom(type)) { flags.add(Flag.OPTIONAL); + types.add(type); genericType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; - if(genericType instanceof ParameterizedType) { - process((Class) ((ParameterizedType)genericType).getRawType(), genericType); - }else if(genericType instanceof TypeVariable) { - processGeneric(parent, (TypeVariable)genericType); - }else { + if (genericType instanceof ParameterizedType) { + process((Class) ((ParameterizedType) genericType).getRawType(), genericType); + } else if (genericType instanceof TypeVariable) { + processGeneric(parent, (TypeVariable) genericType); + } else { process((Class) genericType, null); } return; } - - if(Publisher.class.isAssignableFrom(type)) { + if (Publisher.class.isAssignableFrom(type)) { flags.add(Flag.SUBSCRIPTION); + types.add(type); genericType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; - if(genericType instanceof ParameterizedType) { - process((Class) ((ParameterizedType)genericType).getRawType(), genericType); - }else if(genericType instanceof TypeVariable) { - processGeneric(parent, (TypeVariable)genericType); - }else { + if (genericType instanceof ParameterizedType) { + process((Class) ((ParameterizedType) genericType).getRawType(), genericType); + } else if (genericType instanceof TypeVariable) { + processGeneric(parent, (TypeVariable) genericType); + } else { process((Class) genericType, null); } return; } - if(genericType != null && genericType instanceof TypeVariable) { + if (genericType != null && genericType instanceof TypeVariable) { processGeneric(parent, (TypeVariable) genericType); return; } this.type = type; this.genericType = genericType; + types.add(type); } - Class getType() { + public Class getType() { return type; } - + public Type getGenericType() { return genericType; } @@ -224,88 +230,75 @@ public List getFlags() { return flags; } - public String getName() { - return entityProcessor.process(this); - } - - public String getInputName() { - return entityProcessor.processInput(this); + public List> getTypes() { + return types; } public boolean hasUnmappedGeneric() { - if(type.getTypeParameters().length == 0) { + if (type.getTypeParameters().length == 0) { return false; } - if(genericType == null || !(genericType instanceof ParameterizedType)) { + if (genericType == null || !(genericType instanceof ParameterizedType)) { return true; } - + ParameterizedType pt = (ParameterizedType) genericType; - - for(var type: pt.getActualTypeArguments()) { - if(type instanceof TypeVariable) { - if(resolveToType((TypeVariable) type) == null) { + + for (var type : pt.getActualTypeArguments()) { + if (type instanceof TypeVariable) { + if (resolveToType((TypeVariable) type) == null) { return true; } - }else if(!(type instanceof Class)) { + } else if (!(type instanceof Class)) { return true; } } return false; - } - - - public Class resolveToType(TypeVariable variable) { var parent = this.parent; - + var parentType = type; var genericType = this.genericType; - - while(true) { - if(genericType instanceof ParameterizedType) { - var pt = parentType.getTypeParameters(); - for(int i =0; i< pt.length; i++) { + while (true) { + if (genericType instanceof ParameterizedType) { + var pt = parentType.getTypeParameters(); + for (int i = 0; i < pt.length; i++) { var p = pt[i]; - if(p.equals(variable)) { - var generic = (ParameterizedType)genericType; //safe as has to if equal vaiable + if (p.equals(variable)) { + var generic = (ParameterizedType) genericType; //safe as has to if equal vaiable var implementingType = generic.getActualTypeArguments()[i]; - if(implementingType instanceof Class) { + if (implementingType instanceof Class) { return (Class) implementingType; - }else if (implementingType instanceof TypeVariable){ + } else if (implementingType instanceof TypeVariable) { return resolveToType((TypeVariable) implementingType); - }else { + } else { throw new RuntimeException("Generics are more complex that logic currently can handle"); } - } } } - + var pt = parentType.getSuperclass().getTypeParameters(); - for(int i =0; i< pt.length; i++) { + for (int i = 0; i < pt.length; i++) { var p = pt[i]; - if(p.equals(variable)) { - + if (p.equals(variable)) { var superClass = (ParameterizedType) parentType.getGenericSuperclass(); //safe as has to if equal vaiable var implementingType = superClass.getActualTypeArguments()[i]; - - if(implementingType instanceof Class) { + + if (implementingType instanceof Class) { return (Class) implementingType; - }else if (implementingType instanceof TypeVariable){ + } else if (implementingType instanceof TypeVariable) { return resolveToType((TypeVariable) implementingType); - }else { + } else { throw new RuntimeException("Generics are more complex that logic currently can handle"); } - } - } - - if(parent == null) { + + if (parent == null) { break; } parentType = parent.getType(); @@ -315,5 +308,22 @@ public Class resolveToType(TypeVariable variable) { return null; } + public void optional() { + flags.add(Flag.OPTIONAL); + } + public TypeMeta direct() { + var toReturn = new TypeMeta(parent, type, genericType); + toReturn.direct = true; + return toReturn; + } + + public boolean isDirect() { + return direct; + } + + public TypeMeta notDirect() { + var toReturn = new TypeMeta(parent, type, genericType); + return toReturn; + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/UnionType.java b/src/main/java/com/fleetpin/graphql/builder/UnionType.java new file mode 100644 index 0000000..0892583 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/UnionType.java @@ -0,0 +1,72 @@ +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.Union; +import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; +import graphql.schema.GraphQLNamedInputType; +import graphql.schema.GraphQLNamedOutputType; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLTypeReference; +import graphql.schema.GraphQLUnionType; + +public class UnionType extends EntityHolder { + + private final EntityProcessor entityProcessor; + private final Union union; + + public UnionType(EntityProcessor entityProcessor, Union union) { + this.entityProcessor = entityProcessor; + this.union = union; + } + + @Override + protected GraphQLNamedOutputType buildType() { + String name = buildInputName(); + var builder = GraphQLUnionType.newUnionType(); + builder.name(name); + + for (var type : union.value()) { + var possible = entityProcessor.getEntity(type).getInnerType(new TypeMeta(null, type, type)); + builder.possibleType(GraphQLTypeReference.typeRef(possible.getName())); + } + + entityProcessor + .getCodeRegistry() + .typeResolver( + name, + env -> { + for (var type : union.value()) { + if (type.isInstance(env.getObject())) { + return (GraphQLObjectType) entityProcessor.getEntity(type).getInnerType(new TypeMeta(null, type, type)); + } + } + throw new RuntimeException("Union " + name + " Does not support type " + env.getObject().getClass().getSimpleName()); + } + ); + return builder.build(); + } + + @Override + protected GraphQLNamedInputType buildInput() { + return null; + } + + @Override + protected String buildInputName() { + return name(union); + } + + static String name(Union union) { + StringBuilder name = new StringBuilder("Union"); + + for (var type : union.value()) { + name.append("_"); + name.append(EntityUtil.getName(new TypeMeta(null, type, type))); + } + return name.toString(); + } + + @Override + protected InputTypeBuilder buildResolver() { + return null; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/YearMonthCoercing.java b/src/main/java/com/fleetpin/graphql/builder/YearMonthCoercing.java index 1acfb72..a7cfb0e 100644 --- a/src/main/java/com/fleetpin/graphql/builder/YearMonthCoercing.java +++ b/src/main/java/com/fleetpin/graphql/builder/YearMonthCoercing.java @@ -1,36 +1,34 @@ -package com.fleetpin.graphql.builder; - -import java.time.YearMonth; - -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; - -public class YearMonthCoercing implements Coercing { - - @Override - public YearMonth serialize(Object dataFetcherResult) throws CoercingSerializeException { - return convertImpl(dataFetcherResult); - } - - @Override - public YearMonth parseValue(Object input) throws CoercingParseValueException { - return convertImpl(input); - } - - @Override - public YearMonth parseLiteral(Object input) throws CoercingParseLiteralException { - return convertImpl(input); - } - - - private YearMonth convertImpl(Object input) { - if (input instanceof YearMonth) { - return (YearMonth) input; - } else if (input instanceof String) { - return YearMonth.parse((String) input); - } - return null; - } -} \ No newline at end of file +package com.fleetpin.graphql.builder; + +import graphql.schema.Coercing; +import graphql.schema.CoercingParseLiteralException; +import graphql.schema.CoercingParseValueException; +import graphql.schema.CoercingSerializeException; +import java.time.YearMonth; + +public class YearMonthCoercing implements Coercing { + + @Override + public YearMonth serialize(Object dataFetcherResult) throws CoercingSerializeException { + return convertImpl(dataFetcherResult); + } + + @Override + public YearMonth parseValue(Object input) throws CoercingParseValueException { + return convertImpl(input); + } + + @Override + public YearMonth parseLiteral(Object input) throws CoercingParseLiteralException { + return convertImpl(input); + } + + private YearMonth convertImpl(Object input) { + if (input instanceof YearMonth) { + return (YearMonth) input; + } else if (input instanceof String) { + return YearMonth.parse((String) input); + } + return null; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/ZoneIdCoercing.java b/src/main/java/com/fleetpin/graphql/builder/ZoneIdCoercing.java index c3b6c31..6a996ac 100644 --- a/src/main/java/com/fleetpin/graphql/builder/ZoneIdCoercing.java +++ b/src/main/java/com/fleetpin/graphql/builder/ZoneIdCoercing.java @@ -12,12 +12,11 @@ package com.fleetpin.graphql.builder; -import java.time.ZoneId; - import graphql.schema.Coercing; import graphql.schema.CoercingParseLiteralException; import graphql.schema.CoercingParseValueException; import graphql.schema.CoercingSerializeException; +import java.time.ZoneId; public class ZoneIdCoercing implements Coercing { @@ -35,16 +34,13 @@ public ZoneId parseValue(Object input) throws CoercingParseValueException { public ZoneId parseLiteral(Object input) throws CoercingParseLiteralException { return convertImpl(input); } - - - private ZoneId convertImpl(Object input) { - if (input instanceof ZoneId) { - return (ZoneId) input; - } else if (input instanceof String) { - return ZoneId.of((String) input); - } - return null; - } - + private ZoneId convertImpl(Object input) { + if (input instanceof ZoneId) { + return (ZoneId) input; + } else if (input instanceof String) { + return ZoneId.of((String) input); + } + return null; + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java index 06e35a9..a73cc2a 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java @@ -21,5 +21,4 @@ @Retention(RUNTIME) @Target(ElementType.TYPE) public @interface Context { - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java index 21ab8c6..eaa0564 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java @@ -14,15 +14,13 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.fleetpin.graphql.builder.DirectiveOperation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.fleetpin.graphql.builder.DirectiveOperation; - @Retention(RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Directive { Class> value(); - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java index 9d5dac4..84063ce 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java @@ -22,5 +22,4 @@ @Target(ElementType.TYPE) public @interface Entity { SchemaOption value() default SchemaOption.TYPE; - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java index 3a6c529..2881f50 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java @@ -19,7 +19,7 @@ import java.lang.annotation.Target; @Retention(RUNTIME) -@Target({ElementType.METHOD}) +@Target({ ElementType.METHOD }) public @interface GraphQLDeprecated { public String value(); } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java index 6fa0bf0..7dce7ef 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java @@ -19,7 +19,7 @@ import java.lang.annotation.Target; @Retention(RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) +@Target({ ElementType.METHOD, ElementType.TYPE }) public @interface GraphQLDescription { public String value(); } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java index 1f174a1..f5f6887 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java @@ -19,8 +19,6 @@ import java.lang.annotation.Target; @Retention(RUNTIME) -@Target({ElementType.METHOD, ElementType.FIELD}) +@Target({ ElementType.METHOD, ElementType.FIELD }) public @interface GraphQLIgnore { - - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java index 2f9e280..4a4eda4 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java @@ -19,8 +19,6 @@ import java.lang.annotation.Target; @Retention(RUNTIME) -@Target({ElementType.METHOD, ElementType.PARAMETER}) +@Target({ ElementType.METHOD, ElementType.PARAMETER }) public @interface Id { - - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java b/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java index 34d83ba..cb22079 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java @@ -19,8 +19,6 @@ import java.lang.annotation.Target; @Retention(RUNTIME) -@Target({ElementType.METHOD, ElementType.PARAMETER}) +@Target({ ElementType.METHOD, ElementType.PARAMETER }) public @interface InputIgnore { - - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java index 01a48da..b175b5a 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java @@ -21,5 +21,4 @@ @Retention(RUNTIME) @Target(ElementType.METHOD) public @interface Mutation { - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java b/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java new file mode 100644 index 0000000..e0747eb --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java @@ -0,0 +1,33 @@ +/* + * 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 limitations under + * the License. + */ + +package com.fleetpin.graphql.builder.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(ElementType.TYPE) +public @interface OneOf { + Type[] value(); + + @Retention(RUNTIME) + @Target(ElementType.TYPE) + public @interface Type { + String name(); + + Class type(); + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java index f30a5b2..48b7dab 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java @@ -21,5 +21,4 @@ @Retention(RUNTIME) @Target(ElementType.METHOD) public @interface Query { - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java index 20245c2..49fb153 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java @@ -14,17 +14,15 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.fleetpin.graphql.builder.RestrictTypeFactory; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.fleetpin.graphql.builder.RestrictTypeFactory; - @Retention(RUNTIME) @Target(ElementType.TYPE) @Repeatable(Restricts.class) public @interface Restrict { Class> value(); - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java index fa3c85e..92cf170 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java @@ -1,13 +1,13 @@ -package com.fleetpin.graphql.builder.annotations; - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Retention(RUNTIME) -@Target(ElementType.TYPE) -public @interface Restricts { - Restrict[] value(); -} \ No newline at end of file +package com.fleetpin.graphql.builder.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(ElementType.TYPE) +public @interface Restricts { + Restrict[] value(); +} diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java index f3cbafd..f371f0b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java @@ -14,15 +14,13 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; +import graphql.schema.Coercing; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import graphql.schema.Coercing; - @Retention(RUNTIME) @Target(ElementType.TYPE) public @interface Scalar { Class> value(); - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java b/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java index 95a32f6..9df5277 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java @@ -13,5 +13,7 @@ package com.fleetpin.graphql.builder.annotations; public enum SchemaOption { - INPUT, TYPE, BOTH + INPUT, + TYPE, + BOTH, } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java index e60a270..0cb81ad 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java @@ -21,5 +21,4 @@ @Retention(RUNTIME) @Target(ElementType.METHOD) public @interface Subscription { - } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java new file mode 100644 index 0000000..3ad29b6 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java @@ -0,0 +1,25 @@ +/* + * 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 limitations under + * the License. + */ + +package com.fleetpin.graphql.builder.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(ElementType.METHOD) +public @interface Union { + Class[] value(); +} diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java new file mode 100644 index 0000000..da5dd7a --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java @@ -0,0 +1,8 @@ +package com.fleetpin.graphql.builder.mapper; + +import graphql.GraphQLContext; +import java.util.Locale; + +public interface InputTypeBuilder { + Object convert(Object obj, GraphQLContext graphQLContext, Locale locale); +} diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java new file mode 100644 index 0000000..169be24 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java @@ -0,0 +1,75 @@ +package com.fleetpin.graphql.builder.mapper; + +import com.fleetpin.graphql.builder.EntityProcessor; +import com.fleetpin.graphql.builder.TypeMeta; +import graphql.GraphQLContext; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Map; + +public class ObjectFieldBuilder implements InputTypeBuilder { + + private final InputTypeBuilder map; + + public ObjectFieldBuilder(Class type, ArrayList mappers) { + try { + var constructor = type.getConstructor(); + map = + (obj, context, locale) -> { + try { + if (type.isInstance(obj)) { + return obj; + } + + var toReturn = constructor.newInstance(); + + Map map = (Map) obj; + + for (var mapper : mappers) { + var name = mapper.getName(); + if (map.containsKey(name)) { + mapper.map(toReturn, map.get(name), context, locale); + } + } + + return toReturn; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object convert(Object obj, GraphQLContext graphQLContext, Locale locale) { + return map.convert(obj, graphQLContext, locale); + } + + public static class FieldMapper { + + private final Method method; + private final String name; + private final InputTypeBuilder mapper; + + private FieldMapper(String name, Method method, InputTypeBuilder objectBuilder) { + this.name = name; + this.method = method; + this.mapper = objectBuilder; + } + + public String getName() { + return name; + } + + protected void map(Object inputType, Object argument, GraphQLContext graphQLContext, Locale locale) throws ReflectiveOperationException { + method.invoke(inputType, mapper.convert(argument, graphQLContext, locale)); + } + + public static FieldMapper build(EntityProcessor entityProcessor, TypeMeta inputType, String name, Method method) { + return new FieldMapper(name, method, entityProcessor.getResolver(inputType)); + } + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java new file mode 100644 index 0000000..febe3b4 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java @@ -0,0 +1,46 @@ +package com.fleetpin.graphql.builder.mapper; + +import com.fleetpin.graphql.builder.EntityProcessor; +import com.fleetpin.graphql.builder.annotations.OneOf; +import graphql.GraphQLContext; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public class OneOfBuilder implements InputTypeBuilder { + + private final InputTypeBuilder map; + + public OneOfBuilder(EntityProcessor entityProcessor, Class type, OneOf oneOf) { + Map builders = new HashMap<>(); + + for (var typeOf : oneOf.value()) { + builders.put(typeOf.name(), entityProcessor.getResolver(typeOf.type())); + } + + map = + (obj, context, locale) -> { + if (type.isInstance(obj)) { + return obj; + } + + Map map = (Map) obj; + + if (map.size() > 1) { + throw new RuntimeException("OneOf must only have a single field set"); + } + + for (var entry : map.entrySet()) { + var builder = builders.get(entry.getKey()); + return builder.convert(entry.getValue(), context, locale); + } + + throw new RuntimeException("OneOf must only have a single field set"); + }; + } + + @Override + public Object convert(Object obj, GraphQLContext graphQLContext, Locale locale) { + return map.convert(obj, graphQLContext, locale); + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java new file mode 100644 index 0000000..10c047b --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java @@ -0,0 +1,63 @@ +package com.fleetpin.graphql.builder.mapper; + +import graphql.GraphQLContext; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Map; + +public class RecordFieldBuilder implements InputTypeBuilder { + + private final InputTypeBuilder map; + + public RecordFieldBuilder(Class type, ArrayList mappers) { + var argTypes = mappers.stream().map(t -> t.type).toArray(Class[]::new); + + try { + var constructor = type.getConstructor(argTypes); + map = + (obj, context, locale) -> { + try { + if (type.isInstance(obj)) { + return obj; + } + + Map map = (Map) obj; + + var args = new Object[argTypes.length]; + + for (int i = 0; i < args.length; i++) { + var mapper = mappers.get(i); + + if (map.containsKey(mapper.name)) { + args[i] = mapper.resolver.convert(map.get(mapper.name), context, locale); + } + } + + return constructor.newInstance(args); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object convert(Object obj, GraphQLContext graphQLContext, Locale locale) { + return map.convert(obj, graphQLContext, locale); + } + + public static class RecordMapper { + + private final String name; + private final Class type; + private final InputTypeBuilder resolver; + + public RecordMapper(String name, Class type, InputTypeBuilder resolver) { + this.name = name; + this.type = type; + this.resolver = resolver; + } + } +} diff --git a/src/main/java/graphql/execution/reactive/CompletionStageMappingPublisher.java b/src/main/java/graphql/execution/reactive/CompletionStageMappingPublisher.java deleted file mode 100644 index be712ce..0000000 --- a/src/main/java/graphql/execution/reactive/CompletionStageMappingPublisher.java +++ /dev/null @@ -1,114 +0,0 @@ -//REMOVE ONCE PULL REQUEST MERGED -package graphql.execution.reactive; - -import java.util.concurrent.CompletionStage; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Function; - -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -/** - * A reactive Publisher that bridges over another Publisher of `D` and maps the results - * to type `U` via a CompletionStage, handling errors in that stage - * - * @param the down stream type - * @param the up stream type to be mapped to - */ -public class CompletionStageMappingPublisher implements Publisher { - private final Publisher upstreamPublisher; - private final Function> mapper; - - /** - * You need the following : - * - * @param upstreamPublisher an upstream source of data - * @param mapper a mapper function that turns upstream data into a promise of mapped D downstream data - */ - public CompletionStageMappingPublisher(Publisher upstreamPublisher, Function> mapper) { - this.upstreamPublisher = upstreamPublisher; - this.mapper = mapper; - } - - @Override - public void subscribe(Subscriber downstreamSubscriber) { - upstreamPublisher.subscribe(new Subscriber() { - - private final AtomicInteger inFlight = new AtomicInteger(); - private volatile Runnable finish; - - Subscription delegatingSubscription; - - @Override - public void onSubscribe(Subscription subscription) { - delegatingSubscription = new DelegatingSubscription(subscription); - downstreamSubscriber.onSubscribe(delegatingSubscription); - } - - @Override - public void onNext(U u) { - CompletionStage completionStage; - try { - completionStage = mapper.apply(u); - inFlight.getAndIncrement(); - completionStage.whenComplete((d, throwable) -> { - try { - if (throwable != null) { - handleThrowable(throwable); - } else { - downstreamSubscriber.onNext(d); - } - }finally { - if(inFlight.intValue() == 1 && finish != null) { - finish.run(); - finish = null; - } - inFlight.decrementAndGet(); - } - }); - } catch (RuntimeException throwable) { - handleThrowable(throwable); - } - } - - private void handleThrowable(Throwable throwable) { - downstreamSubscriber.onError(throwable); - // - // reactive semantics say that IF an exception happens on a publisher - // then onError is called and no more messages flow. But since the exception happened - // during the mapping, the upstream publisher does not no about this. - // so we cancel to bring the semantics back together, that is as soon as an exception - // has happened, no more messages flow - // - delegatingSubscription.cancel(); - } - - @Override - public void onError(Throwable t) { - if(inFlight.intValue() > 0) { - finish = () -> downstreamSubscriber.onError(t); - if(inFlight.intValue() == 0 && finish != null) { - //happened together - downstreamSubscriber.onError(t); - } - }else { - downstreamSubscriber.onError(t); - } - } - - @Override - public void onComplete() { - if(inFlight.intValue() > 0) { - finish = () -> downstreamSubscriber.onComplete(); - if(inFlight.intValue() == 0 && finish != null) { - //happened together - downstreamSubscriber.onComplete(); - } - }else { - downstreamSubscriber.onComplete(); - } - } - }); - } -} diff --git a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java index 96eb4f5..571c738 100644 --- a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java @@ -1,35 +1,29 @@ -package com.fleetpin.graphql.builder; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -import graphql.GraphQL; -import graphql.schema.FieldCoordinates; - -public class DirectiveTest { - @Test - public void testDirectiveAppliedToQuery() throws ReflectiveOperationException { - - GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type.directive")).build(); - var cat = schema.getGraphQLSchema() - .getFieldDefinition(FieldCoordinates.coordinates(schema.getGraphQLSchema().getQueryType(), "getCat")); - var capture = cat.getAppliedDirective("Capture"); - var argument = capture.getArgument("color"); - var color = argument.getValue(); - assertEquals("meow", color); - - } - - @Test - public void testPresentOnSchema() throws ReflectiveOperationException { - - GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type.directive")).build(); - var capture = schema.getGraphQLSchema().getSchemaAppliedDirective("Capture"); - var argument = capture.getArgument("color"); - var color = argument.getValue(); - assertEquals("top", color); - - } - -} +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import graphql.GraphQL; +import graphql.schema.FieldCoordinates; +import org.junit.jupiter.api.Test; + +public class DirectiveTest { + + @Test + public void testDirectiveAppliedToQuery() throws ReflectiveOperationException { + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type.directive")).build(); + var cat = schema.getGraphQLSchema().getFieldDefinition(FieldCoordinates.coordinates(schema.getGraphQLSchema().getQueryType(), "getCat")); + var capture = cat.getAppliedDirective("Capture"); + var argument = capture.getArgument("color"); + var color = argument.getValue(); + assertEquals("meow", color); + } + + @Test + public void testPresentOnSchema() throws ReflectiveOperationException { + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type.directive")).build(); + var capture = schema.getGraphQLSchema().getSchemaAppliedDirective("Capture"); + var argument = capture.getArgument("color"); + var color = argument.getValue(); + assertEquals("top", color); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/MetaTest.java b/src/test/java/com/fleetpin/graphql/builder/MetaTest.java index b0cf7d7..9540f26 100644 --- a/src/test/java/com/fleetpin/graphql/builder/MetaTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/MetaTest.java @@ -3,20 +3,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; - import graphql.schema.GraphQLObjectType; +import org.junit.jupiter.api.Test; public class MetaTest { - + @Test public void testDeprecated() throws ReflectiveOperationException { var schema = SchemaBuilder.build("com.fleetpin.graphql.builder.type"); - + var query = schema.getQueryType().getField("deprecatedTest"); assertTrue(query.isDeprecated()); assertEquals("old", query.getDeprecationReason()); - + GraphQLObjectType type = (GraphQLObjectType) schema.getType("DeprecatedObject"); var field = type.getField("naame"); assertTrue(field.isDeprecated()); @@ -26,14 +25,13 @@ public void testDeprecated() throws ReflectiveOperationException { @Test public void testDescription() throws ReflectiveOperationException { var schema = SchemaBuilder.build("com.fleetpin.graphql.builder.type"); - + var query = schema.getQueryType().getField("descriptionTest"); assertEquals("returns something", query.getDescription()); - + GraphQLObjectType type = (GraphQLObjectType) schema.getType("DescriptionObject"); assertEquals("test description comes through", type.getDescription()); var field = type.getField("name"); assertEquals("first and last", field.getDescription()); } - } diff --git a/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java index 61f3c28..5208c25 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java @@ -13,21 +13,16 @@ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import graphql.ExecutionResult; +import graphql.GraphQL; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; - import org.junit.jupiter.api.Test; -import com.fleetpin.graphql.builder.annotations.Id; -import com.fleetpin.graphql.builder.annotations.Query; -import com.google.common.base.Optional; - -import graphql.ExecutionResult; -import graphql.GraphQL; - public class ParameterParsingTest { //TODO:add failure cases @@ -36,89 +31,93 @@ public void testRequiredString() throws ReflectiveOperationException { Map> response = execute("query {requiredString(type: \"There\")} ").getData(); assertEquals("There", response.get("requiredString")); } - + @Test public void testOptionalStringPresent() throws ReflectiveOperationException { Map> response = execute("query {optionalString(type: \"There\")} ").getData(); assertEquals("There", response.get("optionalString")); } - + @Test public void testOptionalStringNull() throws ReflectiveOperationException { Map> response = execute("query {optionalString(type: null)} ").getData(); assertEquals(null, response.get("optionalString")); } - @Test public void testOptionalStringMissing() throws ReflectiveOperationException { Map> response = execute("query {optionalString} ").getData(); assertEquals(null, response.get("optionalString")); } - + //TODO:id checks don't confirm actual an id @Test public void testRequiredId() throws ReflectiveOperationException { Map> response = execute("query {testRequiredId(type: \"There\")} ").getData(); assertEquals("There", response.get("testRequiredId")); - } + } + @Test public void testOptionalIdPresent() throws ReflectiveOperationException { Map> response = execute("query {optionalId(type: \"There\")} ").getData(); assertEquals("There", response.get("optionalId")); } - + @Test public void testOptionalIdNull() throws ReflectiveOperationException { - Map> response = execute("query {optionalId(type: null)} ").getData(); + Map response = execute("query {optionalId(type: null)} ").getData(); + assertEquals(null, response.get("optionalId")); + assertTrue(response.containsKey("optionalId")); + } + + @Test + public void testOptionalIdNullAlways() throws ReflectiveOperationException { + Map response = execute("query {optionalIdNull{nullOptional}} ").getData(); assertEquals(null, response.get("optionalId")); } - - + @Test public void testRequiredListStringEmpty() throws ReflectiveOperationException { Map>> response = execute("query {requiredListString(type: [])} ").getData(); assertEquals(Collections.emptyList(), response.get("requiredListString")); } - + @Test public void testRequiredListString() throws ReflectiveOperationException { Map>> response = execute("query {requiredListString(type: [\"free\"])} ").getData(); assertEquals(Arrays.asList("free"), response.get("requiredListString")); } - + @Test public void testOptionalListStringEmpty() throws ReflectiveOperationException { Map>> response = execute("query {optionalListString(type: [])} ").getData(); assertEquals(Collections.emptyList(), response.get("optionalListString")); } - + @Test public void testOptionalListString() throws ReflectiveOperationException { Map>> response = execute("query {optionalListString(type: [\"free\"])} ").getData(); assertEquals(Arrays.asList("free"), response.get("optionalListString")); } - + @Test public void testOptionalListStringNull() throws ReflectiveOperationException { Map>> response = execute("query {optionalListString} ").getData(); assertEquals(null, response.get("optionalListString")); } - - + @Test public void testRequiredListOptionalString() throws ReflectiveOperationException { Map>> response = execute("query {requiredListOptionalString(type: [null, \"free\"])} ").getData(); assertEquals(Arrays.asList(null, "free"), response.get("requiredListOptionalString")); } - - + @Test public void testOptionalListOptionalString() throws ReflectiveOperationException { Map>> response = execute("query {optionalListOptionalString(type: [null, \"free\"])} ").getData(); assertEquals(Arrays.asList(null, "free"), response.get("optionalListOptionalString")); } - + @Test public void testOptionalListOptionalStringNull() throws ReflectiveOperationException { Map>> response = execute("query {optionalListOptionalString} ").getData(); @@ -130,110 +129,103 @@ public void testRequiredListIdEmpty() throws ReflectiveOperationException { Map>> response = execute("query {requiredListId(type: [])} ").getData(); assertEquals(Collections.emptyList(), response.get("requiredListId")); } - + @Test public void testRequiredListId() throws ReflectiveOperationException { Map>> response = execute("query {requiredListId(type: [\"free\"])} ").getData(); assertEquals(Arrays.asList("free"), response.get("requiredListId")); } - + @Test public void testOptionalListIdEmpty() throws ReflectiveOperationException { Map>> response = execute("query {optionalListId(type: [])} ").getData(); assertEquals(Collections.emptyList(), response.get("optionalListId")); } - + @Test public void testOptionalListId() throws ReflectiveOperationException { Map>> response = execute("query {optionalListId(type: [\"free\"])} ").getData(); assertEquals(Arrays.asList("free"), response.get("optionalListId")); } - + @Test public void testOptionalListIdNull() throws ReflectiveOperationException { Map>> response = execute("query {optionalListId} ").getData(); assertEquals(null, response.get("optionalListId")); } - - + @Test public void testRequiredListOptionalId() throws ReflectiveOperationException { Map>> response = execute("query {requiredListOptionalId(type: [null, \"free\"])} ").getData(); assertEquals(Arrays.asList(null, "free"), response.get("requiredListOptionalId")); } - - + @Test public void testOptionalListOptionalId() throws ReflectiveOperationException { Map>> response = execute("query {optionalListOptionalId(type: [null, \"free\"])} ").getData(); assertEquals(Arrays.asList(null, "free"), response.get("optionalListOptionalId")); } - + @Test public void testOptionalListOptionalIdNull() throws ReflectiveOperationException { Map>> response = execute("query {optionalListOptionalId} ").getData(); assertEquals(null, response.get("optionalListOptionalId")); } - - - + @Test public void testMultipleArguments() throws ReflectiveOperationException { Map>> response = execute("query {multipleArguments(first: \"free\", second: \"bird\")} ").getData(); assertEquals("free:bird", response.get("multipleArguments")); } - - + @Test public void testMultipleArgumentsOptional() throws ReflectiveOperationException { Map>> response = execute("query {multipleArgumentsOptional(first: \"free\", second: \"bird\")} ").getData(); assertEquals("free:bird", response.get("multipleArgumentsOptional")); } - + @Test public void testMultipleArgumentsOptionalPartial1() throws ReflectiveOperationException { Map>> response = execute("query {multipleArgumentsOptional(second: \"bird\")} ").getData(); assertEquals(":bird", response.get("multipleArgumentsOptional")); } - + @Test public void testMultipleArgumentsOptionalPartial2() throws ReflectiveOperationException { Map>> response = execute("query {multipleArgumentsOptional(second: null)} ").getData(); assertEquals(":", response.get("multipleArgumentsOptional")); } - + @Test public void testMultipleArgumentsOptionalPartial3() throws ReflectiveOperationException { Map>> response = execute("query {multipleArgumentsOptional} ").getData(); assertEquals(":", response.get("multipleArgumentsOptional")); } - + @Test public void testMultipleArgumentsMix1() throws ReflectiveOperationException { Map>> response = execute("query {multipleArgumentsMix(first: \"free\")} ").getData(); assertEquals("free:", response.get("multipleArgumentsMix")); } - + @Test public void testMultipleArgumentsMix2() throws ReflectiveOperationException { Map>> response = execute("query {multipleArgumentsMix(first: \"free\", second: null)} ").getData(); assertEquals("free:", response.get("multipleArgumentsMix")); } - + @Test public void testMultipleArgumentsMix3() throws ReflectiveOperationException { Map>> response = execute("query {multipleArgumentsMix(first: \"free\", second: \"bird\")} ").getData(); assertEquals("free:bird", response.get("multipleArgumentsMix")); } - - + private ExecutionResult execute(String query) throws ReflectiveOperationException { var schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.parameter")).build(); ExecutionResult result = schema.execute(query); - if(!result.getErrors().isEmpty()) { + if (!result.getErrors().isEmpty()) { throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup } return result; } - } diff --git a/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java index 472692a..ebc1b82 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java @@ -14,125 +14,134 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Test; - +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; - +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; public class ParameterTypeParsingTest { + public static final ObjectMapper MAPPER = new ObjectMapper() + .registerModule(new ParameterNamesModule()) + .registerModule(new Jdk8Module()) + .registerModule(new JavaTimeModule()) + .setVisibility(PropertyAccessor.FIELD, Visibility.ANY); + // TODO:add failure cases @Test public void testRequiredType() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map> response = execute( - "query test($type: InputTestInput!){requiredType(type: $type){value}} ", "{\"value\": \"There\"}") - .getData(); + Map> response = execute("query test($type: InputTestInput!){requiredType(type: $type){value}} ", "{\"value\": \"There\"}") + .getData(); assertEquals("There", response.get("requiredType").get("value")); } @Test - public void testOptionalTypePresent() - throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map> response = execute( - "query test($type: InputTestInput){optionalType(type: $type){value}} ", "{\"value\": \"There\"}") - .getData(); - assertEquals("There", response.get("optionalType").get("value")); + public void testEnum() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { + Map> response = execute("query enumTest($type: AnimalType!){enumTest(type: $type)} ", "\"CAT\"").getData(); + assertEquals("CAT", response.get("enumTest")); } @Test - public void testOptionalTypeNull() - throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { + public void testOptionalTypePresent() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { + Map> response = execute("query test($type: InputTestInput){optionalType(type: $type){value}} ", "{\"value\": \"There\"}") + .getData(); + assertEquals("There", response.get("optionalType").get("value")); + } - Map> response = execute( - "query test($type: InputTestInput){optionalType(type: $type){value}} ", null).getData(); + @Test + public void testOptionalTypeNull() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { + Map> response = execute("query test($type: InputTestInput){optionalType(type: $type){value}} ", null).getData(); assertEquals(null, response.get("optionalType")); - } @Test public void testOptionalTypeMissing() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map> response = execute( - "query test{optionalType{value}} ", null).getData(); + Map> response = execute("query test{optionalType{value}} ", null).getData(); assertEquals(null, response.get("optionalType")); } - @Test public void testRequiredListTypeEmpty() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map>> response = execute("query {requiredListType(type: []){value}} ", null).getData(); + Map>> response = execute("query {requiredListType(type: []){value}} ", null).getData(); assertEquals(Collections.emptyList(), response.get("requiredListType")); } @Test public void testRequiredListType() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - - Map>> response = execute( - "query test($type: [InputTestInput!]!){requiredListType(type: $type){value}} ", "[{\"value\": \"There\"}]") - .getData(); + Map>> response = execute( + "query test($type: [InputTestInput!]!){requiredListType(type: $type){value}} ", + "[{\"value\": \"There\"}]" + ) + .getData(); assertEquals("There", response.get("requiredListType").get(0).get("value")); - } @Test public void testOptionalListTypeEmpty() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map>> response = execute("query {optionalListType(type: []){value}} ", null).getData(); + Map>> response = execute("query {optionalListType(type: []){value}} ", null).getData(); assertEquals(Collections.emptyList(), response.get("optionalListType")); } @Test public void testOptionalListType() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map>> response = execute( - "query test($type: [InputTestInput!]){optionalListType(type: $type){value}} ", "[{\"value\": \"There\"}]") - .getData(); + Map>> response = execute( + "query test($type: [InputTestInput!]){optionalListType(type: $type){value}} ", + "[{\"value\": \"There\"}]" + ) + .getData(); assertEquals("There", response.get("optionalListType").get(0).get("value")); - } @Test public void testOptionalListTypeNull() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map>> response = execute("query {optionalListType{value}} ", null).getData(); + Map>> response = execute("query {optionalListType{value}} ", null).getData(); assertEquals(null, response.get("optionalListType")); } @Test public void testRequiredListOptionalType() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map>> response = execute( - "query test($type: [InputTestInput]!){requiredListOptionalType(type: $type){value}} ", "[null, {\"value\": \"There\"}]") - .getData(); + Map>> response = execute( + "query test($type: [InputTestInput]!){requiredListOptionalType(type: $type){value}} ", + "[null, {\"value\": \"There\"}]" + ) + .getData(); assertEquals("There", response.get("requiredListOptionalType").get(1).get("value")); assertEquals(null, response.get("requiredListOptionalType").get(0)); } @Test public void testOptionalListOptionalType() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map>> response = execute( - "query test($type: [InputTestInput]){optionalListOptionalType(type: $type){value}} ", "[null, {\"value\": \"There\"}]") - .getData(); + Map>> response = execute( + "query test($type: [InputTestInput]){optionalListOptionalType(type: $type){value}} ", + "[null, {\"value\": \"There\"}]" + ) + .getData(); assertEquals("There", response.get("optionalListOptionalType").get(1).get("value")); assertEquals(null, response.get("optionalListOptionalType").get(0)); - } @Test public void testOptionalListOptionalTypeNull() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { - Map>> response = execute("query {optionalListOptionalType{value}} ", null).getData(); + Map>> response = execute("query {optionalListOptionalType{value}} ", null).getData(); assertEquals(null, response.get("optionalListOptionalType")); } - private ExecutionResult execute(String query, String type) - throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { + private ExecutionResult execute(String query, String type) throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { Object obj = null; if (type != null) { - obj = SchemaBuilder.MAPPER.readValue(type, Object.class); + obj = MAPPER.readValue(type, Object.class); } Map variables = new HashMap<>(); variables.put("type", obj); @@ -145,5 +154,4 @@ private ExecutionResult execute(String query, String type) } return result; } - } diff --git a/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java b/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java index b0e3356..4f1b2b7 100644 --- a/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java +++ b/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java @@ -14,19 +14,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import graphql.GraphQL; +import io.reactivex.rxjava3.core.Flowable; import java.util.Arrays; import java.util.List; import java.util.Map; - import org.junit.jupiter.api.Test; import org.reactivestreams.Publisher; -import graphql.GraphQL; -import io.reactivex.rxjava3.core.Flowable; - public class PublishRestrictions { - @Test public void testOptionalArray() throws ReflectiveOperationException { var schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.publishRestrictions")).build(); @@ -34,8 +31,4 @@ public void testOptionalArray() throws ReflectiveOperationException { Publisher response = res.getData(); assertEquals(0, Flowable.fromPublisher(response).count().blockingGet()); } - - - - } diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java index e309b1b..f59f2f2 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java @@ -1,202 +1,198 @@ -package com.fleetpin.graphql.builder; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import graphql.ExecutionResult; -import graphql.GraphQL; - -public class TypeGenericInputParsingTest { - - @Test - public void testAnimalName() throws ReflectiveOperationException { - var name = getField("Animal", "INTERFACE", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testCatName() throws ReflectiveOperationException { - var name = getField("Cat", "OBJECT", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testCatInputName() throws ReflectiveOperationException { - var name = getInputField("CatInput", "INPUT_OBJECT", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testCatInputFur() throws ReflectiveOperationException { - var name = getInputField("CatInput", "INPUT_OBJECT", "fur"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); - } - - - @Test - public void testAnimalInputName() throws ReflectiveOperationException { - var name = getInputField("CatAnimalInput", "INPUT_OBJECT", "id"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - - @Test - public void testAnimalInputGenericName() throws ReflectiveOperationException { - var name = getInputField("AnimalInput_Cat", "INPUT_OBJECT", "id"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - - @Test - public void testAnimalInputCat() throws ReflectiveOperationException { - var name = getInputField("CatAnimalInput", "INPUT_OBJECT", "animal"); - var nonNull = confirmNonNull(name); - confirmInputObject(nonNull, "CatInput"); - - } - - @Test - public void testAnimalInputGenericCat() throws ReflectiveOperationException { - var name = getInputField("AnimalInput_Cat", "INPUT_OBJECT", "animal"); - var nonNull = confirmNonNull(name); - confirmInputObject(nonNull, "CatInput"); - - } - - - private void confirmString(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("String", type.get("name")); - } - - - private void confirmInputObject(Map type, String name) { - Assertions.assertEquals("INPUT_OBJECT", type.get("kind")); - Assertions.assertEquals(name, type.get("name")); - } - - - private Map confirmNonNull(Map type) { - Assertions.assertEquals("NON_NULL", type.get("kind")); - var toReturn = (Map) type.get("ofType"); - Assertions.assertNotNull(toReturn); - return toReturn; - } - - private void confirmBoolean(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("Boolean", type.get("name")); - } - - public Map getField(String typeName, String kind, String name) throws ReflectiveOperationException { - Map> response = execute("{" + - " __type(name: \"" + typeName + "\") {" + - " name" + - " kind" + - " fields {" + - " name" + - " type {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " }" + - " }" + - " }" + - " }" + - " }" + - " }" + - "} ").getData(); - var type = response.get("__type"); - Assertions.assertEquals(typeName, type.get("name")); - Assertions.assertEquals(kind, type.get("kind")); - List> fields = (List>) type.get("fields"); - var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); - Assertions.assertEquals(name, field.get("name")); - return (Map) field.get("type"); - } - - public Map getInputField(String typeName, String kind, String name) throws ReflectiveOperationException { - Map> response = execute("{" + - " __type(name: \"" + typeName + "\") {" + - " name" + - " kind" + - " inputFields {" + - " name" + - " type {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " }" + - " }" + - " }" + - " }" + - " }" + - " }" + - "} ").getData(); - var type = response.get("__type"); - Assertions.assertEquals(typeName, type.get("name")); - Assertions.assertEquals(kind, type.get("kind")); - List> fields = (List>) type.get("inputFields"); - var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); - Assertions.assertEquals(name, field.get("name")); - return (Map) field.get("type"); - } - - @Test - public void testQueryCatFur() throws ReflectiveOperationException { - Map response = execute("mutation {addCat(input: {id: \"1\", animal: {name: \"felix\", fur: false}})} ").getData(); - var cat = response.get("addCat"); - assertEquals("felix", cat); - } - - @Test - public void testQueryCatFurGeneric() throws ReflectiveOperationException { - Map response = execute("mutation {addCatGenerics(input: {id: \"1\", animal: {name: \"felix\", fur: true}})} ").getData(); - var cat = response.get("addCatGenerics"); - assertEquals(true, cat); - } - - - - private ExecutionResult execute(String query) { - try { - GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.inputgenerics")).build(); - ExecutionResult result = schema.execute(query); - if(!result.getErrors().isEmpty()) { - throw new RuntimeException(result.getErrors().toString()); - } - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - - } - -} +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import graphql.ExecutionResult; +import graphql.GraphQL; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TypeGenericInputParsingTest { + + @Test + public void testAnimalName() throws ReflectiveOperationException { + var name = getField("Animal", "INTERFACE", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testCatName() throws ReflectiveOperationException { + var name = getField("Cat", "OBJECT", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testCatInputName() throws ReflectiveOperationException { + var name = getInputField("CatInput", "INPUT_OBJECT", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testCatInputFur() throws ReflectiveOperationException { + var name = getInputField("CatInput", "INPUT_OBJECT", "fur"); + var nonNull = confirmNonNull(name); + confirmBoolean(nonNull); + } + + @Test + public void testAnimalInputName() throws ReflectiveOperationException { + var name = getInputField("CatAnimalInput", "INPUT_OBJECT", "id"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testAnimalInputGenericName() throws ReflectiveOperationException { + var name = getInputField("AnimalInput_Cat", "INPUT_OBJECT", "id"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testAnimalInputCat() throws ReflectiveOperationException { + var name = getInputField("CatAnimalInput", "INPUT_OBJECT", "animal"); + var nonNull = confirmNonNull(name); + confirmInputObject(nonNull, "CatInput"); + } + + @Test + public void testAnimalInputGenericCat() throws ReflectiveOperationException { + var name = getInputField("AnimalInput_Cat", "INPUT_OBJECT", "animal"); + var nonNull = confirmNonNull(name); + confirmInputObject(nonNull, "CatInput"); + } + + private void confirmString(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("String", type.get("name")); + } + + private void confirmInputObject(Map type, String name) { + Assertions.assertEquals("INPUT_OBJECT", type.get("kind")); + Assertions.assertEquals(name, type.get("name")); + } + + private Map confirmNonNull(Map type) { + Assertions.assertEquals("NON_NULL", type.get("kind")); + var toReturn = (Map) type.get("ofType"); + Assertions.assertNotNull(toReturn); + return toReturn; + } + + private void confirmBoolean(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("Boolean", type.get("name")); + } + + public Map getField(String typeName, String kind, String name) throws ReflectiveOperationException { + Map> response = execute( + "{" + + " __type(name: \"" + + typeName + + "\") {" + + " name" + + " kind" + + " fields {" + + " name" + + " type {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " }" + + " }" + + " }" + + " }" + + " }" + + " }" + + "} " + ) + .getData(); + var type = response.get("__type"); + Assertions.assertEquals(typeName, type.get("name")); + Assertions.assertEquals(kind, type.get("kind")); + List> fields = (List>) type.get("fields"); + var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); + Assertions.assertEquals(name, field.get("name")); + return (Map) field.get("type"); + } + + public Map getInputField(String typeName, String kind, String name) throws ReflectiveOperationException { + Map> response = execute( + "{" + + " __type(name: \"" + + typeName + + "\") {" + + " name" + + " kind" + + " inputFields {" + + " name" + + " type {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " }" + + " }" + + " }" + + " }" + + " }" + + " }" + + "} " + ) + .getData(); + var type = response.get("__type"); + Assertions.assertEquals(typeName, type.get("name")); + Assertions.assertEquals(kind, type.get("kind")); + List> fields = (List>) type.get("inputFields"); + var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); + Assertions.assertEquals(name, field.get("name")); + return (Map) field.get("type"); + } + + @Test + public void testQueryCatFur() throws ReflectiveOperationException { + Map response = execute("mutation {addCat(input: {id: \"1\", animal: {name: \"felix\", fur: false}})} ").getData(); + var cat = response.get("addCat"); + assertEquals("felix", cat); + } + + @Test + public void testQueryCatFurGeneric() throws ReflectiveOperationException { + Map response = execute("mutation {addCatGenerics(input: {id: \"1\", animal: {name: \"felix\", fur: true}})} ").getData(); + var cat = response.get("addCatGenerics"); + assertEquals(true, cat); + } + + private ExecutionResult execute(String query) { + try { + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.inputgenerics")).build(); + ExecutionResult result = schema.execute(query); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); + } + return result; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java index 6218043..5275cff 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java @@ -1,311 +1,311 @@ -package com.fleetpin.graphql.builder; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import graphql.ExecutionResult; -import graphql.GraphQL; - -public class TypeGenericParsingTest { - - @Test - public void testAnimalName() throws ReflectiveOperationException { - var name = getField("Animal", "INTERFACE", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testAnimalFur() throws ReflectiveOperationException { - var name = getField("Animal", "INTERFACE", "fur"); - var nonNull = confirmNonNull(name); - confirmInterface(nonNull, "Fur"); - } - - @Test - public void testAnimalFurs() throws ReflectiveOperationException { - var name = getField("Animal", "INTERFACE", "furs"); - var type = confirmNonNull(name); - type = confirmArray(type); - type = confirmNonNull(type); - confirmInterface(type, "Fur"); - } - - @Test - public void testCatName() throws ReflectiveOperationException { - var name = getField("Cat", "OBJECT", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testCatFur() throws ReflectiveOperationException { - var name = getField("Cat", "OBJECT", "fur"); - var nonNull = confirmNonNull(name); - confirmObject(nonNull, "CatFur"); - } - - @Test - public void testCatFurs() throws ReflectiveOperationException { - var name = getField("Cat", "OBJECT", "furs"); - var type = confirmNonNull(name); - type = confirmArray(type); - type = confirmNonNull(type); - confirmObject(type, "CatFur"); - } - - @Test - public void testDogName() throws ReflectiveOperationException { - var name = getField("Dog", "OBJECT", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testDogFur() throws ReflectiveOperationException { - var name = getField("Dog", "OBJECT", "fur"); - var nonNull = confirmNonNull(name); - confirmObject(nonNull, "DogFur"); - } - - @Test - public void testDogFurs() throws ReflectiveOperationException { - var name = getField("Dog", "OBJECT", "furs"); - var type = confirmNonNull(name); - type = confirmArray(type); - type = confirmNonNull(type); - confirmObject(type, "DogFur"); - } - - @Test - public void testFurName() throws ReflectiveOperationException { - var name = getField("Fur", "INTERFACE", "length"); - var nonNull = confirmNonNull(name); - confirmNumber(nonNull); - } - - @Test - public void testCatFurCalico() throws ReflectiveOperationException { - var name = getField("CatFur", "OBJECT", "calico"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); - } - - @Test - public void testCatFurLong() throws ReflectiveOperationException { - var name = getField("CatFur", "OBJECT", "long"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); - } - - @Test - public void testDogFurShaggy() throws ReflectiveOperationException { - var name = getField("DogFur", "OBJECT", "shaggy"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); - } - - @Test - public void testCatFamilyName() throws ReflectiveOperationException { - var name = getField("CatFamily", "INTERFACE", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testCatFamilyFur() throws ReflectiveOperationException { - var name = getField("CatFamily", "INTERFACE", "fur"); - var nonNull = confirmNonNull(name); - confirmInterface(nonNull, "CatFamilyFur"); - } - - @Test - public void testCatFamilyFurs() throws ReflectiveOperationException { - var name = getField("CatFamily", "INTERFACE", "furs"); - var type = confirmNonNull(name); - type = confirmArray(type); - type = confirmNonNull(type); - confirmInterface(type, "CatFamilyFur"); - } - - @Test - public void testCatFamilyFurCalico() throws ReflectiveOperationException { - var name = getField("CatFamilyFur", "INTERFACE", "length"); - var nonNull = confirmNonNull(name); - confirmNumber(nonNull); - } - - @Test - public void testCatFamilyFurLong() throws ReflectiveOperationException { - var name = getField("CatFamilyFur", "INTERFACE", "long"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); - } - - - - - private void confirmString(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("String", type.get("name")); - } - - private void confirmInterface(Map type, String name) { - Assertions.assertEquals("INTERFACE", type.get("kind")); - Assertions.assertEquals(name, type.get("name")); - } - - private void confirmObject(Map type, String name) { - Assertions.assertEquals("OBJECT", type.get("kind")); - Assertions.assertEquals(name, type.get("name")); - } - - - - private void confirmBoolean(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("Boolean", type.get("name")); - } - - private void confirmNumber(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("Int", type.get("name")); - } - - - private Map confirmNonNull(Map type) { - Assertions.assertEquals("NON_NULL", type.get("kind")); - var toReturn = (Map) type.get("ofType"); - Assertions.assertNotNull(toReturn); - return toReturn; - } - - private Map confirmArray(Map type) { - Assertions.assertEquals("LIST", type.get("kind")); - var toReturn = (Map) type.get("ofType"); - Assertions.assertNotNull(toReturn); - return toReturn; - } - - public Map getField(String typeName, String kind, String name) throws ReflectiveOperationException { - Map> response = execute("{" + - " __type(name: \"" + typeName + "\") {" + - " name" + - " kind" + - " fields {" + - " name" + - " type {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " }" + - " }" + - " }" + - " }" + - " }" + - " }" + - "} ").getData(); - var type = response.get("__type"); - Assertions.assertEquals(typeName, type.get("name")); - Assertions.assertEquals(kind, type.get("kind")); - - List> fields = (List>) type.get("fields"); - var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); - Assertions.assertEquals(name, field.get("name")); - return (Map) field.get("type"); - } - - @Test - public void testQueryCatFur() throws ReflectiveOperationException { - Map>> response = execute("query {animals{" + - "name " + - "... on Cat { " + - " fur{ " + - " calico " + - " length" + - " catFur: long" + - " }" + - "} " + - "... on Dog {" + - " fur {" + - " shaggy" + - " dogFur: long" + - " length" + - " } " + - "} " + - "}} ").getData(); - - var animals = response.get("animals"); - - var cat = animals.get(0); - var dog = animals.get(1); - - var catFur = (Map )cat.get("fur"); - var dogFur = (Map) dog.get("fur"); - - assertEquals("name", cat.get("name")); - assertEquals(4, catFur.get("length")); - assertEquals(true, catFur.get("calico")); - assertEquals(true, catFur.get("catFur")); - - assertEquals(4, dogFur.get("length")); - assertEquals(true, dogFur.get("shaggy")); - assertEquals("very", dogFur.get("dogFur")); - } - - @Test - public void testMutationCatFur() throws ReflectiveOperationException { - Map>> response = execute("mutation {makeCat{" + - "item { " + - " ... on Cat { " + - " name " + - " fur{ " + - " calico " + - " length" + - " long" + - " }" + - " } " + - "}" + - "}} ").getData(); - - var makeCat = response.get("makeCat"); - - var cat = makeCat.get("item"); - - var catFur = (Map )cat.get("fur"); - - assertEquals("name", cat.get("name")); - assertEquals(4, catFur.get("length")); - assertEquals(true, catFur.get("calico")); - assertEquals(true, catFur.get("long")); - - } - - private ExecutionResult execute(String query) { - try { - GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.generics")).build(); - ExecutionResult result = schema.execute(query); - if(!result.getErrors().isEmpty()) { - throw new RuntimeException(result.getErrors().toString()); - } - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - - } - -} +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import graphql.ExecutionResult; +import graphql.GraphQL; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TypeGenericParsingTest { + + @Test + public void testAnimalName() throws ReflectiveOperationException { + var name = getField("Animal", "INTERFACE", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testAnimalFur() throws ReflectiveOperationException { + var name = getField("Animal", "INTERFACE", "fur"); + var nonNull = confirmNonNull(name); + confirmInterface(nonNull, "Fur"); + } + + @Test + public void testAnimalFurs() throws ReflectiveOperationException { + var name = getField("Animal", "INTERFACE", "furs"); + var type = confirmNonNull(name); + type = confirmArray(type); + type = confirmNonNull(type); + confirmInterface(type, "Fur"); + } + + @Test + public void testCatName() throws ReflectiveOperationException { + var name = getField("Cat", "OBJECT", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testCatFur() throws ReflectiveOperationException { + var name = getField("Cat", "OBJECT", "fur"); + var nonNull = confirmNonNull(name); + confirmObject(nonNull, "CatFur"); + } + + @Test + public void testCatFurs() throws ReflectiveOperationException { + var name = getField("Cat", "OBJECT", "furs"); + var type = confirmNonNull(name); + type = confirmArray(type); + type = confirmNonNull(type); + confirmObject(type, "CatFur"); + } + + @Test + public void testDogName() throws ReflectiveOperationException { + var name = getField("Dog", "OBJECT", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testDogFur() throws ReflectiveOperationException { + var name = getField("Dog", "OBJECT", "fur"); + var nonNull = confirmNonNull(name); + confirmObject(nonNull, "DogFur"); + } + + @Test + public void testDogFurs() throws ReflectiveOperationException { + var name = getField("Dog", "OBJECT", "furs"); + var type = confirmNonNull(name); + type = confirmArray(type); + type = confirmNonNull(type); + confirmObject(type, "DogFur"); + } + + @Test + public void testFurName() throws ReflectiveOperationException { + var name = getField("Fur", "INTERFACE", "length"); + var nonNull = confirmNonNull(name); + confirmNumber(nonNull); + } + + @Test + public void testCatFurCalico() throws ReflectiveOperationException { + var name = getField("CatFur", "OBJECT", "calico"); + var nonNull = confirmNonNull(name); + confirmBoolean(nonNull); + } + + @Test + public void testCatFurLong() throws ReflectiveOperationException { + var name = getField("CatFur", "OBJECT", "long"); + var nonNull = confirmNonNull(name); + confirmBoolean(nonNull); + } + + @Test + public void testDogFurShaggy() throws ReflectiveOperationException { + var name = getField("DogFur", "OBJECT", "shaggy"); + var nonNull = confirmNonNull(name); + confirmBoolean(nonNull); + } + + @Test + public void testCatFamilyName() throws ReflectiveOperationException { + var name = getField("CatFamily", "INTERFACE", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testCatFamilyFur() throws ReflectiveOperationException { + var name = getField("CatFamily", "INTERFACE", "fur"); + var nonNull = confirmNonNull(name); + confirmInterface(nonNull, "CatFamilyFur"); + } + + @Test + public void testCatFamilyFurs() throws ReflectiveOperationException { + var name = getField("CatFamily", "INTERFACE", "furs"); + var type = confirmNonNull(name); + type = confirmArray(type); + type = confirmNonNull(type); + confirmInterface(type, "CatFamilyFur"); + } + + @Test + public void testCatFamilyFurCalico() throws ReflectiveOperationException { + var name = getField("CatFamilyFur", "INTERFACE", "length"); + var nonNull = confirmNonNull(name); + confirmNumber(nonNull); + } + + @Test + public void testCatFamilyFurLong() throws ReflectiveOperationException { + var name = getField("CatFamilyFur", "INTERFACE", "long"); + var nonNull = confirmNonNull(name); + confirmBoolean(nonNull); + } + + private void confirmString(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("String", type.get("name")); + } + + private void confirmInterface(Map type, String name) { + Assertions.assertEquals("INTERFACE", type.get("kind")); + Assertions.assertEquals(name, type.get("name")); + } + + private void confirmObject(Map type, String name) { + Assertions.assertEquals("OBJECT", type.get("kind")); + Assertions.assertEquals(name, type.get("name")); + } + + private void confirmBoolean(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("Boolean", type.get("name")); + } + + private void confirmNumber(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("Int", type.get("name")); + } + + private Map confirmNonNull(Map type) { + Assertions.assertEquals("NON_NULL", type.get("kind")); + var toReturn = (Map) type.get("ofType"); + Assertions.assertNotNull(toReturn); + return toReturn; + } + + private Map confirmArray(Map type) { + Assertions.assertEquals("LIST", type.get("kind")); + var toReturn = (Map) type.get("ofType"); + Assertions.assertNotNull(toReturn); + return toReturn; + } + + public Map getField(String typeName, String kind, String name) throws ReflectiveOperationException { + Map> response = execute( + "{" + + " __type(name: \"" + + typeName + + "\") {" + + " name" + + " kind" + + " fields {" + + " name" + + " type {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " }" + + " }" + + " }" + + " }" + + " }" + + " }" + + "} " + ) + .getData(); + var type = response.get("__type"); + Assertions.assertEquals(typeName, type.get("name")); + Assertions.assertEquals(kind, type.get("kind")); + + List> fields = (List>) type.get("fields"); + var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); + Assertions.assertEquals(name, field.get("name")); + return (Map) field.get("type"); + } + + @Test + public void testQueryCatFur() throws ReflectiveOperationException { + Map>> response = execute( + "query {animals{" + + "name " + + "... on Cat { " + + " fur{ " + + " calico " + + " length" + + " catFur: long" + + " }" + + "} " + + "... on Dog {" + + " fur {" + + " shaggy" + + " dogFur: long" + + " length" + + " } " + + "} " + + "}} " + ) + .getData(); + + var animals = response.get("animals"); + + var cat = animals.get(0); + var dog = animals.get(1); + + var catFur = (Map) cat.get("fur"); + var dogFur = (Map) dog.get("fur"); + + assertEquals("name", cat.get("name")); + assertEquals(4, catFur.get("length")); + assertEquals(true, catFur.get("calico")); + assertEquals(true, catFur.get("catFur")); + + assertEquals(4, dogFur.get("length")); + assertEquals(true, dogFur.get("shaggy")); + assertEquals("very", dogFur.get("dogFur")); + } + + @Test + public void testMutationCatFur() throws ReflectiveOperationException { + Map>> response = execute( + "mutation {makeCat{" + + "item { " + + " ... on Cat { " + + " name " + + " fur{ " + + " calico " + + " length" + + " long" + + " }" + + " } " + + "}" + + "}} " + ) + .getData(); + + var makeCat = response.get("makeCat"); + + var cat = makeCat.get("item"); + + var catFur = (Map) cat.get("fur"); + + assertEquals("name", cat.get("name")); + assertEquals(4, catFur.get("length")); + assertEquals(true, catFur.get("calico")); + assertEquals(true, catFur.get("long")); + } + + private ExecutionResult execute(String query) { + try { + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.generics")).build(); + ExecutionResult result = schema.execute(query); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); + } + return result; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java index 972b08d..b7dea21 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java @@ -1,248 +1,310 @@ -package com.fleetpin.graphql.builder; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import com.fleetpin.graphql.builder.type.SimpleType; - -import graphql.ExecutionResult; -import graphql.GraphQL; -import graphql.introspection.Introspection; - -public class TypeInheritanceParsingTest { - @Test - public void findTypes() throws ReflectiveOperationException { - Map>>> response = execute("{__schema {types {name}}} ").getData(); - var types = response.get("__schema").get("types"); - var count = types.stream().filter(map -> map.get("name").equals("SimpleType")).count(); - Assertions.assertEquals(1, count); - } - - @Test - public void testAnimalName() throws ReflectiveOperationException { - var name = getField("Animal", "INTERFACE", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - - @Test - public void testCatName() throws ReflectiveOperationException { - var name = getField("Cat", "OBJECT", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testCatAge() throws ReflectiveOperationException { - var name = getField("Cat", "OBJECT", "age"); - var nonNull = confirmNonNull(name); - confirmNumber(nonNull); - } - - @Test - public void testCatFur() throws ReflectiveOperationException { - var name = getField("Cat", "OBJECT", "fur"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); - } - - @Test - public void testCatCalico() throws ReflectiveOperationException { - var name = getField("Cat", "OBJECT", "calico"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); - } - - - @Test - public void testDogName() throws ReflectiveOperationException { - var name = getField("Dog", "OBJECT", "name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testDogFur() throws ReflectiveOperationException { - var name = getField("Dog", "OBJECT", "fur"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testDogAge() throws ReflectiveOperationException { - var name = getField("Dog", "OBJECT", "age"); - var nonNull = confirmNonNull(name); - confirmNumber(nonNull); - } - - - - private void confirmString(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("String", type.get("name")); - } - - private void confirmBoolean(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("Boolean", type.get("name")); - } - - private void confirmNumber(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("Int", type.get("name")); - } - - - private Map confirmNonNull(Map type) { - Assertions.assertEquals("NON_NULL", type.get("kind")); - var toReturn = (Map) type.get("ofType"); - Assertions.assertNotNull(toReturn); - return toReturn; - } - - private Map confirmArray(Map type) { - Assertions.assertEquals("LIST", type.get("kind")); - var toReturn = (Map) type.get("ofType"); - Assertions.assertNotNull(toReturn); - return toReturn; - } - - public Map getField(String typeName, String kind, String name) throws ReflectiveOperationException { - Map> response = execute("{" + - " __type(name: \"" + typeName + "\") {" + - " name" + - " kind" + - " fields {" + - " name" + - " type {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " }" + - " }" + - " }" + - " }" + - " }" + - " }" + - "} ").getData(); - var type = response.get("__type"); - Assertions.assertEquals(typeName, type.get("name")); - Assertions.assertEquals(kind, type.get("kind")); - - List> fields = (List>) type.get("fields"); - var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); - Assertions.assertEquals(name, field.get("name")); - return (Map) field.get("type"); - } - - @Test - public void testQueryCatFur() throws ReflectiveOperationException { - Map>> response = execute("query {animals{" + - "name " + - "... on Cat { " + - " age " + - " fur " + - " calico " + - "} " + - "... on Dog {" + - " age " + - "} " + - "}} ").getData(); - - var animals = response.get("animals"); - - var cat = animals.get(0); - var dog = animals.get(1); - - - assertEquals("name", cat.get("name")); - assertEquals(3, cat.get("age")); - assertEquals(true, cat.get("fur")); - assertEquals(true, cat.get("calico")); - - assertEquals("name", dog.get("name")); - assertEquals(6, dog.get("age")); - } - - @Test - public void testQueryDogFur() throws ReflectiveOperationException { - Map>> response = execute("query {animals{" + - "name " + - "... on Cat { " + - " age " + - " calico " + - "} " + - "... on Dog {" + - " age " + - " fur " + - "} " + - "}} ").getData(); - - var animals = response.get("animals"); - - var cat = animals.get(0); - var dog = animals.get(1); - - - assertEquals("name", cat.get("name")); - assertEquals(3, cat.get("age")); - assertEquals(true, cat.get("calico")); - - assertEquals("name", dog.get("name")); - assertEquals(6, dog.get("age")); - assertEquals("shaggy", dog.get("fur")); - } - - @Test - public void testBothFurFails() throws ReflectiveOperationException { - Assertions.assertThrows(RuntimeException.class,() -> { - execute("query {animals{" + - "name " + - "... on Cat { " + - " age " + - " fur " + - " calico " + - "} " + - "... on Dog {" + - " age " + - " fur " + - "} " + - "}} "); - - }); - - } - - - private ExecutionResult execute(String query) { - try { - GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type")).build(); - ExecutionResult result = schema.execute(query); - if(!result.getErrors().isEmpty()) { - throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup - } - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - - } - -} +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import graphql.ExecutionResult; +import graphql.GraphQL; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TypeInheritanceParsingTest { + + @Test + public void findTypes() throws ReflectiveOperationException { + Map>>> response = execute("{__schema {types {name}}} ").getData(); + var types = response.get("__schema").get("types"); + var count = types.stream().filter(map -> map.get("name").equals("SimpleType")).count(); + Assertions.assertEquals(1, count); + } + + @Test + public void testAnimalName() throws ReflectiveOperationException { + var name = getField("Animal", "INTERFACE", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testAnimalInputName() throws ReflectiveOperationException { + var name = getField("AnimalInput", "INPUT_OBJECT", "cat"); + confirmInputObject(name, "CatInput"); + } + + @Test + public void testCatName() throws ReflectiveOperationException { + var name = getField("Cat", "OBJECT", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testCatAge() throws ReflectiveOperationException { + var name = getField("Cat", "OBJECT", "age"); + var nonNull = confirmNonNull(name); + confirmNumber(nonNull); + } + + @Test + public void testCatFur() throws ReflectiveOperationException { + var name = getField("Cat", "OBJECT", "fur"); + var nonNull = confirmNonNull(name); + confirmBoolean(nonNull); + } + + @Test + public void testCatCalico() throws ReflectiveOperationException { + var name = getField("Cat", "OBJECT", "calico"); + var nonNull = confirmNonNull(name); + confirmBoolean(nonNull); + } + + @Test + public void testDogName() throws ReflectiveOperationException { + var name = getField("Dog", "OBJECT", "name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testDogFur() throws ReflectiveOperationException { + var name = getField("Dog", "OBJECT", "fur"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testDogAge() throws ReflectiveOperationException { + var name = getField("Dog", "OBJECT", "age"); + var nonNull = confirmNonNull(name); + confirmNumber(nonNull); + } + + private void confirmString(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("String", type.get("name")); + } + + private void confirmInputObject(Map type, String name) { + Assertions.assertEquals("INPUT_OBJECT", type.get("kind")); + Assertions.assertEquals(name, type.get("name")); + } + + private void confirmBoolean(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("Boolean", type.get("name")); + } + + private void confirmNumber(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("Int", type.get("name")); + } + + private Map confirmNonNull(Map type) { + Assertions.assertEquals("NON_NULL", type.get("kind")); + var toReturn = (Map) type.get("ofType"); + Assertions.assertNotNull(toReturn); + return toReturn; + } + + private Map confirmArray(Map type) { + Assertions.assertEquals("LIST", type.get("kind")); + var toReturn = (Map) type.get("ofType"); + Assertions.assertNotNull(toReturn); + return toReturn; + } + + public Map getField(String typeName, String kind, String name) throws ReflectiveOperationException { + Map> response = execute( + "{" + + " __type(name: \"" + + typeName + + "\") {" + + " name" + + " kind" + + " fields {" + + " name" + + " type {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " }" + + " }" + + " }" + + " }" + + " }" + + " inputFields {" + + " name" + + " type {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " }" + + " }" + + " }" + + " }" + + " }" + + " }" + + "} " + ) + .getData(); + var type = response.get("__type"); + Assertions.assertEquals(typeName, type.get("name")); + Assertions.assertEquals(kind, type.get("kind")); + + List> fields = (List>) type.get("fields"); + if (fields == null) { + fields = (List>) type.get("inputFields"); + } + var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); + Assertions.assertEquals(name, field.get("name")); + return (Map) field.get("type"); + } + + @Test + public void testQueryCatFur() throws ReflectiveOperationException { + Map>> response = execute( + "query {animals{" + "name " + "... on Cat { " + " age " + " fur " + " calico " + "} " + "... on Dog {" + " age " + "} " + "}} " + ) + .getData(); + + var animals = response.get("animals"); + + var cat = animals.get(0); + var dog = animals.get(1); + + assertEquals("name", cat.get("name")); + assertEquals(3, cat.get("age")); + assertEquals(true, cat.get("fur")); + assertEquals(true, cat.get("calico")); + + assertEquals("name", dog.get("name")); + assertEquals(6, dog.get("age")); + } + + @Test + public void testQueryDogFur() throws ReflectiveOperationException { + Map>> response = execute( + "query {animals{" + "name " + "... on Cat { " + " age " + " calico " + "} " + "... on Dog {" + " age " + " fur " + "} " + "}} " + ) + .getData(); + + var animals = response.get("animals"); + + var cat = animals.get(0); + var dog = animals.get(1); + + assertEquals("name", cat.get("name")); + assertEquals(3, cat.get("age")); + assertEquals(true, cat.get("calico")); + + assertEquals("name", dog.get("name")); + assertEquals(6, dog.get("age")); + assertEquals("shaggy", dog.get("fur")); + } + + @Test + public void testBothFurFails() throws ReflectiveOperationException { + Assertions.assertThrows( + RuntimeException.class, + () -> { + execute( + "query {animals{" + "name " + "... on Cat { " + " age " + " fur " + " calico " + "} " + "... on Dog {" + " age " + " fur " + "} " + "}} " + ); + } + ); + } + + @Test + public void testOneOf() throws ReflectiveOperationException { + Map>> response = execute( + "mutation {myAnimals(animals: [" + + "{cat: {fur: true, calico: false, name: \"socks\", age: 4}}," + + "{dog: {fur: \"short\", name: \"patches\", age: 5}}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " + ) + .getData(); + + var animals = response.get("myAnimals"); + + var cat = animals.get(0); + var dog = animals.get(1); + + assertEquals("socks", cat.get("name")); + assertEquals(4, cat.get("age")); + assertEquals(false, cat.get("calico")); + + assertEquals("patches", dog.get("name")); + assertEquals(5, dog.get("age")); + assertEquals("short", dog.get("fur")); + } + + @Test + public void testOneOfError() throws ReflectiveOperationException { + var exception = assertThrows( + RuntimeException.class, + () -> + execute( + "mutation {myAnimals(animals: [" + + "{cat: {fur: true, calico: false, name: \"socks\", age: 4}," + + "dog: {fur: \"short\", name: \"patches\", age: 5}}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " + ) + .getData() + ); + + assertTrue(exception.getMessage().contains("OneOf must only have a single field set")); + } + + private ExecutionResult execute(String query) { + try { + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type")).build(); + ExecutionResult result = schema.execute(query); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup + } + return result; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeParsingTest.java index 56b6425..cdb3761 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeParsingTest.java @@ -1,231 +1,236 @@ -package com.fleetpin.graphql.builder; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import graphql.ExecutionResult; -import graphql.GraphQL; - -public class TypeParsingTest { - @Test - public void findTypes() throws ReflectiveOperationException { - Map>>> response = execute("{__schema {types {name}}} ").getData(); - var types = response.get("__schema").get("types"); - var count = types.stream().filter(map -> map.get("name").equals("SimpleType")).count(); - Assertions.assertEquals(1, count); - } - - @Test - public void testName() throws ReflectiveOperationException { - var name = getField("name"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - - @Test - public void testDeleted() throws ReflectiveOperationException { - var name = getField("deleted"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); - } - - - @Test - public void testAlive() throws ReflectiveOperationException { - var name = getField("alive"); - confirmBoolean(name); - } - - @Test - public void testParts() throws ReflectiveOperationException { - var type = getField("parts"); - type = confirmNonNull(type); - type = confirmArray(type); - type = confirmNonNull(type); - confirmString(type); - } - - @Test - public void testGappyParts() throws ReflectiveOperationException { - var type = getField("gappyParts"); - type = confirmNonNull(type); - type = confirmArray(type); - confirmString(type); - } - - @Test - public void testOptioanlParts() throws ReflectiveOperationException { - var type = getField("optionalParts"); - type = confirmArray(type); - type = confirmNonNull(type); - confirmString(type); - } - - @Test - public void testOptioanlGappyParts() throws ReflectiveOperationException { - var type = getField("optionalGappyParts"); - type = confirmArray(type); - confirmString(type); - } - - - @Test - public void testNameFuture() throws ReflectiveOperationException { - var name = getField("nameFuture"); - var nonNull = confirmNonNull(name); - confirmString(nonNull); - } - @Test - public void isDeletedFuture() throws ReflectiveOperationException { - var name = getField("deletedFuture"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); - } - @Test - public void testAliveFuture() throws ReflectiveOperationException { - var name = getField("aliveFuture"); - confirmBoolean(name); - } - @Test - public void testPartsFuture() throws ReflectiveOperationException { - var type = getField("partsFuture"); - type = confirmNonNull(type); - type = confirmArray(type); - type = confirmNonNull(type); - confirmString(type); - } - @Test - public void testGappyPartsFuture() throws ReflectiveOperationException { - var type = getField("gappyPartsFuture"); - type = confirmNonNull(type); - type = confirmArray(type); - confirmString(type); - } - @Test - public void testOptionalPartsFuture() throws ReflectiveOperationException { - var type = getField("optionalPartsFuture"); - type = confirmArray(type); - type = confirmNonNull(type); - confirmString(type); - } - @Test - public void testOptionalGappyPartsFuture() throws ReflectiveOperationException { - var type = getField("optionalGappyPartsFuture"); - type = confirmArray(type); - confirmString(type); - } - - - - private void confirmString(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("String", type.get("name")); - } - - private void confirmBoolean(Map type) { - Assertions.assertEquals("SCALAR", type.get("kind")); - Assertions.assertEquals("Boolean", type.get("name")); - } - - private Map confirmNonNull(Map type) { - Assertions.assertEquals("NON_NULL", type.get("kind")); - var toReturn = (Map) type.get("ofType"); - Assertions.assertNotNull(toReturn); - return toReturn; - } - - private Map confirmArray(Map type) { - Assertions.assertEquals("LIST", type.get("kind")); - var toReturn = (Map) type.get("ofType"); - Assertions.assertNotNull(toReturn); - return toReturn; - } - - public Map getField(String name) throws ReflectiveOperationException { - Map> response = execute("{" + - " __type(name: \"SimpleType\") {" + - " name" + - " fields {" + - " name" + - " type {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " ofType {" + - " name" + - " kind" + - " }" + - " }" + - " }" + - " }" + - " }" + - " }" + - "} ").getData(); - var type = response.get("__type"); - Assertions.assertEquals("SimpleType", type.get("name")); - - List> fields = (List>) type.get("fields"); - var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); - Assertions.assertEquals(name, field.get("name")); - return (Map) field.get("type"); - } - - @Test - public void testQuery() throws ReflectiveOperationException { - Map> response = execute("query {simpleType{" + - "name " + - "deleted " + - "alive " + - "parts " + - "gappyParts " + - "optionalParts " + - "optionalGappyParts " + - "nameFuture " + - "deletedFuture " + - "aliveFuture " + - "partsFuture " + - "gappyPartsFuture " + - "optionalPartsFuture " + - "optionalGappyPartsFuture " + - "}} ").getData(); - - var simpleType = response.get("simpleType"); - assertEquals("green", simpleType.get("name")); - assertEquals(false, simpleType.get("deleted")); - assertEquals(null, simpleType.get("alive")); - assertEquals(Arrays.asList("green", "eggs"), simpleType.get("parts")); - assertEquals(Arrays.asList(null, "eggs"), simpleType.get("gappyParts")); - assertEquals(null, simpleType.get("optionalParts")); - assertEquals(Arrays.asList(), simpleType.get("optionalGappyParts")); - - assertEquals("green", simpleType.get("nameFuture")); - assertEquals(false, simpleType.get("deletedFuture")); - assertEquals(false, simpleType.get("aliveFuture")); - assertEquals(Arrays.asList(), simpleType.get("partsFuture")); - assertEquals(Arrays.asList(), simpleType.get("gappyPartsFuture")); - assertEquals(Arrays.asList(), simpleType.get("optionalPartsFuture")); - assertEquals(null, simpleType.get("optionalGappyPartsFuture")); - } - - - private ExecutionResult execute(String query) throws ReflectiveOperationException { - var schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type")).build(); - ExecutionResult result = schema.execute(query); - if(!result.getErrors().isEmpty()) { - throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup - } - return result; - } - -} +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import graphql.ExecutionResult; +import graphql.GraphQL; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TypeParsingTest { + + @Test + public void findTypes() throws ReflectiveOperationException { + Map>>> response = execute("{__schema {types {name}}} ").getData(); + var types = response.get("__schema").get("types"); + var count = types.stream().filter(map -> map.get("name").equals("SimpleType")).count(); + Assertions.assertEquals(1, count); + } + + @Test + public void testName() throws ReflectiveOperationException { + var name = getField("name"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void testDeleted() throws ReflectiveOperationException { + var name = getField("deleted"); + var nonNull = confirmNonNull(name); + confirmBoolean(nonNull); + } + + @Test + public void testAlive() throws ReflectiveOperationException { + var name = getField("alive"); + confirmBoolean(name); + } + + @Test + public void testParts() throws ReflectiveOperationException { + var type = getField("parts"); + type = confirmNonNull(type); + type = confirmArray(type); + type = confirmNonNull(type); + confirmString(type); + } + + @Test + public void testGappyParts() throws ReflectiveOperationException { + var type = getField("gappyParts"); + type = confirmNonNull(type); + type = confirmArray(type); + confirmString(type); + } + + @Test + public void testOptioanlParts() throws ReflectiveOperationException { + var type = getField("optionalParts"); + type = confirmArray(type); + type = confirmNonNull(type); + confirmString(type); + } + + @Test + public void testOptioanlGappyParts() throws ReflectiveOperationException { + var type = getField("optionalGappyParts"); + type = confirmArray(type); + confirmString(type); + } + + @Test + public void testNameFuture() throws ReflectiveOperationException { + var name = getField("nameFuture"); + var nonNull = confirmNonNull(name); + confirmString(nonNull); + } + + @Test + public void isDeletedFuture() throws ReflectiveOperationException { + var name = getField("deletedFuture"); + var nonNull = confirmNonNull(name); + confirmBoolean(nonNull); + } + + @Test + public void testAliveFuture() throws ReflectiveOperationException { + var name = getField("aliveFuture"); + confirmBoolean(name); + } + + @Test + public void testPartsFuture() throws ReflectiveOperationException { + var type = getField("partsFuture"); + type = confirmNonNull(type); + type = confirmArray(type); + type = confirmNonNull(type); + confirmString(type); + } + + @Test + public void testGappyPartsFuture() throws ReflectiveOperationException { + var type = getField("gappyPartsFuture"); + type = confirmNonNull(type); + type = confirmArray(type); + confirmString(type); + } + + @Test + public void testOptionalPartsFuture() throws ReflectiveOperationException { + var type = getField("optionalPartsFuture"); + type = confirmArray(type); + type = confirmNonNull(type); + confirmString(type); + } + + @Test + public void testOptionalGappyPartsFuture() throws ReflectiveOperationException { + var type = getField("optionalGappyPartsFuture"); + type = confirmArray(type); + confirmString(type); + } + + private void confirmString(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("String", type.get("name")); + } + + private void confirmBoolean(Map type) { + Assertions.assertEquals("SCALAR", type.get("kind")); + Assertions.assertEquals("Boolean", type.get("name")); + } + + private Map confirmNonNull(Map type) { + Assertions.assertEquals("NON_NULL", type.get("kind")); + var toReturn = (Map) type.get("ofType"); + Assertions.assertNotNull(toReturn); + return toReturn; + } + + private Map confirmArray(Map type) { + Assertions.assertEquals("LIST", type.get("kind")); + var toReturn = (Map) type.get("ofType"); + Assertions.assertNotNull(toReturn); + return toReturn; + } + + public Map getField(String name) throws ReflectiveOperationException { + Map> response = execute( + "{" + + " __type(name: \"SimpleType\") {" + + " name" + + " fields {" + + " name" + + " type {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " ofType {" + + " name" + + " kind" + + " }" + + " }" + + " }" + + " }" + + " }" + + " }" + + "} " + ) + .getData(); + var type = response.get("__type"); + Assertions.assertEquals("SimpleType", type.get("name")); + + List> fields = (List>) type.get("fields"); + var field = fields.stream().filter(map -> map.get("name").equals(name)).findAny().get(); + Assertions.assertEquals(name, field.get("name")); + return (Map) field.get("type"); + } + + @Test + public void testQuery() throws ReflectiveOperationException { + Map> response = execute( + "query {simpleType{" + + "name " + + "deleted " + + "alive " + + "parts " + + "gappyParts " + + "optionalParts " + + "optionalGappyParts " + + "nameFuture " + + "deletedFuture " + + "aliveFuture " + + "partsFuture " + + "gappyPartsFuture " + + "optionalPartsFuture " + + "optionalGappyPartsFuture " + + "}} " + ) + .getData(); + + var simpleType = response.get("simpleType"); + assertEquals("green", simpleType.get("name")); + assertEquals(false, simpleType.get("deleted")); + assertEquals(null, simpleType.get("alive")); + assertEquals(Arrays.asList("green", "eggs"), simpleType.get("parts")); + assertEquals(Arrays.asList(null, "eggs"), simpleType.get("gappyParts")); + assertEquals(null, simpleType.get("optionalParts")); + assertEquals(Arrays.asList(), simpleType.get("optionalGappyParts")); + + assertEquals("green", simpleType.get("nameFuture")); + assertEquals(false, simpleType.get("deletedFuture")); + assertEquals(false, simpleType.get("aliveFuture")); + assertEquals(Arrays.asList(), simpleType.get("partsFuture")); + assertEquals(Arrays.asList(), simpleType.get("gappyPartsFuture")); + assertEquals(Arrays.asList(), simpleType.get("optionalPartsFuture")); + assertEquals(null, simpleType.get("optionalGappyPartsFuture")); + } + + private ExecutionResult execute(String query) throws ReflectiveOperationException { + var schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type")).build(); + ExecutionResult result = schema.execute(query); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup + } + return result; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/UnionTest.java b/src/test/java/com/fleetpin/graphql/builder/UnionTest.java new file mode 100644 index 0000000..74103f6 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/UnionTest.java @@ -0,0 +1,74 @@ +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import graphql.ExecutionResult; +import graphql.GraphQL; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class UnionTest { + + @Test + public void testUnion() throws ReflectiveOperationException { + Map>> response = execute( + "query {union{" + + " __typename" + + " ... on SimpleType {" + + " name" + + " }" + + " ... on UnionType {" + + " type {" + + " __typename" + + " ... on SimpleType {" + + " name" + + " }" + + " }" + + " }" + + "}} " + ) + .getData(); + var union = response.get("union"); + assertEquals("green", union.get(0).get("name")); + Map simple = (Map) union.get(1).get("type"); + assertEquals("green", simple.get("name")); + } + + @Test + public void testUnionFailure() throws ReflectiveOperationException { + var error = assertThrows( + RuntimeException.class, + () -> + execute( + "query {unionFailure{" + + " __typename" + + " ... on SimpleType {" + + " name" + + " }" + + " ... on UnionType {" + + " type {" + + " __typename" + + " ... on SimpleType {" + + " name" + + " }" + + " }" + + " }" + + "}} " + ) + ); + assertEquals("Union Union_SimpleType_UnionType Does not support type Boolean", error.getMessage()); + } + + private ExecutionResult execute(String query) throws ReflectiveOperationException { + var schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type")).build(); + ExecutionResult result = schema.execute(query); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup + } + return result; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/Animal.java b/src/test/java/com/fleetpin/graphql/builder/generics/Animal.java index 4507700..5acc423 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/Animal.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/Animal.java @@ -1,63 +1,62 @@ -package com.fleetpin.graphql.builder.generics; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Mutation; -import com.fleetpin.graphql.builder.annotations.Query; - -@Entity -public abstract class Animal{ - - private final T fur; - - Animal(T fur) { - this.fur = fur; - } - - public String getName() { - return "name"; - } - - public T getFur() { - return fur; - } - - public List getFurs() { - return Arrays.asList(fur); - } - - @Query - public static List> animals() { - return Arrays.asList(new Cat(), new Dog()); - } - - @Mutation - public static MutationResponse makeCat() { - return new GenericMutationResponse<>(Optional.of(new Cat())); - } - - - @Entity - public static abstract class MutationResponse { - private Optional> item; - - - public MutationResponse(Optional> item) { - this.item = item; - } - - public Optional> getItem() { - return item; - } - - } - @Entity - public static class GenericMutationResponse extends MutationResponse{ - public GenericMutationResponse(Optional> item) { - super(item); - } - } -} \ No newline at end of file +package com.fleetpin.graphql.builder.generics; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Mutation; +import com.fleetpin.graphql.builder.annotations.Query; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +@Entity +public abstract class Animal { + + private final T fur; + + Animal(T fur) { + this.fur = fur; + } + + public String getName() { + return "name"; + } + + public T getFur() { + return fur; + } + + public List getFurs() { + return Arrays.asList(fur); + } + + @Query + public static List> animals() { + return Arrays.asList(new Cat(), new Dog()); + } + + @Mutation + public static MutationResponse makeCat() { + return new GenericMutationResponse<>(Optional.of(new Cat())); + } + + @Entity + public abstract static class MutationResponse { + + private Optional> item; + + public MutationResponse(Optional> item) { + this.item = item; + } + + public Optional> getItem() { + return item; + } + } + + @Entity + public static class GenericMutationResponse extends MutationResponse { + + public GenericMutationResponse(Optional> item) { + super(item); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/Cat.java b/src/test/java/com/fleetpin/graphql/builder/generics/Cat.java index a683d93..a185bb8 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/Cat.java @@ -1,18 +1,17 @@ -package com.fleetpin.graphql.builder.generics; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Mutation; - -@Entity -public class Cat extends CatFamily{ - - public Cat() { - super(new CatFur()); - } - - @Mutation - public static Cat getCat() { - return null; - } - -} +package com.fleetpin.graphql.builder.generics; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Mutation; + +@Entity +public class Cat extends CatFamily { + + public Cat() { + super(new CatFur()); + } + + @Mutation + public static Cat getCat() { + return null; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/CatFamily.java b/src/test/java/com/fleetpin/graphql/builder/generics/CatFamily.java index 5db2dfc..2692097 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/CatFamily.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/CatFamily.java @@ -1,12 +1,11 @@ -package com.fleetpin.graphql.builder.generics; - -import com.fleetpin.graphql.builder.annotations.Entity; - -@Entity -public abstract class CatFamily extends Animal{ - - CatFamily(R fur) { - super(fur); - } - -} +package com.fleetpin.graphql.builder.generics; + +import com.fleetpin.graphql.builder.annotations.Entity; + +@Entity +public abstract class CatFamily extends Animal { + + CatFamily(R fur) { + super(fur); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/CatFamilyFur.java b/src/test/java/com/fleetpin/graphql/builder/generics/CatFamilyFur.java index 7b77855..81c3404 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/CatFamilyFur.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/CatFamilyFur.java @@ -1,12 +1,11 @@ -package com.fleetpin.graphql.builder.generics; - -import com.fleetpin.graphql.builder.annotations.Entity; - -@Entity -public abstract class CatFamilyFur extends Fur{ - - public boolean isLong() { - return true; - } - -} +package com.fleetpin.graphql.builder.generics; + +import com.fleetpin.graphql.builder.annotations.Entity; + +@Entity +public abstract class CatFamilyFur extends Fur { + + public boolean isLong() { + return true; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/CatFur.java b/src/test/java/com/fleetpin/graphql/builder/generics/CatFur.java index 12fcc88..a539392 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/CatFur.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/CatFur.java @@ -1,11 +1,11 @@ -package com.fleetpin.graphql.builder.generics; - -import com.fleetpin.graphql.builder.annotations.Entity; - -@Entity -public class CatFur extends CatFamilyFur { - public boolean isCalico() { - return true; - } - -} +package com.fleetpin.graphql.builder.generics; + +import com.fleetpin.graphql.builder.annotations.Entity; + +@Entity +public class CatFur extends CatFamilyFur { + + public boolean isCalico() { + return true; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/Dog.java b/src/test/java/com/fleetpin/graphql/builder/generics/Dog.java index 16228d5..46819d7 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/Dog.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/Dog.java @@ -1,21 +1,21 @@ -package com.fleetpin.graphql.builder.generics; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Mutation; - -@Entity -public class Dog extends Animal { - - public Dog() { - super(new DogFur()); - } - - public int getAge() { - return 6; - } - - @Mutation - public static Dog getDog() { - return null; - } -} +package com.fleetpin.graphql.builder.generics; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Mutation; + +@Entity +public class Dog extends Animal { + + public Dog() { + super(new DogFur()); + } + + public int getAge() { + return 6; + } + + @Mutation + public static Dog getDog() { + return null; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/DogFur.java b/src/test/java/com/fleetpin/graphql/builder/generics/DogFur.java index 487311b..77992dd 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/DogFur.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/DogFur.java @@ -1,15 +1,15 @@ -package com.fleetpin.graphql.builder.generics; - -import com.fleetpin.graphql.builder.annotations.Entity; - -@Entity -public class DogFur extends Fur { - public boolean isShaggy() { - return true; - } - - public String getLong() { - return "very"; - } - -} +package com.fleetpin.graphql.builder.generics; + +import com.fleetpin.graphql.builder.annotations.Entity; + +@Entity +public class DogFur extends Fur { + + public boolean isShaggy() { + return true; + } + + public String getLong() { + return "very"; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/Fur.java b/src/test/java/com/fleetpin/graphql/builder/generics/Fur.java index 2a95df5..fea78e2 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/Fur.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/Fur.java @@ -1,11 +1,11 @@ -package com.fleetpin.graphql.builder.generics; - -import com.fleetpin.graphql.builder.annotations.Entity; - -@Entity -public abstract class Fur { - - public int getLength() { - return 4; - } -} +package com.fleetpin.graphql.builder.generics; + +import com.fleetpin.graphql.builder.annotations.Entity; + +@Entity +public abstract class Fur { + + public int getLength() { + return 4; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Animal.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Animal.java index ee0bfac..bb45920 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Animal.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Animal.java @@ -1,19 +1,18 @@ -package com.fleetpin.graphql.builder.inputgenerics; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.SchemaOption; - -@Entity -public abstract class Animal { - - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - -} +package com.fleetpin.graphql.builder.inputgenerics; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.SchemaOption; + +@Entity +public abstract class Animal { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalInput.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalInput.java index 88621e5..f435f88 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalInput.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalInput.java @@ -1,27 +1,27 @@ -package com.fleetpin.graphql.builder.inputgenerics; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.SchemaOption; - -@Entity(SchemaOption.INPUT) -public class AnimalInput { - String id; - T animal; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public T getAnimal() { - return animal; - } - - public void setAnimal(T animal) { - this.animal = animal; - } - -} \ No newline at end of file +package com.fleetpin.graphql.builder.inputgenerics; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.SchemaOption; + +@Entity(SchemaOption.INPUT) +public class AnimalInput { + + String id; + T animal; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public T getAnimal() { + return animal; + } + + public void setAnimal(T animal) { + this.animal = animal; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalOuterWrapper.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalOuterWrapper.java index 338a49a..f506770 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalOuterWrapper.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalOuterWrapper.java @@ -1,27 +1,27 @@ -package com.fleetpin.graphql.builder.inputgenerics; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.SchemaOption; - -@Entity(SchemaOption.BOTH) -public class AnimalOuterWrapper { - String id; - AnimalWrapper animal; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public AnimalWrapper getAnimal() { - return animal; - } - - public void setAnimal(AnimalWrapper animal) { - this.animal = animal; - } - -} \ No newline at end of file +package com.fleetpin.graphql.builder.inputgenerics; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.SchemaOption; + +@Entity(SchemaOption.BOTH) +public class AnimalOuterWrapper { + + String id; + AnimalWrapper animal; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public AnimalWrapper getAnimal() { + return animal; + } + + public void setAnimal(AnimalWrapper animal) { + this.animal = animal; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalWrapper.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalWrapper.java index debc015..01fd6c7 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalWrapper.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalWrapper.java @@ -1,26 +1,26 @@ -package com.fleetpin.graphql.builder.inputgenerics; - -import com.fleetpin.graphql.builder.annotations.Entity; - -@Entity -public class AnimalWrapper { - String id; - T animal; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public T getAnimal() { - return animal; - } - - public void setAnimal(T animal) { - this.animal = animal; - } - -} \ No newline at end of file +package com.fleetpin.graphql.builder.inputgenerics; + +import com.fleetpin.graphql.builder.annotations.Entity; + +@Entity +public class AnimalWrapper { + + String id; + T animal; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public T getAnimal() { + return animal; + } + + public void setAnimal(T animal) { + this.animal = animal; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Cat.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Cat.java index ea2ab02..8b2ccef 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Cat.java @@ -1,47 +1,43 @@ -package com.fleetpin.graphql.builder.inputgenerics; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Mutation; -import com.fleetpin.graphql.builder.annotations.Query; -import com.fleetpin.graphql.builder.annotations.SchemaOption; - -@Entity(SchemaOption.BOTH) -public class Cat extends Animal { - - private boolean fur; - - - public void setFur(boolean fur) { - this.fur = fur; - } - - public boolean isFur() { - return fur; - } - - @Query - public static String getCat() { - return "cat"; - } - - @Mutation - public static String addCat(CatAnimalInput input) { - return input.getAnimal().getName(); - } - - - @Mutation - public static boolean addCatGenerics(AnimalInput input) { - return input.getAnimal().isFur(); - } - - - @Mutation - public static AnimalOuterWrapper addNestedGenerics(AnimalInput input) { - var wrapper = new AnimalOuterWrapper(); - wrapper.setAnimal(new AnimalWrapper()); - wrapper.getAnimal().setAnimal(input.getAnimal()); - return wrapper; - } - -} +package com.fleetpin.graphql.builder.inputgenerics; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Mutation; +import com.fleetpin.graphql.builder.annotations.Query; +import com.fleetpin.graphql.builder.annotations.SchemaOption; + +@Entity(SchemaOption.BOTH) +public class Cat extends Animal { + + private boolean fur; + + public void setFur(boolean fur) { + this.fur = fur; + } + + public boolean isFur() { + return fur; + } + + @Query + public static String getCat() { + return "cat"; + } + + @Mutation + public static String addCat(CatAnimalInput input) { + return input.getAnimal().getName(); + } + + @Mutation + public static boolean addCatGenerics(AnimalInput input) { + return input.getAnimal().isFur(); + } + + @Mutation + public static AnimalOuterWrapper addNestedGenerics(AnimalInput input) { + var wrapper = new AnimalOuterWrapper(); + wrapper.setAnimal(new AnimalWrapper()); + wrapper.getAnimal().setAnimal(input.getAnimal()); + return wrapper; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/CatAnimalInput.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/CatAnimalInput.java index d8ae618..1416c55 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/CatAnimalInput.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/CatAnimalInput.java @@ -1,9 +1,7 @@ -package com.fleetpin.graphql.builder.inputgenerics; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.SchemaOption; - -@Entity(SchemaOption.INPUT) -public class CatAnimalInput extends AnimalInput { - -} \ No newline at end of file +package com.fleetpin.graphql.builder.inputgenerics; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.SchemaOption; + +@Entity(SchemaOption.INPUT) +public class CatAnimalInput extends AnimalInput {} diff --git a/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java b/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java index 9ff0deb..1ec5377 100644 --- a/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java +++ b/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java @@ -12,14 +12,19 @@ package com.fleetpin.graphql.builder.parameter; -import java.util.List; -import java.util.Optional; - +import com.fleetpin.graphql.builder.annotations.Entity; import com.fleetpin.graphql.builder.annotations.Id; import com.fleetpin.graphql.builder.annotations.Query; +import java.util.List; +import java.util.Optional; +@Entity public class Parameter { + public Optional getNullOptional() { + return null; + } + @Query public static String requiredString(String type) { return type; @@ -42,6 +47,11 @@ public static Optional optionalId(@Id Optional type) { return type; } + @Query + public static Parameter optionalIdNull() { + return new Parameter(); + } + @Query public static List requiredListString(List type) { return type; @@ -85,20 +95,19 @@ public static List> requiredListOptionalId(@Id List>> optionalListOptionalId(@Id Optional>> type) { return type; } - + @Query public static String multipleArguments(String first, String second) { return first + ":" + second; } - + @Query public static String multipleArgumentsOptional(Optional first, Optional second) { return first.orElse("") + ":" + second.orElse(""); } - + @Query public static String multipleArgumentsMix(String first, Optional second) { return first + ":" + second.orElse(""); } - } diff --git a/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java b/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java index 70a20d2..1459b23 100644 --- a/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java +++ b/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java @@ -12,17 +12,23 @@ package com.fleetpin.graphql.builder.parameter; -import java.util.List; -import java.util.Optional; - import com.fleetpin.graphql.builder.annotations.Entity; import com.fleetpin.graphql.builder.annotations.Query; import com.fleetpin.graphql.builder.annotations.SchemaOption; +import java.util.List; +import java.util.Optional; public class TypeInputParameter { + @Entity + public enum AnimalType { + CAT, + DOG, + } + @Entity(SchemaOption.BOTH) public static class InputTest { + private String value; public String getValue() { @@ -34,6 +40,11 @@ public void setValue(String value) { } } + @Query + public static AnimalType enumTest(AnimalType type) { + return type; + } + @Query public static InputTest requiredType(InputTest type) { type.getValue(); // makes sure is right type and not odd runtime passthrough @@ -69,10 +80,8 @@ public static List> requiredListOptionalType(List>> optionalListOptionalType( - Optional>> type) { + public static Optional>> optionalListOptionalType(Optional>> type) { type.map(tt -> tt.stream().map(t -> t.map(InputTest::getValue))); return type; } - } diff --git a/src/test/java/com/fleetpin/graphql/builder/publishRestrictions/Test.java b/src/test/java/com/fleetpin/graphql/builder/publishRestrictions/Test.java index 71c486e..3d6fe9b 100644 --- a/src/test/java/com/fleetpin/graphql/builder/publishRestrictions/Test.java +++ b/src/test/java/com/fleetpin/graphql/builder/publishRestrictions/Test.java @@ -1,24 +1,22 @@ package com.fleetpin.graphql.builder.publishRestrictions; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; - -import org.reactivestreams.Publisher; - import com.fleetpin.graphql.builder.RestrictType; import com.fleetpin.graphql.builder.RestrictTypeFactory; import com.fleetpin.graphql.builder.annotations.Entity; import com.fleetpin.graphql.builder.annotations.Query; import com.fleetpin.graphql.builder.annotations.Restrict; import com.fleetpin.graphql.builder.annotations.Subscription; - import graphql.schema.DataFetchingEnvironment; import io.reactivex.rxjava3.core.Flowable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import org.reactivestreams.Publisher; @Entity @Restrict(Test.Restrictor.class) public class Test { + static Executor executor = CompletableFuture.delayedExecutor(100, TimeUnit.MILLISECONDS); private boolean value; @@ -29,12 +27,12 @@ public Test(boolean value) { public boolean isValue() { return value; } - + @Query public static String MustHaveAQuery() { return "String"; } - + @Subscription public static Publisher test() { return Flowable.just(new Test(false)).flatMap(f -> Flowable.fromCompletionStage(CompletableFuture.supplyAsync(() -> f, executor))); @@ -45,13 +43,11 @@ public static class Restrictor implements RestrictTypeFactory, RestrictTyp @Override public CompletableFuture> create(DataFetchingEnvironment context) { return CompletableFuture.supplyAsync(() -> this, executor); - } @Override public CompletableFuture allow(Test obj) { return CompletableFuture.supplyAsync(() -> false, executor); } - } -} \ No newline at end of file +} diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/EntityRestrictions.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/EntityRestrictions.java index 5292b49..b10ab32 100644 --- a/src/test/java/com/fleetpin/graphql/builder/restrictions/EntityRestrictions.java +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/EntityRestrictions.java @@ -1,12 +1,10 @@ package com.fleetpin.graphql.builder.restrictions; -import java.util.concurrent.CompletableFuture; - import com.fleetpin.graphql.builder.RestrictType; import com.fleetpin.graphql.builder.RestrictTypeFactory; import com.fleetpin.graphql.builder.restrictions.parameter.RestrictedEntity; - import graphql.schema.DataFetchingEnvironment; +import java.util.concurrent.CompletableFuture; public class EntityRestrictions implements RestrictTypeFactory { @@ -14,7 +12,7 @@ public class EntityRestrictions implements RestrictTypeFactory public CompletableFuture> create(DataFetchingEnvironment context) { return CompletableFuture.completedFuture(new DatabaseRestrict()); } - + public static class DatabaseRestrict implements RestrictType { @Override @@ -22,4 +20,4 @@ public CompletableFuture allow(RestrictedEntity obj) { return CompletableFuture.completedFuture(obj.isAllowed()); } } -} \ No newline at end of file +} diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java index a093fc0..c90547f 100644 --- a/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java @@ -1,106 +1,100 @@ package com.fleetpin.graphql.builder.restrictions; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fleetpin.graphql.builder.SchemaBuilder; +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fleetpin.graphql.builder.SchemaBuilder; - -import graphql.ExecutionInput; -import graphql.ExecutionResult; -import graphql.GraphQL; - public class RestrictionTypesTest { + /* * Really basic tests, just to ensure restrictions work on different types. */ - + private static GraphQL schema; + @BeforeAll public static void init() throws ReflectiveOperationException { - schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.restrictions.parameter")).build(); + schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.restrictions.parameter")).build(); } - + private static String singleQueryGql = "query entityQuery( $allowed: Boolean! ) { single(allowed: $allowed) { __typename } }"; private static String singleOptionalQueryGql = "query entityQuery( $allowed: Boolean ) { singleOptional(allowed: $allowed) { __typename } }"; private static String listQueryGql = "query entityQuery( $allowed: [Boolean!]! ) { list(allowed: $allowed) { __typename } }"; private static String listOptionalQueryGql = "query entityQuery( $allowed: [Boolean!] ) { listOptional(allowed: $allowed) { __typename } }"; - - + @Test public void singleEntityQuery() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { Map variables = new HashMap<>(); variables.put("allowed", true); - Map> response = execute(singleQueryGql, variables).getData(); + Map> response = execute(singleQueryGql, variables).getData(); // No fancy checks. Just want to make ensure it executes without issue. Assertions.assertTrue(response.get("single").containsKey("__typename")); } - + @Test public void singleOptionalEntityQuery() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { Map variables = new HashMap<>(); - + // Allowed variables.put("allowed", true); - Map> responseAllowed = execute(singleOptionalQueryGql, variables).getData(); + Map> responseAllowed = execute(singleOptionalQueryGql, variables).getData(); Assertions.assertTrue(responseAllowed.get("singleOptional").containsKey("__typename")); - + // Not allowed - variables.put("allowed", false); + variables.put("allowed", false); Map responseDenied = execute(singleOptionalQueryGql, variables).getData(); Assertions.assertNull(responseDenied.get("singleOptional")); } - - + @Test public void listEntityQuery() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { Map variables = new HashMap<>(); - - variables.put("allowed", Arrays.asList(true,true,true)); + + variables.put("allowed", Arrays.asList(true, true, true)); Map> responseAllAllowed = execute(listQueryGql, variables).getData(); Assertions.assertEquals(3, responseAllAllowed.get("list").size()); - - variables.put("allowed", Arrays.asList(true,false,true)); + + variables.put("allowed", Arrays.asList(true, false, true)); Map> responseSomeAllowed = execute(listQueryGql, variables).getData(); Assertions.assertEquals(2, responseSomeAllowed.get("list").size()); - - variables.put("allowed", Arrays.asList(false,false,false)); + + variables.put("allowed", Arrays.asList(false, false, false)); Map> responseNoneAllowed = execute(listQueryGql, variables).getData(); Assertions.assertEquals(0, responseNoneAllowed.get("list").size()); } - - + @Test public void optionalListEntityQuery() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { Map variables = new HashMap<>(); - + // No list passed through Map> responseNoVariables = execute(listOptionalQueryGql, variables).getData(); Assertions.assertNull(responseNoVariables.get("listOptional")); - - variables.put("allowed", Arrays.asList(true,true,true)); + + variables.put("allowed", Arrays.asList(true, true, true)); Map> responseAllAllowed = execute(listOptionalQueryGql, variables).getData(); Assertions.assertEquals(3, responseAllAllowed.get("listOptional").size()); - - variables.put("allowed", Arrays.asList(true,false,true)); + + variables.put("allowed", Arrays.asList(true, false, true)); Map> responseSomeAllowed = execute(listOptionalQueryGql, variables).getData(); Assertions.assertEquals(2, responseSomeAllowed.get("listOptional").size()); - - variables.put("allowed", Arrays.asList(false,false,false)); + + variables.put("allowed", Arrays.asList(false, false, false)); Map> responseNoneAllowed = execute(listOptionalQueryGql, variables).getData(); Assertions.assertEquals(0, responseNoneAllowed.get("listOptional").size()); } - - private static ExecutionResult execute(String query, Map variables) - throws JsonMappingException, JsonProcessingException { - + + private static ExecutionResult execute(String query, Map variables) throws JsonMappingException, JsonProcessingException { var input = ExecutionInput.newExecutionInput().query(query).variables(variables).build(); ExecutionResult result = schema.execute(input); if (!result.getErrors().isEmpty()) { @@ -108,5 +102,4 @@ private static ExecutionResult execute(String query, Map variabl } return result; } - } diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java index 1f1ee7d..bfc0eae 100644 --- a/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java @@ -12,14 +12,13 @@ package com.fleetpin.graphql.builder.restrictions.parameter; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - import com.fleetpin.graphql.builder.annotations.Entity; import com.fleetpin.graphql.builder.annotations.Query; import com.fleetpin.graphql.builder.annotations.Restrict; import com.fleetpin.graphql.builder.restrictions.EntityRestrictions; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; @Entity @Restrict(EntityRestrictions.class) @@ -35,14 +34,13 @@ public void setAllowed(boolean allowed) { this.allowed = allowed; } - @Query public static RestrictedEntity single(Boolean allowed) { RestrictedEntity entity = new RestrictedEntity(); entity.setAllowed(allowed); return entity; } - + @Query public static Optional singleOptional(Optional allowed) { if (allowed.isEmpty()) return Optional.empty(); @@ -50,25 +48,33 @@ public static Optional singleOptional(Optional allowe entity.setAllowed(allowed.get()); return Optional.of(entity); } - + @Query public static List list(List allowed) { - return allowed.stream().map(isAllowed -> { - RestrictedEntity entity = new RestrictedEntity(); - entity.setAllowed(isAllowed); - return entity; - }).collect(Collectors.toList()); + return allowed + .stream() + .map(isAllowed -> { + RestrictedEntity entity = new RestrictedEntity(); + entity.setAllowed(isAllowed); + return entity; + }) + .collect(Collectors.toList()); } - + @Query public static Optional> listOptional(Optional> allowed) { if (allowed.isEmpty()) return Optional.empty(); - - return Optional.of(allowed.get().stream().map(isAllowed -> { - RestrictedEntity entity = new RestrictedEntity(); - entity.setAllowed(isAllowed); - return entity; - }).collect(Collectors.toList())); + + return Optional.of( + allowed + .get() + .stream() + .map(isAllowed -> { + RestrictedEntity entity = new RestrictedEntity(); + entity.setAllowed(isAllowed); + return entity; + }) + .collect(Collectors.toList()) + ); } - } diff --git a/src/test/java/com/fleetpin/graphql/builder/type/Circular.java b/src/test/java/com/fleetpin/graphql/builder/type/Circular.java index 6621914..2260982 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/Circular.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/Circular.java @@ -1,19 +1,17 @@ - -package com.fleetpin.graphql.builder.type; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Mutation; - -@Entity -public class Circular { - - public Circular getCircular() { - return null; - } - - - @Mutation - public static Circular circularTest() { - return new Circular(); - } -} +package com.fleetpin.graphql.builder.type; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Mutation; + +@Entity +public class Circular { + + public Circular getCircular() { + return null; + } + + @Mutation + public static Circular circularTest() { + return new Circular(); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/DeprecatedObject.java b/src/test/java/com/fleetpin/graphql/builder/type/DeprecatedObject.java index cb53174..a53edac 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/DeprecatedObject.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/DeprecatedObject.java @@ -1,4 +1,3 @@ - package com.fleetpin.graphql.builder.type; import com.fleetpin.graphql.builder.annotations.Entity; @@ -9,10 +8,10 @@ public class DeprecatedObject { @GraphQLDeprecated("spelling") - public DeprecatedObject getNaame() { + public DeprecatedObject getNaame() { return null; } - + @Query @GraphQLDeprecated("old") public static DeprecatedObject deprecatedTest() { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/DescriptionObject.java b/src/test/java/com/fleetpin/graphql/builder/type/DescriptionObject.java index 5db8659..34b6fa9 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/DescriptionObject.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/DescriptionObject.java @@ -1,4 +1,3 @@ - package com.fleetpin.graphql.builder.type; import com.fleetpin.graphql.builder.annotations.Entity; @@ -10,10 +9,10 @@ public class DescriptionObject { @GraphQLDescription("first and last") - public DescriptionObject getName() { + public DescriptionObject getName() { return null; } - + @Query @GraphQLDescription("returns something") public static DescriptionObject descriptionTest() { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/SimpleType.java b/src/test/java/com/fleetpin/graphql/builder/type/SimpleType.java index f7c5b51..ca328c9 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/SimpleType.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/SimpleType.java @@ -1,74 +1,73 @@ -package com.fleetpin.graphql.builder.type; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Query; - -@Entity -public class SimpleType { - - public String getName() { - return "green"; - } - - public boolean isDeleted() { - return false; - } - - public Optional getAlive() { - return Optional.empty(); - } - - public List getParts() { - return Arrays.asList("green", "eggs"); - } - - public List> getGappyParts() { - return Arrays.asList(Optional.empty(), Optional.of("eggs")); - } - - public Optional> getOptionalParts() { - return Optional.empty(); - } - - public Optional>> getOptionalGappyParts() { - return Optional.of(Arrays.asList()); - } - - public CompletableFuture getNameFuture() { - return CompletableFuture.completedFuture("green"); - } - - public CompletableFuture isDeletedFuture() { - return CompletableFuture.completedFuture(false); - } - - public CompletableFuture> getAliveFuture() { - return CompletableFuture.completedFuture(Optional.of(false)); - } - - public CompletableFuture> getPartsFuture() { - return CompletableFuture.completedFuture(Arrays.asList()); - } - - public CompletableFuture>> getGappyPartsFuture() { - return CompletableFuture.completedFuture(Arrays.asList()); - } - - public CompletableFuture>> getOptionalPartsFuture() { - return CompletableFuture.completedFuture(Optional.of(Arrays.asList())); - } - - public CompletableFuture>>> getOptionalGappyPartsFuture() { - return CompletableFuture.completedFuture(Optional.empty()); - } - - @Query - public static SimpleType simpleType() { - return new SimpleType(); - } -} +package com.fleetpin.graphql.builder.type; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Query; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +@Entity +public class SimpleType { + + public String getName() { + return "green"; + } + + public boolean isDeleted() { + return false; + } + + public Optional getAlive() { + return Optional.empty(); + } + + public List getParts() { + return Arrays.asList("green", "eggs"); + } + + public List> getGappyParts() { + return Arrays.asList(Optional.empty(), Optional.of("eggs")); + } + + public Optional> getOptionalParts() { + return Optional.empty(); + } + + public Optional>> getOptionalGappyParts() { + return Optional.of(Arrays.asList()); + } + + public CompletableFuture getNameFuture() { + return CompletableFuture.completedFuture("green"); + } + + public CompletableFuture isDeletedFuture() { + return CompletableFuture.completedFuture(false); + } + + public CompletableFuture> getAliveFuture() { + return CompletableFuture.completedFuture(Optional.of(false)); + } + + public CompletableFuture> getPartsFuture() { + return CompletableFuture.completedFuture(Arrays.asList()); + } + + public CompletableFuture>> getGappyPartsFuture() { + return CompletableFuture.completedFuture(Arrays.asList()); + } + + public CompletableFuture>> getOptionalPartsFuture() { + return CompletableFuture.completedFuture(Optional.of(Arrays.asList())); + } + + public CompletableFuture>>> getOptionalGappyPartsFuture() { + return CompletableFuture.completedFuture(Optional.empty()); + } + + @Query + public static SimpleType simpleType() { + return new SimpleType(); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/UnionType.java b/src/test/java/com/fleetpin/graphql/builder/type/UnionType.java new file mode 100644 index 0000000..c759dad --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/type/UnionType.java @@ -0,0 +1,33 @@ +package com.fleetpin.graphql.builder.type; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Query; +import com.fleetpin.graphql.builder.annotations.Union; +import java.util.List; + +@Entity +public class UnionType { + + private final Object type; + + public UnionType(Object type) { + this.type = type; + } + + @Union({ SimpleType.class, UnionType.class }) + public Object getType() { + return type; + } + + @Query + @Union({ SimpleType.class, UnionType.class }) + public static List union() { + return List.of(new SimpleType(), new UnionType(new SimpleType())); + } + + @Query + @Union({ SimpleType.class, UnionType.class }) + public static List unionFailure() { + return List.of(new UnionType(new UnionType(4d)), false); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java index 2f7c7ab..0a6cf40 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java @@ -1,38 +1,31 @@ -package com.fleetpin.graphql.builder.type.directive; - - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.util.List; - -import com.fleetpin.graphql.builder.SDLDirective; -import com.fleetpin.graphql.builder.annotations.Directive; - -import graphql.introspection.Introspection.DirectiveLocation; - -@Directive(Capture.Processor.class) -@Retention(RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) -public @interface Capture { - - String value(); - - static class Processor implements SDLDirective { - - - @Override - public List validLocations() { - return List.of(DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA); - } - - @Override - public CaptureType build(Capture annotation, Class location) { - return new CaptureType().setColor(annotation.value()); - } - - } - -} +package com.fleetpin.graphql.builder.type.directive; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.fleetpin.graphql.builder.SDLDirective; +import com.fleetpin.graphql.builder.annotations.Directive; +import graphql.introspection.Introspection.DirectiveLocation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.List; + +@Directive(Capture.Processor.class) +@Retention(RUNTIME) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface Capture { + String value(); + + static class Processor implements SDLDirective { + + @Override + public List validLocations() { + return List.of(DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA); + } + + @Override + public CaptureType build(Capture annotation, Class location) { + return new CaptureType().setColor(annotation.value()); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/CaptureType.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/CaptureType.java index 22a8f90..5083582 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/CaptureType.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/CaptureType.java @@ -1,18 +1,19 @@ -package com.fleetpin.graphql.builder.type.directive; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.SchemaOption; - -@Entity(SchemaOption.INPUT) -public class CaptureType { - private String color; - - public CaptureType setColor(String color) { - this.color = color; - return this; - } - - public String getColor() { - return color; - } -} \ No newline at end of file +package com.fleetpin.graphql.builder.type.directive; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.SchemaOption; + +@Entity(SchemaOption.INPUT) +public class CaptureType { + + private String color; + + public CaptureType setColor(String color) { + this.color = color; + return this; + } + + public String getColor() { + return color; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java index 01ad286..ea3e628 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java @@ -1,29 +1,26 @@ -package com.fleetpin.graphql.builder.type.directive; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Query; - -@Entity -public class Cat { - - - - public boolean isCalico() { - return true; - } - - public int getAge() { - return 3; - } - - public boolean getFur() { - return true; - } - - @Query - @Capture("meow") - public static Cat getCat() { - return new Cat(); - } -} - +package com.fleetpin.graphql.builder.type.directive; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Query; + +@Entity +public class Cat { + + public boolean isCalico() { + return true; + } + + public int getAge() { + return 3; + } + + public boolean getFur() { + return true; + } + + @Query + @Capture("meow") + public static Cat getCat() { + return new Cat(); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java index 4b2cff6..d2ed983 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java @@ -1,8 +1,6 @@ -package com.fleetpin.graphql.builder.type.directive; - -import com.fleetpin.graphql.builder.SchemaConfiguration; - -@Capture("top") -public class CatSchema implements SchemaConfiguration { - -} +package com.fleetpin.graphql.builder.type.directive; + +import com.fleetpin.graphql.builder.SchemaConfiguration; + +@Capture("top") +public class CatSchema implements SchemaConfiguration {} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java index 3ddb593..c49065e 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java @@ -1,20 +1,34 @@ -package com.fleetpin.graphql.builder.type.inheritance; - -import java.util.Arrays; -import java.util.List; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Query; - -@Entity -public abstract class Animal{ - - public String getName() { - return "name"; - } - - @Query - public static List animals() { - return Arrays.asList(new Cat(), new Dog()); - } -} \ No newline at end of file +package com.fleetpin.graphql.builder.type.inheritance; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Mutation; +import com.fleetpin.graphql.builder.annotations.OneOf; +import com.fleetpin.graphql.builder.annotations.Query; +import com.fleetpin.graphql.builder.annotations.SchemaOption; +import java.util.Arrays; +import java.util.List; + +@Entity(SchemaOption.BOTH) +@OneOf(value = { @OneOf.Type(name = "cat", type = Cat.class), @OneOf.Type(name = "dog", type = Dog.class) }) +public abstract class Animal { + + private String name = "name"; + + public String getName() { + return name; + } + + @Query + public static List animals() { + return Arrays.asList(new Cat(), new Dog()); + } + + public void setName(String name) { + this.name = name; + } + + @Mutation + public static List myAnimals(List animals) { + return animals; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java index d235f23..07f52c9 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java @@ -1,26 +1,55 @@ -package com.fleetpin.graphql.builder.type.inheritance; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Mutation; - -@Entity -public class Cat extends Animal{ - - public boolean isCalico() { - return true; - } - - public int getAge() { - return 3; - } - - public boolean getFur() { - return true; - } - - @Mutation - public static Cat getCat() { - return null; - } -} - +package com.fleetpin.graphql.builder.type.inheritance; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Mutation; +import com.fleetpin.graphql.builder.annotations.SchemaOption; + +@Entity(SchemaOption.BOTH) +public class Cat extends Animal { + + private boolean calico; + private int age; + private boolean fur; + + public Cat() { + calico = true; + age = 3; + fur = true; + } + + public Cat(boolean calico, int age, boolean fur) { + super(); + this.calico = calico; + this.age = age; + this.fur = fur; + } + + public boolean isCalico() { + return calico; + } + + public void setCalico(boolean calico) { + this.calico = calico; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public boolean isFur() { + return fur; + } + + public void setFur(boolean fur) { + this.fur = fur; + } + + @Mutation + public static Cat getCat() { + return null; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Dog.java b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Dog.java index 4302816..86793d4 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Dog.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Dog.java @@ -1,20 +1,33 @@ -package com.fleetpin.graphql.builder.type.inheritance; - -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Mutation; - -@Entity -public class Dog extends Animal{ - - public int getAge() { - return 6; - } - public String getFur() { - return "shaggy"; - } - - @Mutation - public static Dog getDog() { - return null; - } -} +package com.fleetpin.graphql.builder.type.inheritance; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Mutation; +import com.fleetpin.graphql.builder.annotations.SchemaOption; + +@Entity(SchemaOption.BOTH) +public class Dog extends Animal { + + private int age = 6; + private String fur = "shaggy"; + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getFur() { + return fur; + } + + public void setFur(String fur) { + this.fur = fur; + } + + @Mutation + public static Dog getDog() { + return null; + } +} From c5e47b69bd3b4f166622af9706b60f55d69e9e5d Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Mon, 6 Mar 2023 09:21:38 +1300 Subject: [PATCH 002/112] cleanup and license --- pom.xml | 33 +++++ src/license/license.txt | 9 ++ .../fleetpin/graphql/builder/Authorizer.java | 11 ++ .../graphql/builder/AuthorizerSchema.java | 14 +- .../graphql/builder/DirectiveCaller.java | 11 ++ .../graphql/builder/DirectiveOperation.java | 11 ++ .../graphql/builder/DirectivesSchema.java | 36 ++--- .../graphql/builder/DurationCoercing.java | 46 ------ .../graphql/builder/EntityHolder.java | 23 +++ .../graphql/builder/EntityProcessor.java | 111 ++++---------- .../fleetpin/graphql/builder/EntityUtil.java | 86 +++++++++++ .../fleetpin/graphql/builder/EnumEntity.java | 23 +++ .../fleetpin/graphql/builder/ErrorType.java | 30 ++++ .../graphql/builder/FilteredPublisher.java | 104 ++++++++++++++ .../graphql/builder/GraphqlLongCoercing.java | 75 ---------- .../graphql/builder/InputBuilder.java | 96 +++++-------- .../graphql/builder/InstantCoercing.java | 49 ------- .../graphql/builder/LocalDateCoercing.java | 46 ------ .../builder/LocalDateTimeCoercing.java | 46 ------ .../graphql/builder/MonthDayCoercing.java | 34 ----- .../graphql/builder/ObjectEntity.java | 23 +++ .../graphql/builder/ReferenceEntity.java | 58 ++++++++ .../graphql/builder/RestrictType.java | 11 ++ .../graphql/builder/RestrictTypeFactory.java | 11 ++ .../graphql/builder/SDLDirective.java | 11 ++ .../graphql/builder/SDLProcessor.java | 92 ++++++------ .../graphql/builder/ScalarEntity.java | 23 +++ .../graphql/builder/SchemaBuilder.java | 32 +++-- .../graphql/builder/SchemaConfiguration.java | 23 +++ .../fleetpin/graphql/builder/TypeBuilder.java | 88 ++++++------ .../fleetpin/graphql/builder/TypeMeta.java | 11 ++ .../fleetpin/graphql/builder/UnionType.java | 23 +++ .../graphql/builder/YearMonthCoercing.java | 34 ----- .../graphql/builder/ZoneIdCoercing.java | 46 ------ .../graphql/builder/annotations/Context.java | 11 ++ .../builder/annotations/Directive.java | 11 ++ .../graphql/builder/annotations/Entity.java | 11 ++ .../annotations/GraphQLDeprecated.java | 11 ++ .../annotations/GraphQLDescription.java | 11 ++ .../builder/annotations/GraphQLIgnore.java | 11 ++ .../graphql/builder/annotations/Id.java | 11 ++ .../builder/annotations/InputIgnore.java | 11 ++ .../graphql/builder/annotations/Mutation.java | 11 ++ .../graphql/builder/annotations/OneOf.java | 11 ++ .../graphql/builder/annotations/Query.java | 11 ++ .../graphql/builder/annotations/Restrict.java | 11 ++ .../builder/annotations/Restricts.java | 23 +++ .../graphql/builder/annotations/Scalar.java | 11 ++ .../builder/annotations/SchemaOption.java | 11 ++ .../builder/annotations/Subscription.java | 11 ++ .../graphql/builder/annotations/Union.java | 11 ++ .../builder/mapper/InputTypeBuilder.java | 23 +++ .../builder/mapper/ObjectFieldBuilder.java | 40 +++++- .../graphql/builder/mapper/OneOfBuilder.java | 27 +++- .../builder/mapper/RecordFieldBuilder.java | 65 +++++---- .../graphql/builder/AuthorizerTest.java | 69 +++++++++ .../graphql/builder/DirectiveTest.java | 11 ++ .../fleetpin/graphql/builder/MetaTest.java | 11 ++ .../graphql/builder/ParameterParsingTest.java | 11 ++ .../builder/ParameterTypeParsingTest.java | 11 ++ .../graphql/builder/PublishRestrictions.java | 11 ++ .../fleetpin/graphql/builder/ScalarTest.java | 110 ++++++++++++++ .../builder/TypeGenericInputParsingTest.java | 11 ++ .../builder/TypeGenericParsingTest.java | 11 ++ .../builder/TypeInheritanceParsingTest.java | 135 +++++++++++++++++- .../graphql/builder/TypeParsingTest.java | 11 ++ .../fleetpin/graphql/builder/UnionTest.java | 11 ++ .../graphql/builder/authorizer/Cat.java | 36 +++++ .../builder/authorizer/CatAuthorizer.java | 21 +++ .../graphql/builder/generics/Animal.java | 11 ++ .../graphql/builder/generics/Cat.java | 11 ++ .../graphql/builder/generics/CatFamily.java | 11 ++ .../builder/generics/CatFamilyFur.java | 11 ++ .../graphql/builder/generics/CatFur.java | 11 ++ .../graphql/builder/generics/Dog.java | 11 ++ .../graphql/builder/generics/DogFur.java | 11 ++ .../graphql/builder/generics/Fur.java | 11 ++ .../graphql/builder/inputgenerics/Animal.java | 11 ++ .../builder/inputgenerics/AnimalInput.java | 11 ++ .../inputgenerics/AnimalOuterWrapper.java | 11 ++ .../builder/inputgenerics/AnimalWrapper.java | 11 ++ .../graphql/builder/inputgenerics/Cat.java | 11 ++ .../builder/inputgenerics/CatAnimalInput.java | 11 ++ .../graphql/builder/parameter/Parameter.java | 11 ++ .../builder/parameter/TypeInputParameter.java | 11 ++ .../builder/publishRestrictions/Test.java | 11 ++ .../restrictions/EntityRestrictions.java | 11 ++ .../restrictions/RestrictionTypesTest.java | 11 ++ .../parameter/RestrictedEntity.java | 11 ++ .../graphql/builder/scalar/Capture.java | 40 ++++++ .../graphql/builder/scalar/CaptureType.java | 18 +++ .../fleetpin/graphql/builder/scalar/Cat.java | 39 +++++ .../fleetpin/graphql/builder/scalar/Fur.java | 61 ++++++++ .../graphql/builder/scalar/Shape.java | 60 ++++++++ .../graphql/builder/type/Circular.java | 11 ++ .../builder/type/DeprecatedObject.java | 11 ++ .../builder/type/DescriptionObject.java | 11 ++ .../graphql/builder/type/SimpleType.java | 11 ++ .../graphql/builder/type/UnionType.java | 11 ++ .../builder/type/directive/Capture.java | 11 ++ .../builder/type/directive/CaptureType.java | 11 ++ .../graphql/builder/type/directive/Cat.java | 11 ++ .../builder/type/directive/CatSchema.java | 11 ++ .../builder/type/inheritance/Animal.java | 11 ++ .../graphql/builder/type/inheritance/Cat.java | 26 +++- .../graphql/builder/type/inheritance/Dog.java | 11 ++ 106 files changed, 2096 insertions(+), 693 deletions(-) create mode 100644 src/license/license.txt delete mode 100644 src/main/java/com/fleetpin/graphql/builder/DurationCoercing.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/ErrorType.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/FilteredPublisher.java delete mode 100644 src/main/java/com/fleetpin/graphql/builder/GraphqlLongCoercing.java delete mode 100644 src/main/java/com/fleetpin/graphql/builder/InstantCoercing.java delete mode 100644 src/main/java/com/fleetpin/graphql/builder/LocalDateCoercing.java delete mode 100644 src/main/java/com/fleetpin/graphql/builder/LocalDateTimeCoercing.java delete mode 100644 src/main/java/com/fleetpin/graphql/builder/MonthDayCoercing.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/ReferenceEntity.java delete mode 100644 src/main/java/com/fleetpin/graphql/builder/YearMonthCoercing.java delete mode 100644 src/main/java/com/fleetpin/graphql/builder/ZoneIdCoercing.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/AuthorizerTest.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/ScalarTest.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/authorizer/Cat.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/authorizer/CatAuthorizer.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/scalar/CaptureType.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/scalar/Cat.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/scalar/Fur.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/scalar/Shape.java diff --git a/pom.xml b/pom.xml index 2452094..821d8ea 100644 --- a/pom.xml +++ b/pom.xml @@ -1,3 +1,16 @@ + 4.0.0 com.fleetpin @@ -8,6 +21,12 @@ Builds a graphql schema from a model using reflection https://github.com/fleetpin/graphql-builder + + test + + 2017 + + 5.6.0 UTF-8 @@ -107,6 +126,19 @@ + + com.mycila + license-maven-plugin + 4.1 + + + +
src/license/license.txt
+
+
+
+
+ @@ -149,6 +181,7 @@ io.reactivex.rxjava3 rxjava 3.1.6 + test diff --git a/src/license/license.txt b/src/license/license.txt new file mode 100644 index 0000000..3208ac9 --- /dev/null +++ b/src/license/license.txt @@ -0,0 +1,9 @@ +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 limitations under +the License. \ No newline at end of file diff --git a/src/main/java/com/fleetpin/graphql/builder/Authorizer.java b/src/main/java/com/fleetpin/graphql/builder/Authorizer.java index 9b85eb5..98166a6 100644 --- a/src/main/java/com/fleetpin/graphql/builder/Authorizer.java +++ b/src/main/java/com/fleetpin/graphql/builder/Authorizer.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java index 225949c..72a7d61 100644 --- a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java @@ -9,9 +9,21 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; +import graphql.GraphqlErrorException; import graphql.schema.DataFetcher; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -158,7 +170,7 @@ public DataFetcher wrap(DataFetcher fetcher, Method method) { if ((Boolean) allow) { return fetcher.get(env); } else { - throw new RuntimeException("Invalid access"); + throw GraphqlErrorException.newErrorException().message("unauthorized").errorClassification(ErrorType.UNAUTHORIZED).build(); } } else { //only other type that passes checks above diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java index 4881964..a96e8b0 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java index 0493f0b..26f2cca 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java index 177585c..f481d19 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java @@ -1,11 +1,14 @@ -package com.fleetpin.graphql.builder; - -import com.fleetpin.graphql.builder.annotations.Directive; -import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; -import graphql.schema.GraphQLAppliedDirective; -import graphql.schema.GraphQLDirective; -import io.reactivex.rxjava3.core.Flowable; +/* + * 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 limitations under + * the License. + */ /* * 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 @@ -18,6 +21,13 @@ * the License. */ +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.Directive; +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import graphql.schema.GraphQLAppliedDirective; +import graphql.schema.GraphQLDirective; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; @@ -33,11 +43,9 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Flow; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.reactivestreams.FlowAdapters; import org.reactivestreams.Publisher; class DirectivesSchema { @@ -164,13 +172,7 @@ private CompletableFuture applyRestrict(RestrictType restrict, Objec if (response instanceof List) { return restrict.filter((List) response); } else if (response instanceof Publisher) { - return CompletableFuture.completedFuture( - Flowable - .fromPublisher((Publisher) response) - .flatMap(entry -> { - return Flowable.fromCompletionStage(restrict.allow(entry)).filter(t -> t == Boolean.TRUE).map(t -> entry); - }) - ); + return CompletableFuture.completedFuture(new FilteredPublisher((Publisher) response, restrict)); } else if (response instanceof Optional) { var optional = (Optional) response; if (optional.isEmpty()) { diff --git a/src/main/java/com/fleetpin/graphql/builder/DurationCoercing.java b/src/main/java/com/fleetpin/graphql/builder/DurationCoercing.java deleted file mode 100644 index 1e2c62e..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/DurationCoercing.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 limitations under - * the License. - */ - -package com.fleetpin.graphql.builder; - -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; -import java.time.Duration; - -public class DurationCoercing implements Coercing { - - @Override - public Duration serialize(Object dataFetcherResult) throws CoercingSerializeException { - return convertImpl(dataFetcherResult); - } - - @Override - public Duration parseValue(Object input) throws CoercingParseValueException { - return convertImpl(input); - } - - @Override - public Duration parseLiteral(Object input) throws CoercingParseLiteralException { - return convertImpl(input); - } - - private Duration convertImpl(Object input) { - if (input instanceof Duration) { - return (Duration) input; - } else if (input instanceof String) { - return Duration.parse((String) input); - } - return null; - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java index c8692d9..32742c4 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.TypeMeta.Flag; diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java index a012aa0..b92ecf3 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Scalar; @@ -9,16 +32,9 @@ import graphql.schema.GraphQLInputType; import graphql.schema.GraphQLOutputType; import graphql.schema.GraphQLType; -import graphql.schema.GraphQLUnionType; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Type; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.MonthDay; -import java.time.YearMonth; -import java.time.ZoneId; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -44,9 +60,6 @@ private void addDefaults() { put(Boolean.class, new ScalarEntity(Scalars.GraphQLBoolean)); put(Boolean.TYPE, new ScalarEntity(Scalars.GraphQLBoolean)); - put(Float.class, new ScalarEntity(Scalars.GraphQLFloat)); - put(Float.TYPE, new ScalarEntity(Scalars.GraphQLFloat)); - put(Double.class, new ScalarEntity(Scalars.GraphQLFloat)); put(Double.TYPE, new ScalarEntity(Scalars.GraphQLFloat)); @@ -54,16 +67,6 @@ private void addDefaults() { put(Integer.TYPE, new ScalarEntity(Scalars.GraphQLInt)); put(String.class, new ScalarEntity(Scalars.GraphQLString)); - - put(Long.class, new ScalarEntity(SchemaBuilder.LONG_SCALAR)); - put(Long.TYPE, new ScalarEntity(SchemaBuilder.LONG_SCALAR)); - - put(Instant.class, new ScalarEntity(SchemaBuilder.INSTANT_SCALAR)); - put(LocalDate.class, new ScalarEntity(SchemaBuilder.DATE_SCALAR)); - put(ZoneId.class, new ScalarEntity(SchemaBuilder.ZONE_ID_SCALAR)); - put(Duration.class, new ScalarEntity(SchemaBuilder.DURATION_SCALAR)); - put(MonthDay.class, new ScalarEntity(SchemaBuilder.MONTH_DAY_SCALAR)); - put(YearMonth.class, new ScalarEntity(SchemaBuilder.YEAR_MONTH_SCALAR)); } private void put(Class type, ScalarEntity entity) { @@ -85,6 +88,9 @@ EntityHolder getEntity(TypeMeta meta) { name, __ -> { Class type = meta.getType(); + if (type.equals(Object.class)) { + return new ReferenceEntity("Object"); + } Type genericType = meta.getGenericType(); if (genericType == null) { genericType = type; @@ -157,69 +163,4 @@ public InputTypeBuilder getResolver(Class type) { var meta = new TypeMeta(null, type, type); return getEntity(meta).getResolver(meta); } - // - // - // private static Class extraOptionalType(Type type) { - // if (type instanceof Class) { - // return (Class) type; - // } else if (type instanceof ParameterizedType) { - // return extraOptionalType(((ParameterizedType) type).getActualTypeArguments()[0]); - // } - // throw new RuntimeException("extraction failure for " + type.getClass()); - // } - - // - // private String getNameInput(TypeMeta meta) { - // var type = meta.getType(); - // String name = null; - // if (type.isEnum()) { - // name = type.getSimpleName(); - // }else if (type.isAnnotationPresent(Scalar.class)) { - // name = type.getSimpleName(); - // }else if (type.isAnnotationPresent(OneOf.class)) { - // name = type.getSimpleName(); - // }else if (type.isAnnotationPresent(Entity.class)) { - // if (type.getAnnotation(Entity.class).value() == SchemaOption.BOTH) { - // name = type.getSimpleName() + "Input"; - // } else { - // name = type.getSimpleName(); - // } - // }else { - // name = type.getSimpleName() + "Input"; - // } - // - // var genericType = meta.getGenericType(); - // - // if (genericType instanceof ParameterizedType) { - // var parameterizedTypes = ((ParameterizedType) genericType).getActualTypeArguments(); - // - // for (var t : parameterizedTypes) { - // if (t instanceof Class) { - // String extra = ((Class) t).getSimpleName(); - // name += "_" + extra; - // } - // } - // } - // - // return name; - // } - - // public String processInput(TypeMeta meta) { - // String name = getNameInput(meta); - // if (name != null && !this.additionalTypes.containsKey(name) && !inputTypeBuilders.containsKey(meta.getGenericType())) { - // this.additionalTypes.put(name, null); // so we don't go around in circles if depend on self - // addType(meta, true); - // } - // return name; - // } - // - // public ObjectBuilder getResolver(TypeMeta meta) { - // processInput(meta); - // return InputTypeBuilder.process(inputTypeBuilders, meta.getTypes().iterator()); - // } - // - // public EntitiesHolder getEntities() { - // return entities; - // } - } diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java index 6921174..e4a62cd 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java @@ -1,8 +1,36 @@ +/* + * 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 limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; +import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; +import com.fleetpin.graphql.builder.annotations.InputIgnore; +import com.fleetpin.graphql.builder.mapper.ObjectFieldBuilder.FieldMapper; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.TypeVariable; +import java.util.Optional; public class EntityUtil { @@ -63,4 +91,62 @@ static String getName(TypeMeta meta) { return name; } + + public static Optional getter(Method method) { + if (method.isSynthetic()) { + return Optional.empty(); + } + if (method.getDeclaringClass().equals(Object.class)) { + return Optional.empty(); + } + if (method.isAnnotationPresent(GraphQLIgnore.class)) { + return Optional.empty(); + } + //will also be on implementing class + if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { + return Optional.empty(); + } + if (Modifier.isStatic(method.getModifiers())) { + return Optional.empty(); + } else { + if (method.getName().matches("(get|is)[A-Z].*")) { + String name; + if (method.getName().startsWith("get")) { + name = method.getName().substring("get".length(), "get".length() + 1).toLowerCase() + method.getName().substring("get".length() + 1); + } else { + name = method.getName().substring("is".length(), "is".length() + 1).toLowerCase() + method.getName().substring("is".length() + 1); + } + return Optional.of(name); + } + } + return Optional.empty(); + } + + public static Optional setter(Method method) { + if (method.isSynthetic()) { + return Optional.empty(); + } + if (method.getDeclaringClass().equals(Object.class)) { + return Optional.empty(); + } + if (method.isAnnotationPresent(GraphQLIgnore.class)) { + return Optional.empty(); + } + //will also be on implementing class + if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { + return Optional.empty(); + } + if (Modifier.isStatic(method.getModifiers())) { + return Optional.empty(); + } else { + //getter type + if (method.getName().matches("set[A-Z].*")) { + if (method.getParameterCount() == 1 && !method.isAnnotationPresent(InputIgnore.class)) { + String name = method.getName().substring("set".length(), "set".length() + 1).toLowerCase() + method.getName().substring("set".length() + 1); + return Optional.of(name); + } + } + } + return Optional.empty(); + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java index e95c669..7be84ce 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.GraphQLDescription; diff --git a/src/main/java/com/fleetpin/graphql/builder/ErrorType.java b/src/main/java/com/fleetpin/graphql/builder/ErrorType.java new file mode 100644 index 0000000..7e67454 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/ErrorType.java @@ -0,0 +1,30 @@ +/* + * 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 limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + +package com.fleetpin.graphql.builder; + +import graphql.ErrorClassification; + +enum ErrorType implements ErrorClassification { + UNAUTHORIZED, +} diff --git a/src/main/java/com/fleetpin/graphql/builder/FilteredPublisher.java b/src/main/java/com/fleetpin/graphql/builder/FilteredPublisher.java new file mode 100644 index 0000000..ead590a --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/FilteredPublisher.java @@ -0,0 +1,104 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder; + +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +/* + * 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 limitations under + * the License. + */ + +class FilteredPublisher implements Publisher { + + private final RestrictType restrict; + private final Publisher publisher; + + public FilteredPublisher(Publisher publisher, RestrictType restrict) { + this.publisher = publisher; + this.restrict = restrict; + } + + @Override + public void subscribe(Subscriber subscriber) { + publisher.subscribe( + new Subscriber() { + private CompletableFuture current = CompletableFuture.completedFuture(null); + private Subscription s; + + @Override + public void onSubscribe(Subscription s) { + this.s = s; + subscriber.onSubscribe(s); + } + + @Override + public void onNext(T t) { + synchronized (this) { + current = current.thenCompose(__ -> restrict.allow(t)).whenComplete(process(subscriber, t)); + } + } + + private BiConsumer process(Subscriber subscriber, T t) { + return (allow, error) -> { + if (error != null) { + subscriber.onError(error); + return; + } + if (allow) { + subscriber.onNext(t); + } else { + s.request(1); + } + }; + } + + @Override + public void onError(Throwable t) { + synchronized (this) { + current = + current.whenComplete((__, error) -> { + if (error != null) { + subscriber.onError(error); + } + subscriber.onError(t); + }); + } + } + + @Override + public void onComplete() { + synchronized (this) { + current = + current.whenComplete((__, error) -> { + if (error != null) { + subscriber.onError(error); + } + subscriber.onComplete(); + }); + } + } + } + ); + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/GraphqlLongCoercing.java b/src/main/java/com/fleetpin/graphql/builder/GraphqlLongCoercing.java deleted file mode 100644 index 64942d3..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/GraphqlLongCoercing.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.fleetpin.graphql.builder; - -import static graphql.Assert.assertNotNull; - -import graphql.Internal; -import graphql.language.IntValue; -import graphql.language.Value; -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; -import java.math.BigDecimal; -import java.math.BigInteger; - -public class GraphqlLongCoercing implements Coercing { - - private Long convertImpl(Object input) { - if (input instanceof Long) { - return (Long) input; - } else if (isNumberIsh(input)) { - BigDecimal value; - try { - value = new BigDecimal(input.toString()); - } catch (NumberFormatException e) { - return null; - } - try { - return value.longValueExact(); - } catch (ArithmeticException e) { - return null; - } - } else { - return null; - } - } - - @Override - public Long serialize(Object input) { - Long result = convertImpl(input); - if (result == null) { - throw new CoercingSerializeException("Expected type 'Long' but was '" + typeName(input) + "'."); - } - return result; - } - - @Override - public Long parseValue(Object input) { - Long result = convertImpl(input); - if (result == null) { - throw new CoercingParseValueException("Expected type 'Int' but was '" + typeName(input) + "'."); - } - return result; - } - - @Override - public Long parseLiteral(Object input) { - if (!(input instanceof IntValue)) { - throw new CoercingParseLiteralException("Expected AST type 'IntValue' but was '" + typeName(input) + "'."); - } - BigInteger value = ((IntValue) input).getValue(); - return value.longValue(); - } - - private boolean isNumberIsh(Object input) { - return input instanceof Number || input instanceof String; - } - - private String typeName(Object input) { - if (input == null) { - return "null"; - } - - return input.getClass().getSimpleName(); - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java index c9e8047..ac947cf 100644 --- a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java @@ -1,3 +1,15 @@ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Entity; @@ -108,38 +120,16 @@ public ObjectType(EntityProcessor entityProcessor, TypeMeta meta) { void processFields(Builder graphInputType) { for (Method method : meta.getType().getMethods()) { try { - if (method.isSynthetic()) { - continue; - } - if (method.getDeclaringClass().equals(Object.class)) { - continue; - } - if (method.isAnnotationPresent(GraphQLIgnore.class)) { - continue; - } - //will also be on implementing class - if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { - continue; - } - if (Modifier.isStatic(method.getModifiers())) { - continue; - } else { - //getter type - if (method.getName().matches("set[A-Z].*")) { - if (method.getParameterCount() == 1 && !method.isAnnotationPresent(InputIgnore.class)) { - String name = - method.getName().substring("set".length(), "set".length() + 1).toLowerCase() + - method.getName().substring("set".length() + 1); - GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); - field.name(name); - entityProcessor.addSchemaDirective(method, meta.getType(), field::withAppliedDirective); - TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); - var entity = entityProcessor.getEntity(innerMeta); - var inputType = entity.getInputType(innerMeta, method.getParameterAnnotations()[0]); - field.type(inputType); - graphInputType.field(field); - } - } + var name = EntityUtil.setter(method); + if (name.isPresent()) { + GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); + field.name(name.get()); + entityProcessor.addSchemaDirective(method, meta.getType(), field::withAppliedDirective); + TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); + var entity = entityProcessor.getEntity(innerMeta); + var inputType = entity.getInputType(innerMeta, method.getParameterAnnotations()[0]); + field.type(inputType); + graphInputType.field(field); } } catch (RuntimeException e) { throw new RuntimeException("Failed to process method " + method, e); @@ -153,32 +143,10 @@ public InputTypeBuilder resolve() { for (Method method : meta.getType().getMethods()) { try { - if (method.isSynthetic()) { - continue; - } - if (method.getDeclaringClass().equals(Object.class)) { - continue; - } - if (method.isAnnotationPresent(GraphQLIgnore.class)) { - continue; - } - //will also be on implementing class - if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { - continue; - } - if (Modifier.isStatic(method.getModifiers())) { - continue; - } else { - //getter type - if (method.getName().matches("set[A-Z].*")) { - if (method.getParameterCount() == 1 && !method.isAnnotationPresent(InputIgnore.class)) { - String name = - method.getName().substring("set".length(), "set".length() + 1).toLowerCase() + - method.getName().substring("set".length() + 1); - TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); - fieldMappers.add(FieldMapper.build(entityProcessor, innerMeta, name, method)); - } - } + var name = EntityUtil.setter(method); + if (name.isPresent()) { + TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); + fieldMappers.add(FieldMapper.build(entityProcessor, innerMeta, name.get(), method)); } } catch (RuntimeException e) { throw new RuntimeException("Failed to process method " + method, e); @@ -229,10 +197,10 @@ void processFields(Builder graphInputType) { @Override protected InputTypeBuilder resolve() { - var fieldMappers = new ArrayList(); + try { + var fieldMappers = new ArrayList(); - for (var field : this.meta.getType().getDeclaredFields()) { - try { + for (var field : this.meta.getType().getDeclaredFields()) { if (field.isSynthetic()) { continue; } @@ -249,11 +217,11 @@ protected InputTypeBuilder resolve() { fieldMappers.add(new RecordMapper(field.getName(), field.getType(), resolver)); } } - } catch (RuntimeException e) { - throw new RuntimeException("Failed to process method " + field, e); } + return new RecordFieldBuilder(meta.getType(), fieldMappers); + } catch (RuntimeException | ReflectiveOperationException e) { + throw new RuntimeException("Failed to process " + this.meta.getType(), e); } - return new RecordFieldBuilder(meta.getType(), fieldMappers); } } } diff --git a/src/main/java/com/fleetpin/graphql/builder/InstantCoercing.java b/src/main/java/com/fleetpin/graphql/builder/InstantCoercing.java deleted file mode 100644 index cf31dfb..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/InstantCoercing.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 limitations under - * the License. - */ - -package com.fleetpin.graphql.builder; - -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; -import java.time.Instant; - -public class InstantCoercing implements Coercing { - - @Override - public Instant serialize(Object dataFetcherResult) throws CoercingSerializeException { - return convertImpl(dataFetcherResult); - } - - @Override - public Instant parseValue(Object input) throws CoercingParseValueException { - return convertImpl(input); - } - - @Override - public Instant parseLiteral(Object input) throws CoercingParseLiteralException { - return convertImpl(input); - } - - private Instant convertImpl(Object input) { - if (input instanceof Instant) { - return (Instant) input; - } else if (input instanceof String) { - return Instant.parse((String) input); - } - if (input instanceof Long) { - return Instant.ofEpochMilli((long) input); - } - return null; - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/LocalDateCoercing.java b/src/main/java/com/fleetpin/graphql/builder/LocalDateCoercing.java deleted file mode 100644 index eb5af8a..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/LocalDateCoercing.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 limitations under - * the License. - */ - -package com.fleetpin.graphql.builder; - -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; -import java.time.LocalDate; - -public class LocalDateCoercing implements Coercing { - - @Override - public LocalDate serialize(Object dataFetcherResult) throws CoercingSerializeException { - return convertImpl(dataFetcherResult); - } - - @Override - public LocalDate parseValue(Object input) throws CoercingParseValueException { - return convertImpl(input); - } - - @Override - public LocalDate parseLiteral(Object input) throws CoercingParseLiteralException { - return convertImpl(input); - } - - private LocalDate convertImpl(Object input) { - if (input instanceof LocalDate) { - return (LocalDate) input; - } else if (input instanceof String) { - return LocalDate.parse((String) input); - } - return null; - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/LocalDateTimeCoercing.java b/src/main/java/com/fleetpin/graphql/builder/LocalDateTimeCoercing.java deleted file mode 100644 index 51da76c..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/LocalDateTimeCoercing.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 limitations under - * the License. - */ - -package com.fleetpin.graphql.builder; - -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; -import java.time.LocalDateTime; - -public class LocalDateTimeCoercing implements Coercing { - - @Override - public LocalDateTime serialize(Object dataFetcherResult) throws CoercingSerializeException { - return convertImpl(dataFetcherResult); - } - - @Override - public LocalDateTime parseValue(Object input) throws CoercingParseValueException { - return convertImpl(input); - } - - @Override - public LocalDateTime parseLiteral(Object input) throws CoercingParseLiteralException { - return convertImpl(input); - } - - private LocalDateTime convertImpl(Object input) { - if (input instanceof LocalDateTime) { - return (LocalDateTime) input; - } else if (input instanceof String) { - return LocalDateTime.parse((String) input); - } - return null; - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/MonthDayCoercing.java b/src/main/java/com/fleetpin/graphql/builder/MonthDayCoercing.java deleted file mode 100644 index 18bb5de..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/MonthDayCoercing.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.fleetpin.graphql.builder; - -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; -import java.time.MonthDay; - -public class MonthDayCoercing implements Coercing { - - @Override - public MonthDay serialize(Object dataFetcherResult) throws CoercingSerializeException { - return convertImpl(dataFetcherResult); - } - - @Override - public MonthDay parseValue(Object input) throws CoercingParseValueException { - return convertImpl(input); - } - - @Override - public MonthDay parseLiteral(Object input) throws CoercingParseLiteralException { - return convertImpl(input); - } - - private MonthDay convertImpl(Object input) { - if (input instanceof MonthDay) { - return (MonthDay) input; - } else if (input instanceof String) { - return MonthDay.parse((String) input); - } - return null; - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java b/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java index 2598817..90987f4 100644 --- a/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.OneOf; diff --git a/src/main/java/com/fleetpin/graphql/builder/ReferenceEntity.java b/src/main/java/com/fleetpin/graphql/builder/ReferenceEntity.java new file mode 100644 index 0000000..a5b6061 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/ReferenceEntity.java @@ -0,0 +1,58 @@ +/* + * 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 limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; +import graphql.schema.GraphQLNamedInputType; +import graphql.schema.GraphQLNamedOutputType; +import graphql.schema.GraphQLTypeReference; + +public class ReferenceEntity extends EntityHolder { + + private final GraphQLTypeReference reference; + + ReferenceEntity(String typeName) { + this.reference = GraphQLTypeReference.typeRef(typeName); + } + + @Override + protected GraphQLNamedOutputType buildType() { + return reference; + } + + @Override + protected GraphQLNamedInputType buildInput() { + return reference; + } + + @Override + protected String buildInputName() { + return reference.getName(); + } + + @Override + protected InputTypeBuilder buildResolver() { + return null; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/RestrictType.java b/src/main/java/com/fleetpin/graphql/builder/RestrictType.java index 3402cff..d6a0335 100644 --- a/src/main/java/com/fleetpin/graphql/builder/RestrictType.java +++ b/src/main/java/com/fleetpin/graphql/builder/RestrictType.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java b/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java index 422fa88..ccbe6e8 100644 --- a/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java +++ b/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java b/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java index 5c2eb30..41c8d8f 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java +++ b/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java b/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java index 821cac2..e4d1637 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; @@ -50,54 +73,31 @@ public static SDLProcessor build(EntityProcessor entityProcessor, SDLDirective> builders = new ArrayList<>(); for (Method method : arguments.getMethods()) { + if (method.getParameterCount() != 0) { + continue; + } try { - if (method.isSynthetic()) { - continue; - } - if (method.getDeclaringClass().equals(Object.class)) { - continue; - } - if (method.isAnnotationPresent(GraphQLIgnore.class)) { - continue; - } - //will also be on implementing class - if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { - continue; - } - if (Modifier.isStatic(method.getModifiers())) { - continue; - } else { - if (method.getName().matches("(get|is)[A-Z].*") && method.getParameterCount() == 0) { - String name; - if (method.getName().startsWith("get")) { - name = - method.getName().substring("get".length(), "get".length() + 1).toLowerCase() + - method.getName().substring("get".length() + 1); - } else { - name = - method.getName().substring("is".length(), "is".length() + 1).toLowerCase() + - method.getName().substring("is".length() + 1); + var name = EntityUtil.getter(method); + name.ifPresent(n -> { + GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); + argument.name(n); + TypeMeta innerMeta = new TypeMeta(null, method.getReturnType(), method.getGenericReturnType()); + var argumentType = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, method.getAnnotations()); + argument.type(argumentType); + builder.argument(argument); + builders.add(object -> { + try { + return GraphQLAppliedDirectiveArgument + .newArgument() + .name(n) + .type(argumentType) + .valueProgrammatic(method.invoke(object)) + .build(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); } - GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); - argument.name(name); - TypeMeta innerMeta = new TypeMeta(null, method.getReturnType(), method.getGenericReturnType()); - var argumentType = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, method.getAnnotations()); - argument.type(argumentType); - builder.argument(argument); - builders.add(object -> { - try { - return GraphQLAppliedDirectiveArgument - .newArgument() - .name(name) - .type(argumentType) - .valueProgrammatic(method.invoke(object)) - .build(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }); - } - } + }); + }); } catch (RuntimeException e) { throw new RuntimeException("Failed to process method " + method, e); } diff --git a/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java b/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java index 3545f17..ac7b17d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.GraphQLDescription; diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index c7f12f6..39100e8 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; @@ -24,7 +35,6 @@ import com.fleetpin.graphql.builder.annotations.SchemaOption; import com.fleetpin.graphql.builder.annotations.Subscription; import graphql.GraphQLContext; -import graphql.language.UnionTypeDefinition; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.FieldCoordinates; @@ -33,7 +43,6 @@ import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLObjectType.Builder; -import graphql.schema.GraphQLScalarType; import graphql.schema.GraphQLSchema; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; @@ -50,14 +59,6 @@ public class SchemaBuilder { - protected static final GraphQLScalarType INSTANT_SCALAR = GraphQLScalarType.newScalar().name("DateTime").coercing(new InstantCoercing()).build(); - protected static final GraphQLScalarType DATE_SCALAR = GraphQLScalarType.newScalar().name("Date").coercing(new LocalDateCoercing()).build(); - protected static final GraphQLScalarType DURATION_SCALAR = GraphQLScalarType.newScalar().name("Duration").coercing(new DurationCoercing()).build(); - protected static final GraphQLScalarType ZONE_ID_SCALAR = GraphQLScalarType.newScalar().name("Timezone").coercing(new ZoneIdCoercing()).build(); - protected static final GraphQLScalarType MONTH_DAY_SCALAR = GraphQLScalarType.newScalar().name("MonthDay").coercing(new MonthDayCoercing()).build(); - protected static final GraphQLScalarType YEAR_MONTH_SCALAR = GraphQLScalarType.newScalar().name("YearMonth").coercing(new YearMonthCoercing()).build(); - protected static final GraphQLScalarType LONG_SCALAR = GraphQLScalarType.newScalar().name("Long").coercing(new GraphqlLongCoercing()).build(); - private final DirectivesSchema diretives; private final AuthorizerSchema authorizer; @@ -152,7 +153,7 @@ private SchemaBuilder processTypes(Set> types) { return this; } - private GraphQLSchema build(Set> schemaConfiguration) { + private graphql.schema.GraphQLSchema.Builder build(Set> schemaConfiguration) { var builder = GraphQLSchema.newSchema().codeRegistry(codeRegistry.build()).additionalTypes(entityProcessor.getAdditionalTypes()); var query = graphQuery.build(); @@ -167,15 +168,12 @@ private GraphQLSchema build(Set> schemaConf builder.subscription(subscriptions); } - builder.build(); - diretives.getSchemaDirective().forEach(directive -> builder.additionalDirective(directive)); for (var schema : schemaConfiguration) { this.diretives.addSchemaDirective(schema, schema, builder::withSchemaAppliedDirective); } - - return builder.build(); + return builder; } private static boolean isContext(Class class1) { @@ -258,6 +256,10 @@ private Function buildResolver(String name, Typ } public static GraphQLSchema build(String... classPath) throws ReflectiveOperationException { + return builder(classPath).build(); + } + + public static GraphQLSchema.Builder builder(String... classPath) throws ReflectiveOperationException { Reflections reflections = new Reflections(classPath, Scanners.SubTypes, Scanners.MethodsAnnotated, Scanners.TypesAnnotated); Set> authorizers = reflections.getSubTypesOf(Authorizer.class); //want to make everything split by package diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java b/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java index 9f740b4..53d12bf 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; /** diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java index 9c4336a..8b81fe5 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Entity; @@ -219,59 +242,34 @@ protected void processFields(String typeName, Builder graphType, graphql.schema. var type = meta.getType(); for (Method method : type.getMethods()) { try { - if (method.isSynthetic()) { - continue; - } - if (method.getDeclaringClass().equals(Object.class)) { + var name = EntityUtil.getter(method); + if (name.isEmpty()) { continue; } - if (method.isAnnotationPresent(GraphQLIgnore.class)) { - continue; + + GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); + field.name(name.get()); + entityProcessor.addSchemaDirective(method, type, field::withAppliedDirective); + var deprecated = method.getAnnotation(GraphQLDeprecated.class); + if (deprecated != null) { + field.deprecate(deprecated.value()); } - //will also be on implementing class - if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { - continue; + var description = method.getAnnotation(GraphQLDescription.class); + if (description != null) { + field.description(description.value()); } - if (Modifier.isStatic(method.getModifiers())) { - continue; - } else { - //getter type - if (method.getName().matches("(get|is)[A-Z].*")) { - String name; - if (method.getName().startsWith("get")) { - name = - method.getName().substring("get".length(), "get".length() + 1).toLowerCase() + - method.getName().substring("get".length() + 1); - } else { - name = - method.getName().substring("is".length(), "is".length() + 1).toLowerCase() + method.getName().substring("is".length() + 1); - } - - GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); - field.name(name); - entityProcessor.addSchemaDirective(method, type, field::withAppliedDirective); - var deprecated = method.getAnnotation(GraphQLDeprecated.class); - if (deprecated != null) { - field.deprecate(deprecated.value()); - } - var description = method.getAnnotation(GraphQLDescription.class); - if (description != null) { - field.description(description.value()); - } - TypeMeta innerMeta = new TypeMeta(meta, method.getReturnType(), method.getGenericReturnType()); + TypeMeta innerMeta = new TypeMeta(meta, method.getReturnType(), method.getGenericReturnType()); - field.type(entityProcessor.getType(innerMeta, method.getAnnotations())); - graphType.field(field); - interfaceBuilder.field(field); + field.type(entityProcessor.getType(innerMeta, method.getAnnotations())); + graphType.field(field); + interfaceBuilder.field(field); - var directives = entityProcessor.getDirectives(); - var codeRegistry = entityProcessor.getCodeRegistry(); + var directives = entityProcessor.getDirectives(); + var codeRegistry = entityProcessor.getCodeRegistry(); - if (method.getParameterCount() > 0 || directives.target(method, innerMeta)) { - codeRegistry.dataFetcher(FieldCoordinates.coordinates(typeName, name), buildDirectiveWrapper(directives, method, innerMeta)); - } - } + if (method.getParameterCount() > 0 || directives.target(method, innerMeta)) { + codeRegistry.dataFetcher(FieldCoordinates.coordinates(typeName, name.get()), buildDirectiveWrapper(directives, method, innerMeta)); } } catch (RuntimeException e) { throw new RuntimeException("Failed to process method " + method, e); diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java index 6e410c6..710180c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/main/java/com/fleetpin/graphql/builder/UnionType.java b/src/main/java/com/fleetpin/graphql/builder/UnionType.java index 0892583..a8974fa 100644 --- a/src/main/java/com/fleetpin/graphql/builder/UnionType.java +++ b/src/main/java/com/fleetpin/graphql/builder/UnionType.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Union; diff --git a/src/main/java/com/fleetpin/graphql/builder/YearMonthCoercing.java b/src/main/java/com/fleetpin/graphql/builder/YearMonthCoercing.java deleted file mode 100644 index a7cfb0e..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/YearMonthCoercing.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.fleetpin.graphql.builder; - -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; -import java.time.YearMonth; - -public class YearMonthCoercing implements Coercing { - - @Override - public YearMonth serialize(Object dataFetcherResult) throws CoercingSerializeException { - return convertImpl(dataFetcherResult); - } - - @Override - public YearMonth parseValue(Object input) throws CoercingParseValueException { - return convertImpl(input); - } - - @Override - public YearMonth parseLiteral(Object input) throws CoercingParseLiteralException { - return convertImpl(input); - } - - private YearMonth convertImpl(Object input) { - if (input instanceof YearMonth) { - return (YearMonth) input; - } else if (input instanceof String) { - return YearMonth.parse((String) input); - } - return null; - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/ZoneIdCoercing.java b/src/main/java/com/fleetpin/graphql/builder/ZoneIdCoercing.java deleted file mode 100644 index 6a996ac..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/ZoneIdCoercing.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 limitations under - * the License. - */ - -package com.fleetpin.graphql.builder; - -import graphql.schema.Coercing; -import graphql.schema.CoercingParseLiteralException; -import graphql.schema.CoercingParseValueException; -import graphql.schema.CoercingSerializeException; -import java.time.ZoneId; - -public class ZoneIdCoercing implements Coercing { - - @Override - public ZoneId serialize(Object dataFetcherResult) throws CoercingSerializeException { - return convertImpl(dataFetcherResult); - } - - @Override - public ZoneId parseValue(Object input) throws CoercingParseValueException { - return convertImpl(input); - } - - @Override - public ZoneId parseLiteral(Object input) throws CoercingParseLiteralException { - return convertImpl(input); - } - - private ZoneId convertImpl(Object input) { - if (input instanceof ZoneId) { - return (ZoneId) input; - } else if (input instanceof String) { - return ZoneId.of((String) input); - } - return null; - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java index a73cc2a..6f10e36 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java index eaa0564..7228da8 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java index 84063ce..ce14c96 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java index 2881f50..895b481 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java index 7dce7ef..e7fdca4 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java index f5f6887..1824605 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java index 4a4eda4..2b6f1ed 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java b/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java index cb22079..92c0894 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java index b175b5a..0c7ce2e 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java b/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java index e0747eb..fa5338c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java index 48b7dab..e29dd25 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java index 49fb153..761ccb9 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java index 92cf170..0f53bea 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java index f371f0b..d8b3ac7 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java b/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java index 9df5277..cddd845 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java index 0cb81ad..e51d9fb 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java index 3ad29b6..f774c13 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.annotations; diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java index da5dd7a..63b5181 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder.mapper; import graphql.GraphQLContext; diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java index 169be24..eb068b7 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java @@ -1,8 +1,33 @@ +/* + * 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 limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder.mapper; import com.fleetpin.graphql.builder.EntityProcessor; import com.fleetpin.graphql.builder.TypeMeta; import graphql.GraphQLContext; +import graphql.com.google.common.base.Throwables; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Locale; @@ -18,10 +43,6 @@ public ObjectFieldBuilder(Class type, ArrayList mappers) { map = (obj, context, locale) -> { try { - if (type.isInstance(obj)) { - return obj; - } - var toReturn = constructor.newInstance(); Map map = (Map) obj; @@ -34,7 +55,8 @@ public ObjectFieldBuilder(Class type, ArrayList mappers) { } return toReturn; - } catch (ReflectiveOperationException e) { + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } }; @@ -64,8 +86,12 @@ public String getName() { return name; } - protected void map(Object inputType, Object argument, GraphQLContext graphQLContext, Locale locale) throws ReflectiveOperationException { - method.invoke(inputType, mapper.convert(argument, graphQLContext, locale)); + protected void map(Object inputType, Object argument, GraphQLContext graphQLContext, Locale locale) throws Throwable { + try { + method.invoke(inputType, mapper.convert(argument, graphQLContext, locale)); + } catch (InvocationTargetException e) { + throw e.getCause(); + } } public static FieldMapper build(EntityProcessor entityProcessor, TypeMeta inputType, String name, Method method) { diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java index febe3b4..928963d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder.mapper; import com.fleetpin.graphql.builder.EntityProcessor; @@ -20,10 +43,6 @@ public OneOfBuilder(EntityProcessor entityProcessor, Class type, OneOf oneOf) map = (obj, context, locale) -> { - if (type.isInstance(obj)) { - return obj; - } - Map map = (Map) obj; if (map.size() > 1) { diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java index 10c047b..99474d6 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java @@ -1,3 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +/* + * 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 limitations under + * the License. + */ + package com.fleetpin.graphql.builder.mapper; import graphql.GraphQLContext; @@ -9,38 +32,30 @@ public class RecordFieldBuilder implements InputTypeBuilder { private final InputTypeBuilder map; - public RecordFieldBuilder(Class type, ArrayList mappers) { + public RecordFieldBuilder(Class type, ArrayList mappers) throws ReflectiveOperationException { var argTypes = mappers.stream().map(t -> t.type).toArray(Class[]::new); - try { - var constructor = type.getConstructor(argTypes); - map = - (obj, context, locale) -> { - try { - if (type.isInstance(obj)) { - return obj; - } - - Map map = (Map) obj; + var constructor = type.getConstructor(argTypes); + map = + (obj, context, locale) -> { + try { + Map map = (Map) obj; - var args = new Object[argTypes.length]; + var args = new Object[argTypes.length]; - for (int i = 0; i < args.length; i++) { - var mapper = mappers.get(i); + for (int i = 0; i < args.length; i++) { + var mapper = mappers.get(i); - if (map.containsKey(mapper.name)) { - args[i] = mapper.resolver.convert(map.get(mapper.name), context, locale); - } + if (map.containsKey(mapper.name)) { + args[i] = mapper.resolver.convert(map.get(mapper.name), context, locale); } - - return constructor.newInstance(args); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); } - }; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + + return constructor.newInstance(args); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }; } @Override diff --git a/src/test/java/com/fleetpin/graphql/builder/AuthorizerTest.java b/src/test/java/com/fleetpin/graphql/builder/AuthorizerTest.java new file mode 100644 index 0000000..8516418 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/AuthorizerTest.java @@ -0,0 +1,69 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class AuthorizerTest { + + @Test + public void testQueryCatAllowed() throws ReflectiveOperationException { + var result = execute("query {getCat(name: \"socks\") {age}}"); + Map> response = result.getData(); + + var cat = response.get("getCat"); + + assertEquals(3, cat.get("age")); + + assertTrue(result.getErrors().isEmpty()); + } + + @Test + public void testQueryCatNotAllowed() throws ReflectiveOperationException { + var result = execute("query {getCat(name: \"boots\") {age}}"); + + assertNull(result.getData()); + + assertEquals(1, result.getErrors().size()); + var error = result.getErrors().get(0); + + assertEquals("Exception while fetching data (/getCat) : unauthorized", error.getMessage()); + //assertEquals("", error.getErrorType()); + } + + private ExecutionResult execute(String query) { + return execute(query, null); + } + + private ExecutionResult execute(String query, Map variables) { + try { + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.authorizer")).build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); + } + ExecutionResult result = schema.execute(input); + return result; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java index 571c738..961f1f7 100644 --- a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/fleetpin/graphql/builder/MetaTest.java b/src/test/java/com/fleetpin/graphql/builder/MetaTest.java index 9540f26..5e939ff 100644 --- a/src/test/java/com/fleetpin/graphql/builder/MetaTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/MetaTest.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java index 5208c25..7abcc46 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java index ebc1b82..2678b32 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java b/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java index 4f1b2b7..6ea293d 100644 --- a/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java +++ b/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; diff --git a/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java b/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java new file mode 100644 index 0000000..f171363 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java @@ -0,0 +1,110 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fleetpin.graphql.builder.scalar.Fur; +import com.fleetpin.graphql.builder.scalar.Shape; +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; +import graphql.introspection.IntrospectionWithDirectivesSupport; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ScalarTest { + + @Test + public void testCatFur() throws ReflectiveOperationException { + var scalar = getField("Fur", "SCALAR"); + assertEquals("soft", scalar.get("description")); + } + + @Test + public void testCatShape() throws ReflectiveOperationException { + var scalar = getField("Shape", "SCALAR"); + assertEquals(null, scalar.get("description")); + List> directive = (List>) scalar.get("appliedDirectives"); + assertEquals("Capture", directive.get(0).get("name")); + } + + public Map getField(String typeName, String kind) throws ReflectiveOperationException { + Map> response = execute( + "{" + + " __type(name: \"" + + typeName + + "\") {" + + " name" + + " description" + + " kind" + + " appliedDirectives {\n" + + " name\n" + + " args {\n" + + " name\n" + + " value\n" + + " }\n" + + " }" + + " }" + + "} " + ) + .getData(); + var type = response.get("__type"); + Assertions.assertEquals(typeName, type.get("name")); + Assertions.assertEquals(kind, type.get("kind")); + return type; + } + + @Test + public void testQueryCatFur() throws ReflectiveOperationException { + Map> response = execute("query fur($fur: Fur!){getCat(fur: $fur){ fur}} ", Map.of("fur", "long")).getData(); + var cat = response.get("getCat"); + + var fur = cat.get("fur"); + + assertEquals("long", fur.getInput()); + } + + @Test + public void testQueryShape() throws ReflectiveOperationException { + Map response = execute("query shape($shape: Shape!){getShape(shape: $shape)} ", Map.of("shape", "round")).getData(); + var shape = response.get("getShape"); + + assertEquals("round", shape.getInput()); + } + + private ExecutionResult execute(String query) { + return execute(query, null); + } + + private ExecutionResult execute(String query, Map variables) { + try { + GraphQL schema = GraphQL + .newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.scalar"))) + .build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); + } + ExecutionResult result = schema.execute(input); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup + } + return result; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java index f59f2f2..0e36b8a 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java index 5275cff..fccf72c 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java index b7dea21..ad1ed37 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java @@ -1,9 +1,21 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; import java.util.List; @@ -51,8 +63,7 @@ public void testCatAge() throws ReflectiveOperationException { @Test public void testCatFur() throws ReflectiveOperationException { var name = getField("Cat", "OBJECT", "fur"); - var nonNull = confirmNonNull(name); - confirmBoolean(nonNull); + confirmBoolean(name); } @Test @@ -268,6 +279,64 @@ public void testOneOf() throws ReflectiveOperationException { assertEquals("short", dog.get("fur")); } + @Test + public void testOptionalFieldNotSet() throws ReflectiveOperationException { + Map>> response = execute( + "mutation {myAnimals(animals: [" + + "{cat: {calico: false, name: \"socks\", age: 4}}," + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + " fur " + + "} " + + "... on Dog {" + + " age " + + "} " + + "}} " + ) + .getData(); + + var animals = response.get("myAnimals"); + + var cat = animals.get(0); + + assertEquals("socks", cat.get("name")); + assertEquals(4, cat.get("age")); + assertEquals(false, cat.get("calico")); + assertEquals(true, cat.get("fur")); + } + + @Test + public void testOptionalFieldNull() throws ReflectiveOperationException { + Map>> response = execute( + "mutation {myAnimals(animals: [" + + "{cat: {fur: null, calico: false, name: \"socks\", age: 4}}," + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + " fur " + + "} " + + "... on Dog {" + + " age " + + "} " + + "}} " + ) + .getData(); + + var animals = response.get("myAnimals"); + + var cat = animals.get(0); + + assertEquals("socks", cat.get("name")); + assertEquals(4, cat.get("age")); + assertEquals(false, cat.get("calico")); + assertEquals(null, cat.get("fur")); + } + @Test public void testOneOfError() throws ReflectiveOperationException { var exception = assertThrows( @@ -295,10 +364,70 @@ public void testOneOfError() throws ReflectiveOperationException { assertTrue(exception.getMessage().contains("OneOf must only have a single field set")); } + @Test + public void testOneOfErrorEmpty() throws ReflectiveOperationException { + var exception = assertThrows( + RuntimeException.class, + () -> + execute( + "mutation {myAnimals(animals: [" + + "{}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " + ) + .getData() + ); + + assertTrue(exception.getMessage().contains("OneOf must only have a single field set")); + } + + @Test + public void testOneOfErrorField() throws ReflectiveOperationException { + var exception = assertThrows( + RuntimeException.class, + () -> + execute( + "mutation {myAnimals(animals: [" + + "{cat: {fur: null, calico: false, name: \"socks\", age: 4, error: \"fail\"}}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " + ) + .getData() + ); + assertTrue(exception.getMessage().contains("ERROR")); + } + private ExecutionResult execute(String query) { + return execute(query, null); + } + + private ExecutionResult execute(String query, Map variables) { try { GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type")).build(); - ExecutionResult result = schema.execute(query); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); + } + ExecutionResult result = schema.execute(input); if (!result.getErrors().isEmpty()) { throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup } diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeParsingTest.java index cdb3761..feaec82 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeParsingTest.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/fleetpin/graphql/builder/UnionTest.java b/src/test/java/com/fleetpin/graphql/builder/UnionTest.java index 74103f6..58cec93 100644 --- a/src/test/java/com/fleetpin/graphql/builder/UnionTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/UnionTest.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/fleetpin/graphql/builder/authorizer/Cat.java b/src/test/java/com/fleetpin/graphql/builder/authorizer/Cat.java new file mode 100644 index 0000000..e228c56 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/authorizer/Cat.java @@ -0,0 +1,36 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.authorizer; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Query; + +@Entity +public class Cat { + + public boolean isCalico() { + return true; + } + + public int getAge() { + return 3; + } + + public boolean getFur() { + return true; + } + + @Query + public static Cat getCat(String name) { + return new Cat(); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/authorizer/CatAuthorizer.java b/src/test/java/com/fleetpin/graphql/builder/authorizer/CatAuthorizer.java new file mode 100644 index 0000000..bc0fe8f --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/authorizer/CatAuthorizer.java @@ -0,0 +1,21 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.authorizer; + +import com.fleetpin.graphql.builder.Authorizer; + +public class CatAuthorizer implements Authorizer { + + public boolean allow(String name) { + return "socks".equals(name); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/Animal.java b/src/test/java/com/fleetpin/graphql/builder/generics/Animal.java index 5acc423..8969d94 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/Animal.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/Animal.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.generics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/Cat.java b/src/test/java/com/fleetpin/graphql/builder/generics/Cat.java index a185bb8..1a67ebd 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/Cat.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.generics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/CatFamily.java b/src/test/java/com/fleetpin/graphql/builder/generics/CatFamily.java index 2692097..73a1d49 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/CatFamily.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/CatFamily.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.generics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/CatFamilyFur.java b/src/test/java/com/fleetpin/graphql/builder/generics/CatFamilyFur.java index 81c3404..07a9ea4 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/CatFamilyFur.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/CatFamilyFur.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.generics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/CatFur.java b/src/test/java/com/fleetpin/graphql/builder/generics/CatFur.java index a539392..7a0730c 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/CatFur.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/CatFur.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.generics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/Dog.java b/src/test/java/com/fleetpin/graphql/builder/generics/Dog.java index 46819d7..3e00a5c 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/Dog.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/Dog.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.generics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/DogFur.java b/src/test/java/com/fleetpin/graphql/builder/generics/DogFur.java index 77992dd..c91fb96 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/DogFur.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/DogFur.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.generics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/generics/Fur.java b/src/test/java/com/fleetpin/graphql/builder/generics/Fur.java index fea78e2..590e14d 100644 --- a/src/test/java/com/fleetpin/graphql/builder/generics/Fur.java +++ b/src/test/java/com/fleetpin/graphql/builder/generics/Fur.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.generics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Animal.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Animal.java index bb45920..e0e29ee 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Animal.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Animal.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.inputgenerics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalInput.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalInput.java index f435f88..6f736b9 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalInput.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalInput.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.inputgenerics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalOuterWrapper.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalOuterWrapper.java index f506770..5dfa8aa 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalOuterWrapper.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalOuterWrapper.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.inputgenerics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalWrapper.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalWrapper.java index 01fd6c7..19674dc 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalWrapper.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/AnimalWrapper.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.inputgenerics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Cat.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Cat.java index 8b2ccef..e0d45fe 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/Cat.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.inputgenerics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/CatAnimalInput.java b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/CatAnimalInput.java index 1416c55..560ef23 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenerics/CatAnimalInput.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenerics/CatAnimalInput.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.inputgenerics; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java b/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java index 1ec5377..d8cf1aa 100644 --- a/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java +++ b/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.parameter; diff --git a/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java b/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java index 1459b23..23dacbd 100644 --- a/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java +++ b/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.parameter; diff --git a/src/test/java/com/fleetpin/graphql/builder/publishRestrictions/Test.java b/src/test/java/com/fleetpin/graphql/builder/publishRestrictions/Test.java index 3d6fe9b..ff0927a 100644 --- a/src/test/java/com/fleetpin/graphql/builder/publishRestrictions/Test.java +++ b/src/test/java/com/fleetpin/graphql/builder/publishRestrictions/Test.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.publishRestrictions; import com.fleetpin.graphql.builder.RestrictType; diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/EntityRestrictions.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/EntityRestrictions.java index b10ab32..09a95a1 100644 --- a/src/test/java/com/fleetpin/graphql/builder/restrictions/EntityRestrictions.java +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/EntityRestrictions.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.restrictions; import com.fleetpin.graphql.builder.RestrictType; diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java index c90547f..31d6f90 100644 --- a/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.restrictions; import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java index bfc0eae..bbb5528 100644 --- a/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java @@ -9,6 +9,17 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.restrictions.parameter; diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java new file mode 100644 index 0000000..15ff89c --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java @@ -0,0 +1,40 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.scalar; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.fleetpin.graphql.builder.SDLDirective; +import com.fleetpin.graphql.builder.annotations.Directive; +import graphql.introspection.Introspection.DirectiveLocation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.List; + +@Directive(Capture.Processor.class) +@Retention(RUNTIME) +@Target({ ElementType.TYPE }) +public @interface Capture { + static class Processor implements SDLDirective { + + @Override + public List validLocations() { + return List.of(DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA); + } + + @Override + public CaptureType build(Capture annotation, Class location) { + return new CaptureType(); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/CaptureType.java b/src/test/java/com/fleetpin/graphql/builder/scalar/CaptureType.java new file mode 100644 index 0000000..413d7b4 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/CaptureType.java @@ -0,0 +1,18 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.scalar; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.SchemaOption; + +@Entity(SchemaOption.INPUT) +public class CaptureType {} diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/Cat.java b/src/test/java/com/fleetpin/graphql/builder/scalar/Cat.java new file mode 100644 index 0000000..8373373 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/Cat.java @@ -0,0 +1,39 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.scalar; + +import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.Query; + +@Entity +public class Cat { + + private final Fur fur; + + private Cat(Fur fur) { + this.fur = fur; + } + + public Fur getFur() { + return fur; + } + + @Query + public static Cat getCat(Fur fur) { + return new Cat(fur); + } + + @Query + public static Shape getShape(Shape shape) { + return shape; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/Fur.java b/src/test/java/com/fleetpin/graphql/builder/scalar/Fur.java new file mode 100644 index 0000000..0c2f2a6 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/Fur.java @@ -0,0 +1,61 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.scalar; + +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; +import com.fleetpin.graphql.builder.annotations.Scalar; +import graphql.schema.Coercing; +import graphql.schema.CoercingParseLiteralException; +import graphql.schema.CoercingParseValueException; +import graphql.schema.CoercingSerializeException; + +@Scalar(Fur.FurCoercing.class) +@GraphQLDescription("soft") +public class Fur { + + private String input; + + public Fur(String input) { + this.input = input; + } + + public String getInput() { + return input; + } + + public static class FurCoercing implements Coercing { + + @Override + public Fur serialize(Object dataFetcherResult) throws CoercingSerializeException { + return convertImpl(dataFetcherResult); + } + + @Override + public Fur parseValue(Object input) throws CoercingParseValueException { + return convertImpl(input); + } + + @Override + public Fur parseLiteral(Object input) throws CoercingParseLiteralException { + return convertImpl(input); + } + + private Fur convertImpl(Object input) { + if (input instanceof Fur) { + return (Fur) input; + } else if (input instanceof String) { + return new Fur((String) input); + } + throw new CoercingParseLiteralException(); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/Shape.java b/src/test/java/com/fleetpin/graphql/builder/scalar/Shape.java new file mode 100644 index 0000000..fb73e84 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/Shape.java @@ -0,0 +1,60 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.scalar; + +import com.fleetpin.graphql.builder.annotations.Scalar; +import graphql.schema.Coercing; +import graphql.schema.CoercingParseLiteralException; +import graphql.schema.CoercingParseValueException; +import graphql.schema.CoercingSerializeException; + +@Scalar(Shape.ShapeCoercing.class) +@Capture +public class Shape { + + private String input; + + public Shape(String input) { + this.input = input; + } + + public String getInput() { + return input; + } + + public static class ShapeCoercing implements Coercing { + + @Override + public Shape serialize(Object dataFetcherResult) throws CoercingSerializeException { + return convertImpl(dataFetcherResult); + } + + @Override + public Shape parseValue(Object input) throws CoercingParseValueException { + return convertImpl(input); + } + + @Override + public Shape parseLiteral(Object input) throws CoercingParseLiteralException { + return convertImpl(input); + } + + private Shape convertImpl(Object input) { + if (input instanceof Shape) { + return (Shape) input; + } else if (input instanceof String) { + return new Shape((String) input); + } + throw new CoercingParseLiteralException(); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/Circular.java b/src/test/java/com/fleetpin/graphql/builder/type/Circular.java index 2260982..1f0bd46 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/Circular.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/Circular.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/DeprecatedObject.java b/src/test/java/com/fleetpin/graphql/builder/type/DeprecatedObject.java index a53edac..c8e3800 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/DeprecatedObject.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/DeprecatedObject.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/DescriptionObject.java b/src/test/java/com/fleetpin/graphql/builder/type/DescriptionObject.java index 34b6fa9..d3d58ba 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/DescriptionObject.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/DescriptionObject.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/SimpleType.java b/src/test/java/com/fleetpin/graphql/builder/type/SimpleType.java index ca328c9..9b21a5f 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/SimpleType.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/SimpleType.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/UnionType.java b/src/test/java/com/fleetpin/graphql/builder/type/UnionType.java index c759dad..f754ba1 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/UnionType.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/UnionType.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java index 0a6cf40..39243d2 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type.directive; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/CaptureType.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/CaptureType.java index 5083582..3d7c923 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/CaptureType.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/CaptureType.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type.directive; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java index ea3e628..a7dea4b 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type.directive; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java index d2ed983..d2b57ca 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type.directive; import com.fleetpin.graphql.builder.SchemaConfiguration; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java index c49065e..6024e7c 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type.inheritance; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java index 07f52c9..f3ee671 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java @@ -1,23 +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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type.inheritance; import com.fleetpin.graphql.builder.annotations.Entity; import com.fleetpin.graphql.builder.annotations.Mutation; import com.fleetpin.graphql.builder.annotations.SchemaOption; +import java.util.Optional; @Entity(SchemaOption.BOTH) public class Cat extends Animal { private boolean calico; private int age; - private boolean fur; + private Optional fur; public Cat() { calico = true; age = 3; - fur = true; + fur = Optional.of(true); } - public Cat(boolean calico, int age, boolean fur) { + public Cat(boolean calico, int age, Optional fur) { super(); this.calico = calico; this.age = age; @@ -40,14 +52,18 @@ public void setAge(int age) { this.age = age; } - public boolean isFur() { + public Optional getFur() { return fur; } - public void setFur(boolean fur) { + public void setFur(Optional fur) { this.fur = fur; } + public void setError(Optional ignore) { + throw new RuntimeException("ERROR"); + } + @Mutation public static Cat getCat() { return null; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Dog.java b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Dog.java index 86793d4..6c7bbf4 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Dog.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Dog.java @@ -1,3 +1,14 @@ +/* + * 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 limitations under + * the License. + */ package com.fleetpin.graphql.builder.type.inheritance; import com.fleetpin.graphql.builder.annotations.Entity; From beea4ea4b1c52d56c9ae102b5386123041ba5a58 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Mon, 6 Mar 2023 16:33:51 +1300 Subject: [PATCH 003/112] get scalars working correctly --- pom.xml | 17 +- .../fleetpin/graphql/builder/Authorizer.java | 12 -- .../graphql/builder/AuthorizerSchema.java | 12 -- .../graphql/builder/DirectiveCaller.java | 12 -- .../graphql/builder/DirectiveOperation.java | 12 -- .../graphql/builder/DirectivesSchema.java | 12 -- .../graphql/builder/EntityHolder.java | 56 ++++-- .../graphql/builder/EntityProcessor.java | 48 +++-- .../fleetpin/graphql/builder/EntityUtil.java | 12 -- .../fleetpin/graphql/builder/EnumEntity.java | 12 -- .../fleetpin/graphql/builder/ErrorType.java | 12 -- .../graphql/builder/FilteredPublisher.java | 12 -- .../graphql/builder/InputBuilder.java | 51 ++++- .../graphql/builder/ObjectEntity.java | 39 ++-- .../graphql/builder/ReferenceEntity.java | 58 ------ .../graphql/builder/RestrictType.java | 12 -- .../graphql/builder/RestrictTypeFactory.java | 12 -- .../graphql/builder/SDLDirective.java | 12 -- .../graphql/builder/SDLProcessor.java | 12 -- .../graphql/builder/ScalarEntity.java | 14 +- .../graphql/builder/SchemaBuilder.java | 183 ++++++++++-------- .../graphql/builder/SchemaConfiguration.java | 12 -- .../fleetpin/graphql/builder/TypeBuilder.java | 40 ++-- .../fleetpin/graphql/builder/TypeMeta.java | 12 -- .../fleetpin/graphql/builder/UnionType.java | 12 -- .../graphql/builder/annotations/Context.java | 14 +- .../builder/annotations/Directive.java | 12 -- .../graphql/builder/annotations/Entity.java | 12 -- .../builder/annotations/GraphQLCreator.java | 23 +++ .../annotations/GraphQLDeprecated.java | 12 -- .../annotations/GraphQLDescription.java | 12 -- .../builder/annotations/GraphQLIgnore.java | 12 -- .../graphql/builder/annotations/Id.java | 12 -- .../builder/annotations/InputIgnore.java | 12 -- .../graphql/builder/annotations/Mutation.java | 12 -- .../graphql/builder/annotations/OneOf.java | 12 -- .../graphql/builder/annotations/Query.java | 12 -- .../graphql/builder/annotations/Restrict.java | 12 -- .../builder/annotations/Restricts.java | 12 -- .../graphql/builder/annotations/Scalar.java | 12 -- .../builder/annotations/SchemaOption.java | 12 -- .../builder/annotations/Subscription.java | 12 -- .../graphql/builder/annotations/Union.java | 12 -- .../mapper/ConstructorFieldBuilder.java | 70 +++++++ .../builder/mapper/InputTypeBuilder.java | 12 -- .../builder/mapper/ObjectFieldBuilder.java | 12 -- .../graphql/builder/mapper/OneOfBuilder.java | 12 -- .../builder/mapper/RecordFieldBuilder.java | 78 -------- .../fleetpin/graphql/builder/ContextTest.java | 83 ++++++++ .../graphql/builder/ParameterParsingTest.java | 12 -- .../builder/ParameterTypeParsingTest.java | 12 -- .../graphql/builder/PublishRestrictions.java | 12 -- .../fleetpin/graphql/builder/RecordTest.java | 54 ++++++ .../fleetpin/graphql/builder/ScalarTest.java | 13 +- .../graphql/builder/context/GraphContext.java | 29 +++ .../graphql/builder/context/Queries.java | 50 +++++ .../graphql/builder/parameter/Parameter.java | 12 -- .../builder/parameter/TypeInputParameter.java | 12 -- .../graphql/builder/record/Queries.java | 42 ++++ .../parameter/RestrictedEntity.java | 12 -- .../fleetpin/graphql/builder/scalar/Cat.java | 13 +- 61 files changed, 645 insertions(+), 822 deletions(-) delete mode 100644 src/main/java/com/fleetpin/graphql/builder/ReferenceEntity.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLCreator.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/mapper/ConstructorFieldBuilder.java delete mode 100644 src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/ContextTest.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/RecordTest.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/context/GraphContext.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/context/Queries.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/record/Queries.java diff --git a/pom.xml b/pom.xml index 821d8ea..654d3f5 100644 --- a/pom.xml +++ b/pom.xml @@ -15,24 +15,19 @@ 4.0.0 com.fleetpin graphql-builder - 1.0.2-SNAPSHOT + 2.0.0-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection https://github.com/fleetpin/graphql-builder - - test - - 2017 - - 5.6.0 UTF-8 2.14.2 1.9.0 1.0.0 + 20.0 @@ -146,7 +141,13 @@ com.graphql-java graphql-java - 20.0 + ${graphql.version} + + + com.graphql-java + graphql-java-extended-scalars + ${graphql.version} + test org.reflections diff --git a/src/main/java/com/fleetpin/graphql/builder/Authorizer.java b/src/main/java/com/fleetpin/graphql/builder/Authorizer.java index 98166a6..f6480f4 100644 --- a/src/main/java/com/fleetpin/graphql/builder/Authorizer.java +++ b/src/main/java/com/fleetpin/graphql/builder/Authorizer.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; public interface Authorizer {} diff --git a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java index 72a7d61..1c58fcc 100644 --- a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import graphql.GraphqlErrorException; diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java index a96e8b0..73b3bd9 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveCaller.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import graphql.schema.DataFetcher; diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java index 26f2cca..e278c2c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveOperation.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import java.lang.annotation.Annotation; diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java index f481d19..5ef7dcd 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Directive; diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java index 32742c4..ea1f430 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.TypeMeta.Flag; @@ -28,6 +16,7 @@ import com.fleetpin.graphql.builder.annotations.Union; import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; import graphql.Scalars; +import graphql.com.google.common.collect.Sets; import graphql.schema.GraphQLInputType; import graphql.schema.GraphQLList; import graphql.schema.GraphQLNamedInputType; @@ -38,12 +27,17 @@ import graphql.schema.GraphQLTypeReference; import graphql.schema.GraphQLUnionType; import java.lang.annotation.Annotation; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Stream; public abstract class EntityHolder { @@ -172,14 +166,19 @@ private static InputTypeBuilder process(Iterator> iterator, InputTypeBu var type = iterator.next(); if (List.class.isAssignableFrom(type)) { - return processList(iterator, resolver); + return processCollection(ArrayList::new, iterator, resolver); } + + if (Set.class.isAssignableFrom(type)) { + return processCollection(size -> new LinkedHashSet<>((int) (size / 0.75 + 1)), iterator, resolver); + } + if (Optional.class.isAssignableFrom(type)) { return processOptional(iterator, resolver); } - // if(type.isArray()) { - // return processArray(iterator); - // } + if (type.isArray()) { + return processArray(type, iterator, resolver); + } if (iterator.hasNext()) { throw new RuntimeException("Unsupported type " + type); @@ -225,13 +224,34 @@ private static InputTypeBuilder processOptional(Iterator> iterator, Inp }; } - private static InputTypeBuilder processList(Iterator> iterator, InputTypeBuilder resolver) { + private static InputTypeBuilder processArray(Class type, Iterator> iterator, InputTypeBuilder resolver) { + var component = type.getComponentType(); + if (component.isPrimitive()) { + throw new RuntimeException("Do not support primitive array"); + } + var mapper = process(iterator, resolver); return (obj, context, locale) -> { if (obj instanceof Collection) { var collection = (Collection) obj; + Object[] toReturn = (Object[]) Array.newInstance(component, collection.size()); + int i = 0; + for (var c : collection) { + toReturn[i] = mapper.convert(c, context, locale); + } + return toReturn; + } else { + throw new RuntimeException("Expected a Collection got " + obj.getClass()); + } + }; + } - var toReturn = new ArrayList<>(collection.size()); + private static InputTypeBuilder processCollection(Function create, Iterator> iterator, InputTypeBuilder resolver) { + var mapper = process(iterator, resolver); + return (obj, context, locale) -> { + if (obj instanceof Collection) { + var collection = (Collection) obj; + var toReturn = create.apply(collection.size()); for (var c : collection) { toReturn.add(mapper.convert(c, context, locale)); } diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java index b92ecf3..f30629d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Scalar; @@ -31,11 +19,13 @@ import graphql.schema.GraphQLCodeRegistry; import graphql.schema.GraphQLInputType; import graphql.schema.GraphQLOutputType; +import graphql.schema.GraphQLScalarType; import graphql.schema.GraphQLType; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Type; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Consumer; @@ -48,9 +38,10 @@ public class EntityProcessor { private final Map entities; - EntityProcessor(GraphQLCodeRegistry.Builder codeRegistry, DirectivesSchema diretives) { + EntityProcessor(List scalars, GraphQLCodeRegistry.Builder codeRegistry, DirectivesSchema diretives) { this.entities = new HashMap<>(); addDefaults(); + addScalars(scalars); this.codeRegistry = codeRegistry; this.directives = diretives; @@ -69,6 +60,34 @@ private void addDefaults() { put(String.class, new ScalarEntity(Scalars.GraphQLString)); } + private void addScalars(List scalars) { + for (var scalar : scalars) { + var coercing = scalar.getCoercing(); + var type = coercing.getClass(); + for (var method : type.getMethods()) { + if (method.isSynthetic()) { + continue; + } + if ("parseValue".equals(method.getName())) { + var returnType = method.getReturnType(); + if (returnType.equals(Long.class)) { + put(Long.TYPE, new ScalarEntity(scalar)); + } else if (returnType.equals(Byte.class)) { + put(Byte.TYPE, new ScalarEntity(scalar)); + } else if (returnType.equals(Character.class)) { + put(Character.TYPE, new ScalarEntity(scalar)); + } else if (returnType.equals(Float.class)) { + put(Float.TYPE, new ScalarEntity(scalar)); + } else if (returnType.equals(Short.class)) { + put(Short.TYPE, new ScalarEntity(scalar)); + } + put(returnType, new ScalarEntity(scalar)); + break; + } + } + } + } + private void put(Class type, ScalarEntity entity) { var name = EntityUtil.getName(new TypeMeta(null, type, type)); entities.put(name, entity); @@ -88,9 +107,6 @@ EntityHolder getEntity(TypeMeta meta) { name, __ -> { Class type = meta.getType(); - if (type.equals(Object.class)) { - return new ReferenceEntity("Object"); - } Type genericType = meta.getGenericType(); if (genericType == null) { genericType = type; diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java index e4a62cd..91921e8 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; diff --git a/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java index 7be84ce..0909554 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.GraphQLDescription; diff --git a/src/main/java/com/fleetpin/graphql/builder/ErrorType.java b/src/main/java/com/fleetpin/graphql/builder/ErrorType.java index 7e67454..16e7e67 100644 --- a/src/main/java/com/fleetpin/graphql/builder/ErrorType.java +++ b/src/main/java/com/fleetpin/graphql/builder/ErrorType.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import graphql.ErrorClassification; diff --git a/src/main/java/com/fleetpin/graphql/builder/FilteredPublisher.java b/src/main/java/com/fleetpin/graphql/builder/FilteredPublisher.java index ead590a..afe8cc9 100644 --- a/src/main/java/com/fleetpin/graphql/builder/FilteredPublisher.java +++ b/src/main/java/com/fleetpin/graphql/builder/FilteredPublisher.java @@ -17,18 +17,6 @@ import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; -/* - * 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 limitations under - * the License. - */ - class FilteredPublisher implements Publisher { private final RestrictType restrict; diff --git a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java index ac947cf..4b69e26 100644 --- a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java @@ -9,7 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Entity; @@ -18,17 +17,18 @@ import com.fleetpin.graphql.builder.annotations.InputIgnore; import com.fleetpin.graphql.builder.annotations.OneOf; import com.fleetpin.graphql.builder.annotations.SchemaOption; +import com.fleetpin.graphql.builder.mapper.ConstructorFieldBuilder; +import com.fleetpin.graphql.builder.mapper.ConstructorFieldBuilder.RecordMapper; import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; import com.fleetpin.graphql.builder.mapper.ObjectFieldBuilder; import com.fleetpin.graphql.builder.mapper.ObjectFieldBuilder.FieldMapper; import com.fleetpin.graphql.builder.mapper.OneOfBuilder; -import com.fleetpin.graphql.builder.mapper.RecordFieldBuilder; -import com.fleetpin.graphql.builder.mapper.RecordFieldBuilder.RecordMapper; import graphql.schema.GraphQLInputObjectField; import graphql.schema.GraphQLInputObjectType; import graphql.schema.GraphQLInputObjectType.Builder; import graphql.schema.GraphQLNamedInputType; import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -157,6 +157,47 @@ public InputTypeBuilder resolve() { } } + public static class ObjectConstructorType extends InputBuilder { + + private final Constructor constructor; + + public ObjectConstructorType(EntityProcessor entityProcessor, TypeMeta meta, Constructor constructor) { + super(entityProcessor, meta); + this.constructor = constructor; + } + + @Override + void processFields(Builder graphInputType) { + for (var parameter : constructor.getParameters()) { + try { + GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); + field.name(parameter.getName()); + entityProcessor.addSchemaDirective(parameter, meta.getType(), field::withAppliedDirective); + TypeMeta innerMeta = new TypeMeta(meta, parameter.getType(), parameter.getParameterizedType()); + var entity = entityProcessor.getEntity(innerMeta); + var inputType = entity.getInputType(innerMeta, parameter.getAnnotations()); + field.type(inputType); + graphInputType.field(field); + } catch (RuntimeException e) { + throw new RuntimeException("Failed to process method " + parameter, e); + } + } + } + + @Override + public InputTypeBuilder resolve() { + var fieldMappers = new ArrayList(); + + for (var parameter : constructor.getParameters()) { + TypeMeta innerMeta = new TypeMeta(meta, parameter.getType(), parameter.getParameterizedType()); + var resolver = entityProcessor.getResolver(innerMeta); + fieldMappers.add(new RecordMapper(parameter.getName(), parameter.getType(), resolver)); + } + + return new ConstructorFieldBuilder(meta.getType(), fieldMappers); + } + } + public static class Record extends InputBuilder { public Record(EntityProcessor entityProcessor, TypeMeta meta) { @@ -218,8 +259,8 @@ protected InputTypeBuilder resolve() { } } } - return new RecordFieldBuilder(meta.getType(), fieldMappers); - } catch (RuntimeException | ReflectiveOperationException e) { + return new ConstructorFieldBuilder(meta.getType(), fieldMappers); + } catch (RuntimeException e) { throw new RuntimeException("Failed to process " + this.meta.getType(), e); } } diff --git a/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java b/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java index 90987f4..89d76b3 100644 --- a/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java @@ -9,20 +9,9 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; +import com.fleetpin.graphql.builder.annotations.GraphQLCreator; import com.fleetpin.graphql.builder.annotations.OneOf; import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; import graphql.schema.GraphQLNamedInputType; @@ -35,19 +24,33 @@ public class ObjectEntity extends EntityHolder { private TypeBuilder typeBuilder; public ObjectEntity(EntityProcessor entityProcessor, TypeMeta meta) { + if (EntityUtil.isRecord(meta.getType())) { + typeBuilder = new TypeBuilder.Record(entityProcessor, meta); + } else { + typeBuilder = new TypeBuilder.ObjectType(entityProcessor, meta); + } + if (meta.getType().isAnnotationPresent(OneOf.class)) { inputBuilder = new InputBuilder.OneOfInputBuilder(entityProcessor, meta); } else if (EntityUtil.isRecord(meta.getType())) { inputBuilder = new InputBuilder.Record(entityProcessor, meta); } else { + var constructors = meta.getType().getDeclaredConstructors(); + if (constructors.length == 1) { + var constructor = constructors[0]; + if (constructor.getParameterCount() > 0) { + inputBuilder = new InputBuilder.ObjectConstructorType(entityProcessor, meta, constructor); + return; + } + } + for (var constructor : constructors) { + if (constructor.isAnnotationPresent(GraphQLCreator.class)) { + inputBuilder = new InputBuilder.ObjectConstructorType(entityProcessor, meta, constructor); + return; + } + } inputBuilder = new InputBuilder.ObjectType(entityProcessor, meta); } - - if (EntityUtil.isRecord(meta.getType())) { - typeBuilder = new TypeBuilder.Record(entityProcessor, meta); - } else { - typeBuilder = new TypeBuilder.ObjectType(entityProcessor, meta); - } } @Override diff --git a/src/main/java/com/fleetpin/graphql/builder/ReferenceEntity.java b/src/main/java/com/fleetpin/graphql/builder/ReferenceEntity.java deleted file mode 100644 index a5b6061..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/ReferenceEntity.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 limitations under - * the License. - */ -/* - * 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 limitations under - * the License. - */ - -package com.fleetpin.graphql.builder; - -import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; -import graphql.schema.GraphQLNamedInputType; -import graphql.schema.GraphQLNamedOutputType; -import graphql.schema.GraphQLTypeReference; - -public class ReferenceEntity extends EntityHolder { - - private final GraphQLTypeReference reference; - - ReferenceEntity(String typeName) { - this.reference = GraphQLTypeReference.typeRef(typeName); - } - - @Override - protected GraphQLNamedOutputType buildType() { - return reference; - } - - @Override - protected GraphQLNamedInputType buildInput() { - return reference; - } - - @Override - protected String buildInputName() { - return reference.getName(); - } - - @Override - protected InputTypeBuilder buildResolver() { - return null; - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/RestrictType.java b/src/main/java/com/fleetpin/graphql/builder/RestrictType.java index d6a0335..b234d5a 100644 --- a/src/main/java/com/fleetpin/graphql/builder/RestrictType.java +++ b/src/main/java/com/fleetpin/graphql/builder/RestrictType.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import java.util.ArrayList; diff --git a/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java b/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java index ccbe6e8..05e4be2 100644 --- a/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java +++ b/src/main/java/com/fleetpin/graphql/builder/RestrictTypeFactory.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import graphql.schema.DataFetchingEnvironment; diff --git a/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java b/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java index 41c8d8f..7307bad 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java +++ b/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import graphql.introspection.Introspection.DirectiveLocation; diff --git a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java b/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java index e4d1637..ee83e06 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; diff --git a/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java b/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java index ac7b17d..6ccf3f5 100644 --- a/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.GraphQLDescription; @@ -77,6 +65,6 @@ protected String buildInputName() { @Override public InputTypeBuilder buildResolver() { - return scalar.getCoercing()::serialize; + return scalar.getCoercing()::parseValue; } } diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index 39100e8..07c442e 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Context; @@ -42,14 +30,13 @@ import graphql.schema.GraphQLCodeRegistry; import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLObjectType; -import graphql.schema.GraphQLObjectType.Builder; +import graphql.schema.GraphQLScalarType; import graphql.schema.GraphQLSchema; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -64,13 +51,13 @@ public class SchemaBuilder { private final GraphQLCodeRegistry.Builder codeRegistry; - private final Builder graphQuery; - private final Builder graphMutations; - private final Builder graphSubscriptions; + private final GraphQLObjectType.Builder graphQuery; + private final GraphQLObjectType.Builder graphMutations; + private final GraphQLObjectType.Builder graphSubscriptions; private final EntityProcessor entityProcessor; - private SchemaBuilder(DirectivesSchema diretives, AuthorizerSchema authorizer) { + private SchemaBuilder(List scalars, DirectivesSchema diretives, AuthorizerSchema authorizer) { this.diretives = diretives; this.authorizer = authorizer; @@ -82,7 +69,7 @@ private SchemaBuilder(DirectivesSchema diretives, AuthorizerSchema authorizer) { graphSubscriptions.name("Subscriptions"); this.codeRegistry = GraphQLCodeRegistry.newCodeRegistry(); - this.entityProcessor = new EntityProcessor(codeRegistry, diretives); + this.entityProcessor = new EntityProcessor(scalars, codeRegistry, diretives); diretives.processSDL(entityProcessor); } @@ -112,7 +99,7 @@ private SchemaBuilder process(Set endPoints) throws ReflectiveOperationE field.type(type); for (int i = 0; i < method.getParameterCount(); i++) { GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); - if (isContext(method.getParameterTypes()[i])) { + if (isContext(method.getParameterTypes()[i], method.getParameterAnnotations()[i])) { continue; } @@ -176,7 +163,12 @@ private graphql.schema.GraphQLSchema.Builder build(Set class1) { + private static boolean isContext(Class class1, Annotation[] annotations) { + for (var annotation : annotations) { + if (annotation instanceof Context) { + return true; + } + } return ( class1.isAssignableFrom(GraphQLContext.class) || class1.isAssignableFrom(DataFetchingEnvironment.class) || class1.isAnnotationPresent(Context.class) ); @@ -200,7 +192,7 @@ private DataFetcher buildDataFetcher(TypeMeta meta, Method method) { var name = method.getParameters()[i].getName(); var generic = method.getGenericParameterTypes()[i]; var argMeta = new TypeMeta(meta, type, generic); - resolvers[i] = buildResolver(name, argMeta); + resolvers[i] = buildResolver(name, argMeta, method.getParameterAnnotations()[i]); } DataFetcher fetcher = env -> { @@ -224,32 +216,37 @@ private DataFetcher buildDataFetcher(TypeMeta meta, Method method) { return fetcher; } - private Function buildResolver(String name, TypeMeta argMeta) { - var type = argMeta.getType(); - if (type.isAssignableFrom(DataFetchingEnvironment.class)) { - return env -> env; - } - if (type.isAssignableFrom(GraphQLContext.class)) { - return env -> env.getGraphQlContext(); + private Function buildResolver(String name, TypeMeta argMeta, Annotation[] annotations) { + if (isContext(argMeta.getType(), annotations)) { + var type = argMeta.getType(); + if (type.isAssignableFrom(DataFetchingEnvironment.class)) { + return env -> env; + } + if (type.isAssignableFrom(GraphQLContext.class)) { + return env -> env.getGraphQlContext(); + } + return env -> { + var localContext = env.getLocalContext(); + if (localContext != null && type.isAssignableFrom(localContext.getClass())) { + return localContext; + } + + var context = env.getContext(); + if (context != null && type.isAssignableFrom(context.getClass())) { + return context; + } + + context = env.getGraphQlContext().get(name); + if (context != null && type.isAssignableFrom(context.getClass())) { + return context; + } + throw new RuntimeException("Context object " + name + " not found"); + }; } var resolver = entityProcessor.getResolver(argMeta); return env -> { - var localContext = env.getLocalContext(); - if (localContext != null && type.isAssignableFrom(localContext.getClass())) { - return localContext; - } - - var context = env.getContext(); - if (context != null && type.isAssignableFrom(context.getClass())) { - return context; - } - - context = env.getGraphQlContext().get(name); - if (context != null && type.isAssignableFrom(context.getClass())) { - return context; - } var arg = env.getArgument(name); return resolver.convert(arg, env.getGraphQlContext(), env.getLocale()); }; @@ -259,58 +256,90 @@ public static GraphQLSchema build(String... classPath) throws ReflectiveOperatio return builder(classPath).build(); } - public static GraphQLSchema.Builder builder(String... classPath) throws ReflectiveOperationException { - Reflections reflections = new Reflections(classPath, Scanners.SubTypes, Scanners.MethodsAnnotated, Scanners.TypesAnnotated); - Set> authorizers = reflections.getSubTypesOf(Authorizer.class); - //want to make everything split by package - AuthorizerSchema authorizer = AuthorizerSchema.build(new HashSet<>(Arrays.asList(classPath)), authorizers); + public static GraphQLSchema.Builder builder(String... classpath) throws ReflectiveOperationException { + var builder = builder(); + for (var path : classpath) { + builder.classpath(path); + } + return builder.build(); + } - Set> schemaConfiguration = reflections.getSubTypesOf(SchemaConfiguration.class); + public static Builder builder() { + return new Builder(); + } - Set> dierctivesTypes = reflections.getTypesAnnotatedWith(Directive.class); + public static class Builder { - Set> restrict = reflections.getTypesAnnotatedWith(Restrict.class); - Set> restricts = reflections.getTypesAnnotatedWith(Restricts.class); - List> globalRestricts = new ArrayList<>(); + private List classpaths = new ArrayList<>(); + private List scalars = new ArrayList<>(); - for (var r : restrict) { - Restrict annotation = r.getAnnotation(Restrict.class); - var factoryClass = annotation.value(); - var factory = factoryClass.getConstructor().newInstance(); - if (!factory.extractType().isAssignableFrom(r)) { - throw new RuntimeException("Restrict annotation does match class applied to targets" + factory.extractType() + " but was on class " + r); - } - globalRestricts.add(factory); + private Builder() {} + + public Builder classpath(String classpath) { + this.classpaths.add(classpath); + return this; + } + + public Builder scalar(GraphQLScalarType scalar) { + this.scalars.add(scalar); + return this; } - for (var r : restricts) { - Restricts annotations = r.getAnnotation(Restricts.class); - for (Restrict annotation : annotations.value()) { + public GraphQLSchema.Builder build() throws ReflectiveOperationException { + Reflections reflections = new Reflections(classpaths, Scanners.SubTypes, Scanners.MethodsAnnotated, Scanners.TypesAnnotated); + Set> authorizers = reflections.getSubTypesOf(Authorizer.class); + //want to make everything split by package + AuthorizerSchema authorizer = AuthorizerSchema.build(new HashSet<>(classpaths), authorizers); + + Set> schemaConfiguration = reflections.getSubTypesOf(SchemaConfiguration.class); + + Set> dierctivesTypes = reflections.getTypesAnnotatedWith(Directive.class); + + Set> restrict = reflections.getTypesAnnotatedWith(Restrict.class); + Set> restricts = reflections.getTypesAnnotatedWith(Restricts.class); + List> globalRestricts = new ArrayList<>(); + + for (var r : restrict) { + Restrict annotation = r.getAnnotation(Restrict.class); var factoryClass = annotation.value(); var factory = factoryClass.getConstructor().newInstance(); - if (!factory.extractType().isAssignableFrom(r)) { throw new RuntimeException("Restrict annotation does match class applied to targets" + factory.extractType() + " but was on class " + r); } globalRestricts.add(factory); } - } - DirectivesSchema diretivesSchema = DirectivesSchema.build(globalRestricts, dierctivesTypes); + for (var r : restricts) { + Restricts annotations = r.getAnnotation(Restricts.class); + for (Restrict annotation : annotations.value()) { + var factoryClass = annotation.value(); + var factory = factoryClass.getConstructor().newInstance(); + + if (!factory.extractType().isAssignableFrom(r)) { + throw new RuntimeException( + "Restrict annotation does match class applied to targets" + factory.extractType() + " but was on class " + r + ); + } + globalRestricts.add(factory); + } + } - Set> types = reflections.getTypesAnnotatedWith(Entity.class); + DirectivesSchema diretivesSchema = DirectivesSchema.build(globalRestricts, dierctivesTypes); - var mutations = reflections.getMethodsAnnotatedWith(Mutation.class); - var subscriptions = reflections.getMethodsAnnotatedWith(Subscription.class); - var queries = reflections.getMethodsAnnotatedWith(Query.class); + Set> types = reflections.getTypesAnnotatedWith(Entity.class); - var endPoints = new HashSet<>(mutations); - endPoints.addAll(subscriptions); - endPoints.addAll(queries); + var mutations = reflections.getMethodsAnnotatedWith(Mutation.class); + var subscriptions = reflections.getMethodsAnnotatedWith(Subscription.class); + var queries = reflections.getMethodsAnnotatedWith(Query.class); - types.removeIf(t -> t.getDeclaredAnnotation(Entity.class) == null); - types.removeIf(t -> t.isAnonymousClass()); + var endPoints = new HashSet<>(mutations); + endPoints.addAll(subscriptions); + endPoints.addAll(queries); - return new SchemaBuilder(diretivesSchema, authorizer).processTypes(types).process(endPoints).build(schemaConfiguration); + types.removeIf(t -> t.getDeclaredAnnotation(Entity.class) == null); + types.removeIf(t -> t.isAnonymousClass()); + + return new SchemaBuilder(scalars, diretivesSchema, authorizer).processTypes(types).process(endPoints).build(schemaConfiguration); + } } } diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java b/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java index 53d12bf..4eb2412 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaConfiguration.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; /** diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java index 8b81fe5..26c5888 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Entity; @@ -73,6 +61,7 @@ public GraphQLNamedOutputType buildType() throws ReflectiveOperationException { if (unmappedGenerics) { var name = EntityUtil.getName(meta.notDirect()); + graphType.withInterface(GraphQLTypeReference.typeRef(name)); if (meta.isDirect()) { interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(name)); @@ -82,22 +71,19 @@ public GraphQLNamedOutputType buildType() throws ReflectiveOperationException { while (parent != null) { if (parent.isAnnotationPresent(Entity.class)) { TypeMeta innerMeta = new TypeMeta(meta, parent, type.getGenericSuperclass()); - var interfaceName = entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); - graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); - interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + GraphQLInterfaceType interfaceName = (GraphQLInterfaceType) entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); + addInterface(graphType, interfaceBuilder, interfaceName); if (!parent.equals(type.getGenericSuperclass())) { innerMeta = new TypeMeta(meta, parent, parent); - interfaceName = entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); - graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); - interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + interfaceName = (GraphQLInterfaceType) entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); + addInterface(graphType, interfaceBuilder, interfaceName); } var genericMeta = new TypeMeta(null, parent, parent); if (!EntityUtil.getName(innerMeta).equals(EntityUtil.getName(genericMeta))) { - interfaceName = entityProcessor.getEntity(genericMeta).getInnerType(genericMeta); - graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); - interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); + interfaceName = (GraphQLInterfaceType) entityProcessor.getEntity(genericMeta).getInnerType(genericMeta); + addInterface(graphType, interfaceBuilder, interfaceName); } } parent = parent.getSuperclass(); @@ -112,6 +98,9 @@ public GraphQLNamedOutputType buildType() throws ReflectiveOperationException { innerMeta = new TypeMeta(null, type, type); if (!EntityUtil.getName(innerMeta).equals(typeName)) { var interfaceName = entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); + if ("RowGroupUpdate_MiscellaneousRowGroupItem_MiscellaneousRowGroup".equals(interfaceName.getName())) { + System.out.println("hello"); + } graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); } @@ -165,6 +154,15 @@ public GraphQLNamedOutputType buildType() throws ReflectiveOperationException { return built; } + private void addInterface(Builder graphType, GraphQLInterfaceType.Builder interfaceBuilder, GraphQLInterfaceType interfaceName) { + graphType.withInterface(interfaceName); + for (var inner : interfaceName.getInterfaces()) { + graphType.withInterface(GraphQLTypeReference.typeRef(inner.getName())); + interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(inner.getName())); + } + interfaceBuilder.withInterface(interfaceName); + } + protected abstract void processFields(String typeName, Builder graphType, graphql.schema.GraphQLInterfaceType.Builder interfaceBuilder) throws ReflectiveOperationException; diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java index 710180c..f56d89d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import java.lang.reflect.ParameterizedType; diff --git a/src/main/java/com/fleetpin/graphql/builder/UnionType.java b/src/main/java/com/fleetpin/graphql/builder/UnionType.java index a8974fa..c25ccc6 100644 --- a/src/main/java/com/fleetpin/graphql/builder/UnionType.java +++ b/src/main/java/com/fleetpin/graphql/builder/UnionType.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Union; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java index 6f10e36..0ebe5d7 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Context.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -30,6 +18,6 @@ import java.lang.annotation.Target; @Retention(RUNTIME) -@Target(ElementType.TYPE) +@Target({ ElementType.TYPE, ElementType.PARAMETER }) public @interface Context { } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java index 7228da8..317f775 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java index ce14c96..32184cf 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Entity.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLCreator.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLCreator.java new file mode 100644 index 0000000..e70af41 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLCreator.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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target({ ElementType.CONSTRUCTOR }) +public @interface GraphQLCreator { +} diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java index 895b481..49666f7 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDeprecated.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java index e7fdca4..d6d7dda 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java index 1824605..e51c4ae 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLIgnore.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java index 2b6f1ed..c1fe376 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Id.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java b/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java index 92c0894..23362b3 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/InputIgnore.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java index 0c7ce2e..411e98c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Mutation.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java b/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java index fa5338c..a01a0b2 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java index e29dd25..bc5a73b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Query.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java index 761ccb9..b577f50 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Restrict.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java index 0f53bea..dff1c64 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Restricts.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java index d8b3ac7..3902422 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Scalar.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java b/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java index cddd845..391851b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/SchemaOption.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; public enum SchemaOption { diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java index e51d9fb..f661705 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Subscription.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java index f774c13..d373c92 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Union.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/ConstructorFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/ConstructorFieldBuilder.java new file mode 100644 index 0000000..e2a586f --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/ConstructorFieldBuilder.java @@ -0,0 +1,70 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.mapper; + +import graphql.GraphQLContext; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Map; + +public class ConstructorFieldBuilder implements InputTypeBuilder { + + private final InputTypeBuilder map; + + public ConstructorFieldBuilder(Class type, ArrayList mappers) { + try { + var argTypes = mappers.stream().map(t -> t.type).toArray(Class[]::new); + var constructor = type.getDeclaredConstructor(argTypes); + constructor.setAccessible(true); + map = + (obj, context, locale) -> { + try { + Map map = (Map) obj; + + var args = new Object[argTypes.length]; + + for (int i = 0; i < args.length; i++) { + var mapper = mappers.get(i); + + if (map.containsKey(mapper.name)) { + args[i] = mapper.resolver.convert(map.get(mapper.name), context, locale); + } + } + + return constructor.newInstance(args); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object convert(Object obj, GraphQLContext graphQLContext, Locale locale) { + return map.convert(obj, graphQLContext, locale); + } + + public static class RecordMapper { + + private final String name; + private final Class type; + private final InputTypeBuilder resolver; + + public RecordMapper(String name, Class type, InputTypeBuilder resolver) { + this.name = name; + this.type = type; + this.resolver = resolver; + } + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java index 63b5181..0554a41 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/InputTypeBuilder.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.mapper; import graphql.GraphQLContext; diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java index eb068b7..9224df5 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.mapper; import com.fleetpin.graphql.builder.EntityProcessor; diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java index 928963d..c45d964 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.mapper; import com.fleetpin.graphql.builder.EntityProcessor; diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java deleted file mode 100644 index 99474d6..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/RecordFieldBuilder.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 limitations under - * the License. - */ -/* - * 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 limitations under - * the License. - */ - -package com.fleetpin.graphql.builder.mapper; - -import graphql.GraphQLContext; -import java.util.ArrayList; -import java.util.Locale; -import java.util.Map; - -public class RecordFieldBuilder implements InputTypeBuilder { - - private final InputTypeBuilder map; - - public RecordFieldBuilder(Class type, ArrayList mappers) throws ReflectiveOperationException { - var argTypes = mappers.stream().map(t -> t.type).toArray(Class[]::new); - - var constructor = type.getConstructor(argTypes); - map = - (obj, context, locale) -> { - try { - Map map = (Map) obj; - - var args = new Object[argTypes.length]; - - for (int i = 0; i < args.length; i++) { - var mapper = mappers.get(i); - - if (map.containsKey(mapper.name)) { - args[i] = mapper.resolver.convert(map.get(mapper.name), context, locale); - } - } - - return constructor.newInstance(args); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - }; - } - - @Override - public Object convert(Object obj, GraphQLContext graphQLContext, Locale locale) { - return map.convert(obj, graphQLContext, locale); - } - - public static class RecordMapper { - - private final String name; - private final Class type; - private final InputTypeBuilder resolver; - - public RecordMapper(String name, Class type, InputTypeBuilder resolver) { - this.name = name; - this.type = type; - this.resolver = resolver; - } - } -} diff --git a/src/test/java/com/fleetpin/graphql/builder/ContextTest.java b/src/test/java/com/fleetpin/graphql/builder/ContextTest.java new file mode 100644 index 0000000..a6fb735 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/ContextTest.java @@ -0,0 +1,83 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fleetpin.graphql.builder.annotations.Context; +import com.fleetpin.graphql.builder.annotations.Query; +import com.fleetpin.graphql.builder.context.GraphContext; +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; +import graphql.introspection.IntrospectionWithDirectivesSupport; +import java.util.Map; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; + +public class ContextTest { + + @Test + public void testEntireContext() throws ReflectiveOperationException { + Map response = execute("query {entireContext} ", __ -> {}).getData(); + assertTrue(response.get("entireContext")); + } + + @Test + public void testEnv() throws ReflectiveOperationException { + Map response = execute("query {env} ", __ -> {}).getData(); + assertTrue(response.get("env")); + } + + @Test + public void testDeprecated() throws ReflectiveOperationException { + @SuppressWarnings("deprecation") + Map response = execute("query {deprecatedContext} ", b -> b.context(new GraphContext("context"))).getData(); + assertTrue(response.get("deprecatedContext")); + } + + @Test + public void testNamed() throws ReflectiveOperationException { + Map response = execute("query {namedContext} ", b -> b.graphQLContext(c -> c.of("named", new GraphContext("context")))).getData(); + assertTrue(response.get("namedContext")); + } + + @Test + public void testNamedParameter() throws ReflectiveOperationException { + Map response = execute("query {namedParemeterContext} ", b -> b.graphQLContext(c -> c.of("context", "test"))).getData(); + assertTrue(response.get("namedParemeterContext")); + } + + @Test + public void testMissing() throws ReflectiveOperationException { + var response = execute("query {missingContext} ", b -> b.graphQLContext(c -> c.of("context", "test"))); + assertEquals(1, response.getErrors().size()); + var error = response.getErrors().get(0); + assertTrue(error.getMessage().contains("Context object notPresent not found")); + } + + private ExecutionResult execute(String query, Consumer modify) { + try { + GraphQL schema = GraphQL + .newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.context"))) + .build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + modify.accept(input); + ExecutionResult result = schema.execute(input); + return result; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java index 7abcc46..3e56fd8 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java index 2678b32..22f1c94 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java b/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java index 6ea293d..9d137f9 100644 --- a/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java +++ b/src/test/java/com/fleetpin/graphql/builder/PublishRestrictions.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java new file mode 100644 index 0000000..0e7ccb0 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java @@ -0,0 +1,54 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; +import graphql.introspection.IntrospectionWithDirectivesSupport; +import java.util.Map; +import org.junit.jupiter.api.Test; + +//does not test all of records as needs newer version of java. But Classes that look like records +public class RecordTest { + + @Test + public void testEntireContext() { + var type = Map.of("name", "foo", "age", 4); + Map> response = execute( + "query passthrough($type: InputTypeInput!){passthrough(type: $type) {name age}} ", + Map.of("type", type) + ) + .getData(); + var passthrough = response.get("passthrough"); + assertEquals(type, passthrough); + } + + private ExecutionResult execute(String query, Map variables) { + try { + GraphQL schema = GraphQL + .newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.record"))) + .build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); + } + ExecutionResult result = schema.execute(input); + return result; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java b/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java index f171363..cbe2535 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java @@ -19,6 +19,7 @@ import graphql.ExecutionResult; import graphql.GraphQL; import graphql.introspection.IntrospectionWithDirectivesSupport; +import graphql.scalars.ExtendedScalars; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; @@ -68,12 +69,17 @@ public Map getField(String typeName, String kind) throws Reflect @Test public void testQueryCatFur() throws ReflectiveOperationException { - Map> response = execute("query fur($fur: Fur!){getCat(fur: $fur){ fur}} ", Map.of("fur", "long")).getData(); + Map> response = execute( + "query fur($fur: Fur!, $age: Long!){getCat(fur: $fur, age: $age){ fur age}} ", + Map.of("fur", "long", "age", 2) + ) + .getData(); var cat = response.get("getCat"); var fur = cat.get("fur"); assertEquals("long", fur.getInput()); + assertEquals(2L, cat.get("age")); } @Test @@ -91,7 +97,10 @@ private ExecutionResult execute(String query) { private ExecutionResult execute(String query, Map variables) { try { GraphQL schema = GraphQL - .newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.scalar"))) + .newGraphQL( + new IntrospectionWithDirectivesSupport() + .apply(SchemaBuilder.builder().classpath("com.fleetpin.graphql.builder.scalar").scalar(ExtendedScalars.GraphQLLong).build().build()) + ) .build(); var input = ExecutionInput.newExecutionInput(); input.query(query); diff --git a/src/test/java/com/fleetpin/graphql/builder/context/GraphContext.java b/src/test/java/com/fleetpin/graphql/builder/context/GraphContext.java new file mode 100644 index 0000000..22101ac --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/context/GraphContext.java @@ -0,0 +1,29 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.context; + +import com.fleetpin.graphql.builder.annotations.Context; + +@Context +public class GraphContext { + + private final String something; + + public GraphContext(String something) { + super(); + this.something = something; + } + + public String getSomething() { + return something; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/context/Queries.java b/src/test/java/com/fleetpin/graphql/builder/context/Queries.java new file mode 100644 index 0000000..f4aefff --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/context/Queries.java @@ -0,0 +1,50 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.context; + +import com.fleetpin.graphql.builder.annotations.Context; +import com.fleetpin.graphql.builder.annotations.Query; +import graphql.GraphQLContext; +import graphql.schema.DataFetchingEnvironment; + +public class Queries { + + @Query + public static boolean entireContext(GraphQLContext context) { + return context != null; + } + + @Query + public static boolean env(DataFetchingEnvironment context) { + return context != null; + } + + @Query + public static boolean deprecatedContext(GraphContext context) { + return context != null; + } + + @Query + public static boolean namedContext(GraphContext named) { + return named != null; + } + + @Query + public static boolean namedParemeterContext(@Context String context) { + return context != null; + } + + @Query + public static boolean missingContext(GraphContext notPresent) { + return false; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java b/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java index d8cf1aa..78c435b 100644 --- a/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java +++ b/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.parameter; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java b/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java index 23dacbd..3b9f454 100644 --- a/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java +++ b/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.parameter; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java new file mode 100644 index 0000000..3bfbf4c --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java @@ -0,0 +1,42 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.record; + +import com.fleetpin.graphql.builder.annotations.Query; + +public class Queries { + + @Query + public static InputType passthrough(InputType type) { + return type; + } + + static final class InputType { + + private final String name; + private final int age; + + private InputType(String name, int age) { + super(); + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java index bbb5528..3d14875 100644 --- a/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntity.java @@ -9,18 +9,6 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -/* - * 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 limitations under - * the License. - */ - package com.fleetpin.graphql.builder.restrictions.parameter; import com.fleetpin.graphql.builder.annotations.Entity; diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/Cat.java b/src/test/java/com/fleetpin/graphql/builder/scalar/Cat.java index 8373373..fcbdc9d 100644 --- a/src/test/java/com/fleetpin/graphql/builder/scalar/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/Cat.java @@ -19,17 +19,24 @@ public class Cat { private final Fur fur; - private Cat(Fur fur) { + private long age; + + private Cat(Fur fur, long age) { this.fur = fur; + this.age = age; } public Fur getFur() { return fur; } + public long getAge() { + return age; + } + @Query - public static Cat getCat(Fur fur) { - return new Cat(fur); + public static Cat getCat(Fur fur, Long age) { + return new Cat(fur, age); } @Query From 1ca1768ca0e42327990c1f651d701dcd46492ccb Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Mon, 6 Mar 2023 16:56:49 +1300 Subject: [PATCH 004/112] fix incrementing bug --- src/main/java/com/fleetpin/graphql/builder/EntityHolder.java | 2 +- src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java index ea1f430..0ba3510 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java @@ -237,7 +237,7 @@ private static InputTypeBuilder processArray(Class type, Iterator> i Object[] toReturn = (Object[]) Array.newInstance(component, collection.size()); int i = 0; for (var c : collection) { - toReturn[i] = mapper.convert(c, context, locale); + toReturn[i++] = mapper.convert(c, context, locale); } return toReturn; } else { diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java index 26c5888..5de085c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java @@ -98,9 +98,6 @@ public GraphQLNamedOutputType buildType() throws ReflectiveOperationException { innerMeta = new TypeMeta(null, type, type); if (!EntityUtil.getName(innerMeta).equals(typeName)) { var interfaceName = entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); - if ("RowGroupUpdate_MiscellaneousRowGroupItem_MiscellaneousRowGroup".equals(interfaceName.getName())) { - System.out.println("hello"); - } graphType.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); interfaceBuilder.withInterface(GraphQLTypeReference.typeRef(interfaceName.getName())); } From 07944c80aeffdf009408e4c870bc07bfc93bd6be Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Tue, 7 Mar 2023 16:26:08 +1300 Subject: [PATCH 005/112] more refactoring and add more tests --- .../graphql/builder/EntityProcessor.java | 22 +- .../fleetpin/graphql/builder/EntityUtil.java | 17 +- .../graphql/builder/MethodProcessor.java | 205 +++++++++++++ .../graphql/builder/SchemaBuilder.java | 275 ++++-------------- .../fleetpin/graphql/builder/TypeBuilder.java | 123 +------- .../graphql/builder/AuthorizerTest.java | 18 +- .../fleetpin/graphql/builder/ContextTest.java | 20 +- .../graphql/builder/DirectiveTest.java | 37 +++ .../graphql/builder/ParameterParsingTest.java | 6 + .../fleetpin/graphql/builder/RecordTest.java | 20 +- .../fleetpin/graphql/builder/ScalarTest.java | 34 +-- .../builder/TypeGenericInputParsingTest.java | 14 +- .../builder/TypeGenericParsingTest.java | 14 +- .../builder/TypeInheritanceParsingTest.java | 24 +- .../graphql/builder/methodArgs/Queries.java | 46 +++ .../graphql/builder/methodArgsTest.java | 51 ++++ .../graphql/builder/parameter/Parameter.java | 5 + .../graphql/builder/type/directive/Admin.java | 40 +++ .../graphql/builder/type/directive/Cat.java | 6 + 19 files changed, 546 insertions(+), 431 deletions(-) create mode 100644 src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/methodArgs/Queries.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/methodArgsTest.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java index f30629d..4bf1647 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java @@ -33,17 +33,17 @@ public class EntityProcessor { - private final GraphQLCodeRegistry.Builder codeRegistry; private final DirectivesSchema directives; private final Map entities; + private final MethodProcessor methodProcessor; - EntityProcessor(List scalars, GraphQLCodeRegistry.Builder codeRegistry, DirectivesSchema diretives) { + EntityProcessor(List scalars, DirectivesSchema diretives) { + this.methodProcessor = new MethodProcessor(this, diretives); this.entities = new HashMap<>(); addDefaults(); addScalars(scalars); - this.codeRegistry = codeRegistry; this.directives = diretives; } @@ -163,14 +163,6 @@ void addSchemaDirective(AnnotatedElement element, Class location, Consumer type) { var meta = new TypeMeta(null, type, type); return getEntity(meta).getResolver(meta); } + + GraphQLCodeRegistry.Builder getCodeRegistry() { + return this.methodProcessor.getCodeRegistry(); + } + + MethodProcessor getMethodProcessor() { + return methodProcessor; + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java index 91921e8..2916bd9 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java @@ -11,16 +11,20 @@ */ package com.fleetpin.graphql.builder; +import com.fleetpin.graphql.builder.annotations.Context; import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; import com.fleetpin.graphql.builder.annotations.InputIgnore; import com.fleetpin.graphql.builder.mapper.ObjectFieldBuilder.FieldMapper; +import graphql.GraphQLContext; +import graphql.schema.DataFetchingEnvironment; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.TypeVariable; import java.util.Optional; -public class EntityUtil { +class EntityUtil { private static final Method IS_RECORD_METHOD; @@ -137,4 +141,15 @@ public static Optional setter(Method method) { } return Optional.empty(); } + + static boolean isContext(Class class1, Annotation[] annotations) { + for (var annotation : annotations) { + if (annotation instanceof Context) { + return true; + } + } + return ( + class1.isAssignableFrom(GraphQLContext.class) || class1.isAssignableFrom(DataFetchingEnvironment.class) || class1.isAnnotationPresent(Context.class) + ); + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java new file mode 100644 index 0000000..d7a039a --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -0,0 +1,205 @@ +package com.fleetpin.graphql.builder; + +import static com.fleetpin.graphql.builder.EntityUtil.isContext; + +import com.fleetpin.graphql.builder.annotations.GraphQLDeprecated; +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; +import com.fleetpin.graphql.builder.annotations.Mutation; +import com.fleetpin.graphql.builder.annotations.Query; +import com.fleetpin.graphql.builder.annotations.Subscription; +import graphql.GraphQLContext; +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import graphql.schema.FieldCoordinates; +import graphql.schema.GraphQLArgument; +import graphql.schema.GraphQLCodeRegistry; +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLFieldDefinition.Builder; +import graphql.schema.GraphQLObjectType; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.function.Function; + +class MethodProcessor { + + private final EntityProcessor entityProcessor; + private final DirectivesSchema diretives; + + private final GraphQLCodeRegistry.Builder codeRegistry; + + private final GraphQLObjectType.Builder graphQuery; + private final GraphQLObjectType.Builder graphMutations; + private final GraphQLObjectType.Builder graphSubscriptions; + + public MethodProcessor(EntityProcessor entityProcessor, DirectivesSchema diretives) { + this.entityProcessor = entityProcessor; + this.diretives = diretives; + this.codeRegistry = GraphQLCodeRegistry.newCodeRegistry(); + + this.graphQuery = GraphQLObjectType.newObject(); + graphQuery.name("Query"); + this.graphMutations = GraphQLObjectType.newObject(); + graphMutations.name("Mutations"); + this.graphSubscriptions = GraphQLObjectType.newObject(); + graphSubscriptions.name("Subscriptions"); + } + + void process(AuthorizerSchema authorizer, Method method) throws ReflectiveOperationException { + if (!Modifier.isStatic(method.getModifiers())) { + throw new RuntimeException("End point must be a static method"); + } + FieldCoordinates coordinates; + GraphQLObjectType.Builder object; + if (method.isAnnotationPresent(Query.class)) { + coordinates = FieldCoordinates.coordinates("Query", method.getName()); + object = graphQuery; + } else if (method.isAnnotationPresent(Mutation.class)) { + coordinates = FieldCoordinates.coordinates("Mutations", method.getName()); + object = graphMutations; + } else if (method.isAnnotationPresent(Subscription.class)) { + coordinates = FieldCoordinates.coordinates("Subscriptions", method.getName()); + object = graphSubscriptions; + } else { + return; + } + + object.field(process(authorizer, coordinates, null, method)); + } + + Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeMeta parentMeta, Method method) { + GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); + + entityProcessor.addSchemaDirective(method, method.getDeclaringClass(), field::withAppliedDirective); + + var deprecated = method.getAnnotation(GraphQLDeprecated.class); + if (deprecated != null) { + field.deprecate(deprecated.value()); + } + + var description = method.getAnnotation(GraphQLDescription.class); + if (description != null) { + field.description(description.value()); + } + + field.name(coordinates.getFieldName()); + + TypeMeta meta = new TypeMeta(parentMeta, method.getReturnType(), method.getGenericReturnType()); + var type = entityProcessor.getType(meta, method.getAnnotations()); + field.type(type); + for (int i = 0; i < method.getParameterCount(); i++) { + GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); + if (isContext(method.getParameterTypes()[i], method.getParameterAnnotations()[i])) { + continue; + } + + TypeMeta inputMeta = new TypeMeta(null, method.getParameterTypes()[i], method.getGenericParameterTypes()[i]); + argument.type(entityProcessor.getInputType(inputMeta, method.getParameterAnnotations()[i])); //TODO:dirty cast + argument.name(method.getParameters()[i].getName()); + //TODO: argument.defaultValue(defaultValue) + field.argument(argument); + } + + diretives.addSchemaDirective(method, method.getDeclaringClass(), field::withAppliedDirective); + DataFetcher fetcher = buildFetcher(diretives, authorizer, method, meta); + codeRegistry.dataFetcher(coordinates, fetcher); + return field; + } + + private DataFetcher buildFetcher(DirectivesSchema diretives, AuthorizerSchema authorizer, Method method, TypeMeta meta) { + DataFetcher fetcher = buildDataFetcher(meta, method); + fetcher = diretives.wrap(method, meta, fetcher); + + if (authorizer != null) { + fetcher = authorizer.wrap(fetcher, method); + } + return fetcher; + } + + private DataFetcher buildDataFetcher(TypeMeta meta, Method method) { + Function[] resolvers = new Function[method.getParameterCount()]; + + method.setAccessible(true); + + for (int i = 0; i < resolvers.length; i++) { + Class type = method.getParameterTypes()[i]; + var name = method.getParameters()[i].getName(); + var generic = method.getGenericParameterTypes()[i]; + var argMeta = new TypeMeta(meta, type, generic); + resolvers[i] = buildResolver(name, argMeta, method.getParameterAnnotations()[i]); + } + + DataFetcher fetcher = env -> { + try { + Object[] args = new Object[resolvers.length]; + for (int i = 0; i < resolvers.length; i++) { + args[i] = resolvers[i].apply(env); + } + return method.invoke(env.getSource(), args); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Exception) { + throw (Exception) e.getCause(); + } else { + throw e; + } + } catch (Exception e) { + throw e; + } + }; + + return fetcher; + } + + private Function buildResolver(String name, TypeMeta argMeta, Annotation[] annotations) { + if (isContext(argMeta.getType(), annotations)) { + var type = argMeta.getType(); + if (type.isAssignableFrom(DataFetchingEnvironment.class)) { + return env -> env; + } + if (type.isAssignableFrom(GraphQLContext.class)) { + return env -> env.getGraphQlContext(); + } + return env -> { + var localContext = env.getLocalContext(); + if (localContext != null && type.isAssignableFrom(localContext.getClass())) { + return localContext; + } + + var context = env.getContext(); + if (context != null && type.isAssignableFrom(context.getClass())) { + return context; + } + + context = env.getGraphQlContext().get(name); + if (context != null && type.isAssignableFrom(context.getClass())) { + return context; + } + throw new RuntimeException("Context object " + name + " not found"); + }; + } + + var resolver = entityProcessor.getResolver(argMeta); + + return env -> { + var arg = env.getArgument(name); + return resolver.convert(arg, env.getGraphQlContext(), env.getLocale()); + }; + } + + public GraphQLCodeRegistry.Builder getCodeRegistry() { + return codeRegistry; + } + + GraphQLObjectType.Builder getGraphQuery() { + return graphQuery; + } + + GraphQLObjectType.Builder getGraphMutations() { + return graphMutations; + } + + GraphQLObjectType.Builder getGraphSubscriptions() { + return graphSubscriptions; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index 07c442e..d4cbbb1 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -11,36 +11,21 @@ */ package com.fleetpin.graphql.builder; -import com.fleetpin.graphql.builder.annotations.Context; import com.fleetpin.graphql.builder.annotations.Directive; import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.GraphQLDeprecated; -import com.fleetpin.graphql.builder.annotations.GraphQLDescription; import com.fleetpin.graphql.builder.annotations.Mutation; import com.fleetpin.graphql.builder.annotations.Query; import com.fleetpin.graphql.builder.annotations.Restrict; import com.fleetpin.graphql.builder.annotations.Restricts; import com.fleetpin.graphql.builder.annotations.SchemaOption; import com.fleetpin.graphql.builder.annotations.Subscription; -import graphql.GraphQLContext; -import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; -import graphql.schema.FieldCoordinates; -import graphql.schema.GraphQLArgument; -import graphql.schema.GraphQLCodeRegistry; -import graphql.schema.GraphQLFieldDefinition; -import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLScalarType; import graphql.schema.GraphQLSchema; -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Function; import org.reflections.Reflections; import org.reflections.scanners.Scanners; @@ -49,85 +34,17 @@ public class SchemaBuilder { private final DirectivesSchema diretives; private final AuthorizerSchema authorizer; - private final GraphQLCodeRegistry.Builder codeRegistry; - - private final GraphQLObjectType.Builder graphQuery; - private final GraphQLObjectType.Builder graphMutations; - private final GraphQLObjectType.Builder graphSubscriptions; - private final EntityProcessor entityProcessor; private SchemaBuilder(List scalars, DirectivesSchema diretives, AuthorizerSchema authorizer) { this.diretives = diretives; this.authorizer = authorizer; - this.graphQuery = GraphQLObjectType.newObject(); - graphQuery.name("Query"); - this.graphMutations = GraphQLObjectType.newObject(); - graphMutations.name("Mutations"); - this.graphSubscriptions = GraphQLObjectType.newObject(); - graphSubscriptions.name("Subscriptions"); - this.codeRegistry = GraphQLCodeRegistry.newCodeRegistry(); - - this.entityProcessor = new EntityProcessor(scalars, codeRegistry, diretives); + this.entityProcessor = new EntityProcessor(scalars, diretives); diretives.processSDL(entityProcessor); } - private SchemaBuilder process(Set endPoints) throws ReflectiveOperationException { - for (var method : endPoints) { - if (!Modifier.isStatic(method.getModifiers())) { - throw new RuntimeException("End point must be a static method"); - } - //TODO:query vs mutation - GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); - - var deprecated = method.getAnnotation(GraphQLDeprecated.class); - if (deprecated != null) { - field.deprecate(deprecated.value()); - } - - var description = method.getAnnotation(GraphQLDescription.class); - if (description != null) { - field.description(description.value()); - } - - field.name(method.getName()); - - TypeMeta meta = new TypeMeta(null, method.getReturnType(), method.getGenericReturnType()); - var type = entityProcessor.getType(meta, method.getAnnotations()); - field.type(type); - for (int i = 0; i < method.getParameterCount(); i++) { - GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); - if (isContext(method.getParameterTypes()[i], method.getParameterAnnotations()[i])) { - continue; - } - - TypeMeta inputMeta = new TypeMeta(null, method.getParameterTypes()[i], method.getGenericParameterTypes()[i]); - argument.type(entityProcessor.getInputType(inputMeta, method.getParameterAnnotations()[i])); //TODO:dirty cast - argument.name(method.getParameters()[i].getName()); - //TODO: argument.defaultValue(defaultValue) - field.argument(argument); - } - - diretives.addSchemaDirective(method, method.getDeclaringClass(), field::withAppliedDirective); - if (method.isAnnotationPresent(Query.class)) { - graphQuery.field(field); - DataFetcher fetcher = buildFetcher(diretives, authorizer, method, meta); - codeRegistry.dataFetcher(graphQuery.build(), field.build(), fetcher); - } else if (method.isAnnotationPresent(Mutation.class)) { - graphMutations.field(field); - DataFetcher fetcher = buildFetcher(diretives, authorizer, method, meta); - codeRegistry.dataFetcher(FieldCoordinates.coordinates("Mutations", method.getName()), fetcher); - } else if (method.isAnnotationPresent(Subscription.class)) { - graphSubscriptions.field(field); - DataFetcher fetcher = buildFetcher(diretives, authorizer, method, meta); - codeRegistry.dataFetcher(FieldCoordinates.coordinates("Subscriptions", method.getName()), fetcher); - } - } - return this; - } - private SchemaBuilder processTypes(Set> types) { for (var type : types) { TypeMeta meta = new TypeMeta(null, type, type); @@ -140,17 +57,28 @@ private SchemaBuilder processTypes(Set> types) { return this; } + private SchemaBuilder process(HashSet endPoints) throws ReflectiveOperationException { + var methodProcessor = this.entityProcessor.getMethodProcessor(); + for (var method : endPoints) { + methodProcessor.process(authorizer, method); + } + + return this; + } + private graphql.schema.GraphQLSchema.Builder build(Set> schemaConfiguration) { - var builder = GraphQLSchema.newSchema().codeRegistry(codeRegistry.build()).additionalTypes(entityProcessor.getAdditionalTypes()); + var methods = entityProcessor.getMethodProcessor(); - var query = graphQuery.build(); + var builder = GraphQLSchema.newSchema().codeRegistry(methods.getCodeRegistry().build()).additionalTypes(entityProcessor.getAdditionalTypes()); + + var query = methods.getGraphQuery().build(); builder.query(query); - var mutations = graphMutations.build(); + var mutations = methods.getGraphMutations().build(); if (!mutations.getFields().isEmpty()) { builder.mutation(mutations); } - var subscriptions = graphSubscriptions.build(); + var subscriptions = methods.getGraphSubscriptions().build(); if (!subscriptions.getFields().isEmpty()) { builder.subscription(subscriptions); } @@ -163,100 +91,11 @@ private graphql.schema.GraphQLSchema.Builder build(Set class1, Annotation[] annotations) { - for (var annotation : annotations) { - if (annotation instanceof Context) { - return true; - } - } - return ( - class1.isAssignableFrom(GraphQLContext.class) || class1.isAssignableFrom(DataFetchingEnvironment.class) || class1.isAnnotationPresent(Context.class) - ); - } - - private DataFetcher buildFetcher(DirectivesSchema diretives, AuthorizerSchema authorizer, Method method, TypeMeta meta) { - DataFetcher fetcher = buildDataFetcher(meta, method); - fetcher = diretives.wrap(method, meta, fetcher); - - if (authorizer != null) { - fetcher = authorizer.wrap(fetcher, method); - } - return fetcher; - } - - private DataFetcher buildDataFetcher(TypeMeta meta, Method method) { - Function[] resolvers = new Function[method.getParameterCount()]; - - for (int i = 0; i < resolvers.length; i++) { - Class type = method.getParameterTypes()[i]; - var name = method.getParameters()[i].getName(); - var generic = method.getGenericParameterTypes()[i]; - var argMeta = new TypeMeta(meta, type, generic); - resolvers[i] = buildResolver(name, argMeta, method.getParameterAnnotations()[i]); - } - - DataFetcher fetcher = env -> { - try { - Object[] args = new Object[resolvers.length]; - for (int i = 0; i < resolvers.length; i++) { - args[i] = resolvers[i].apply(env); - } - return method.invoke(null, args); - } catch (InvocationTargetException e) { - if (e.getCause() instanceof Exception) { - throw (Exception) e.getCause(); - } else { - throw e; - } - } catch (Exception e) { - throw e; - } - }; - - return fetcher; - } - - private Function buildResolver(String name, TypeMeta argMeta, Annotation[] annotations) { - if (isContext(argMeta.getType(), annotations)) { - var type = argMeta.getType(); - if (type.isAssignableFrom(DataFetchingEnvironment.class)) { - return env -> env; - } - if (type.isAssignableFrom(GraphQLContext.class)) { - return env -> env.getGraphQlContext(); - } - return env -> { - var localContext = env.getLocalContext(); - if (localContext != null && type.isAssignableFrom(localContext.getClass())) { - return localContext; - } - - var context = env.getContext(); - if (context != null && type.isAssignableFrom(context.getClass())) { - return context; - } - - context = env.getGraphQlContext().get(name); - if (context != null && type.isAssignableFrom(context.getClass())) { - return context; - } - throw new RuntimeException("Context object " + name + " not found"); - }; - } - - var resolver = entityProcessor.getResolver(argMeta); - - return env -> { - var arg = env.getArgument(name); - return resolver.convert(arg, env.getGraphQlContext(), env.getLocale()); - }; - } - - public static GraphQLSchema build(String... classPath) throws ReflectiveOperationException { + public static GraphQLSchema build(String... classPath) { return builder(classPath).build(); } - public static GraphQLSchema.Builder builder(String... classpath) throws ReflectiveOperationException { + public static GraphQLSchema.Builder builder(String... classpath) { var builder = builder(); for (var path : classpath) { builder.classpath(path); @@ -285,36 +124,25 @@ public Builder scalar(GraphQLScalarType scalar) { return this; } - public GraphQLSchema.Builder build() throws ReflectiveOperationException { - Reflections reflections = new Reflections(classpaths, Scanners.SubTypes, Scanners.MethodsAnnotated, Scanners.TypesAnnotated); - Set> authorizers = reflections.getSubTypesOf(Authorizer.class); - //want to make everything split by package - AuthorizerSchema authorizer = AuthorizerSchema.build(new HashSet<>(classpaths), authorizers); - - Set> schemaConfiguration = reflections.getSubTypesOf(SchemaConfiguration.class); + public GraphQLSchema.Builder build() { + try { + Reflections reflections = new Reflections(classpaths, Scanners.SubTypes, Scanners.MethodsAnnotated, Scanners.TypesAnnotated); + Set> authorizers = reflections.getSubTypesOf(Authorizer.class); + //want to make everything split by package + AuthorizerSchema authorizer = AuthorizerSchema.build(new HashSet<>(classpaths), authorizers); - Set> dierctivesTypes = reflections.getTypesAnnotatedWith(Directive.class); + Set> schemaConfiguration = reflections.getSubTypesOf(SchemaConfiguration.class); - Set> restrict = reflections.getTypesAnnotatedWith(Restrict.class); - Set> restricts = reflections.getTypesAnnotatedWith(Restricts.class); - List> globalRestricts = new ArrayList<>(); + Set> dierctivesTypes = reflections.getTypesAnnotatedWith(Directive.class); - for (var r : restrict) { - Restrict annotation = r.getAnnotation(Restrict.class); - var factoryClass = annotation.value(); - var factory = factoryClass.getConstructor().newInstance(); - if (!factory.extractType().isAssignableFrom(r)) { - throw new RuntimeException("Restrict annotation does match class applied to targets" + factory.extractType() + " but was on class " + r); - } - globalRestricts.add(factory); - } + Set> restrict = reflections.getTypesAnnotatedWith(Restrict.class); + Set> restricts = reflections.getTypesAnnotatedWith(Restricts.class); + List> globalRestricts = new ArrayList<>(); - for (var r : restricts) { - Restricts annotations = r.getAnnotation(Restricts.class); - for (Restrict annotation : annotations.value()) { + for (var r : restrict) { + Restrict annotation = r.getAnnotation(Restrict.class); var factoryClass = annotation.value(); var factory = factoryClass.getConstructor().newInstance(); - if (!factory.extractType().isAssignableFrom(r)) { throw new RuntimeException( "Restrict annotation does match class applied to targets" + factory.extractType() + " but was on class " + r @@ -322,24 +150,41 @@ public GraphQLSchema.Builder build() throws ReflectiveOperationException { } globalRestricts.add(factory); } - } - DirectivesSchema diretivesSchema = DirectivesSchema.build(globalRestricts, dierctivesTypes); + for (var r : restricts) { + Restricts annotations = r.getAnnotation(Restricts.class); + for (Restrict annotation : annotations.value()) { + var factoryClass = annotation.value(); + var factory = factoryClass.getConstructor().newInstance(); + + if (!factory.extractType().isAssignableFrom(r)) { + throw new RuntimeException( + "Restrict annotation does match class applied to targets" + factory.extractType() + " but was on class " + r + ); + } + globalRestricts.add(factory); + } + } - Set> types = reflections.getTypesAnnotatedWith(Entity.class); + DirectivesSchema diretivesSchema = DirectivesSchema.build(globalRestricts, dierctivesTypes); - var mutations = reflections.getMethodsAnnotatedWith(Mutation.class); - var subscriptions = reflections.getMethodsAnnotatedWith(Subscription.class); - var queries = reflections.getMethodsAnnotatedWith(Query.class); + Set> types = reflections.getTypesAnnotatedWith(Entity.class); - var endPoints = new HashSet<>(mutations); - endPoints.addAll(subscriptions); - endPoints.addAll(queries); + var mutations = reflections.getMethodsAnnotatedWith(Mutation.class); + var subscriptions = reflections.getMethodsAnnotatedWith(Subscription.class); + var queries = reflections.getMethodsAnnotatedWith(Query.class); - types.removeIf(t -> t.getDeclaredAnnotation(Entity.class) == null); - types.removeIf(t -> t.isAnonymousClass()); + var endPoints = new HashSet<>(mutations); + endPoints.addAll(subscriptions); + endPoints.addAll(queries); - return new SchemaBuilder(scalars, diretivesSchema, authorizer).processTypes(types).process(endPoints).build(schemaConfiguration); + types.removeIf(t -> t.getDeclaredAnnotation(Entity.class) == null); + types.removeIf(t -> t.isAnonymousClass()); + + return new SchemaBuilder(scalars, diretivesSchema, authorizer).processTypes(types).process(endPoints).build(schemaConfiguration); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } } } } diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java index 5de085c..a2ff1e0 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java @@ -12,22 +12,16 @@ package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.GraphQLDeprecated; import com.fleetpin.graphql.builder.annotations.GraphQLDescription; import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; -import com.fleetpin.graphql.builder.annotations.Scalar; -import graphql.schema.DataFetcher; import graphql.schema.FieldCoordinates; -import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLInterfaceType; import graphql.schema.GraphQLNamedOutputType; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLObjectType.Builder; import graphql.schema.GraphQLTypeReference; -import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.TypeVariable; public abstract class TypeBuilder { @@ -163,68 +157,6 @@ private void addInterface(Builder graphType, GraphQLInterfaceType.Builder interf protected abstract void processFields(String typeName, Builder graphType, graphql.schema.GraphQLInterfaceType.Builder interfaceBuilder) throws ReflectiveOperationException; - private static DataFetcher buildDirectiveWrapper(DirectivesSchema diretives, Method method, TypeMeta meta) { - DataFetcher fetcher = env -> { - Object[] args = new Object[method.getParameterCount()]; - for (int i = 0; i < args.length; i++) { - if (method.getParameterTypes()[i].isAssignableFrom(env.getClass())) { - args[i] = env; - } else if (method.getParameterTypes()[i].isAssignableFrom(env.getContext().getClass())) { - args[i] = env.getContext(); - } else { - Object obj = env.getArgument(method.getParameters()[i].getName()); - args[i] = obj; - } - } - try { - return method.invoke(env.getSource(), args); - } catch (Exception e) { - if (e.getCause() instanceof Exception) { - throw (Exception) e.getCause(); - } else { - throw e; - } - } - }; - - fetcher = diretives.wrap(method, meta, fetcher); - return fetcher; - } - - private String typeNameLookup(Object obj) { - var type = obj.getClass(); - String name = null; - - if (type.isEnum()) { - name = type.getSimpleName(); - } - if (type.isAnnotationPresent(Scalar.class)) { - name = type.getSimpleName(); - } - if (type.isAnnotationPresent(Entity.class)) { - name = type.getSimpleName(); - } - - for (var t : type.getTypeParameters()) { - for (var method : type.getMethods()) { - var methodType = method.getGenericReturnType(); - if (methodType instanceof TypeVariable) { - var typeVariable = ((TypeVariable) methodType); - if (typeVariable.equals(t)) { - //maybe we should dig through private fields first - try { - name += "_" + typeNameLookup(method.invoke(obj)); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Could not infer type with regard to generics."); - } //TODO: might have arguments might be a future which would make impossible to resolve - } - } - } - } - - return name; - } - public static class ObjectType extends TypeBuilder { public ObjectType(EntityProcessor entityProcessor, TypeMeta meta) { @@ -241,31 +173,9 @@ protected void processFields(String typeName, Builder graphType, graphql.schema. if (name.isEmpty()) { continue; } - - GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); - field.name(name.get()); - entityProcessor.addSchemaDirective(method, type, field::withAppliedDirective); - var deprecated = method.getAnnotation(GraphQLDeprecated.class); - if (deprecated != null) { - field.deprecate(deprecated.value()); - } - var description = method.getAnnotation(GraphQLDescription.class); - if (description != null) { - field.description(description.value()); - } - - TypeMeta innerMeta = new TypeMeta(meta, method.getReturnType(), method.getGenericReturnType()); - - field.type(entityProcessor.getType(innerMeta, method.getAnnotations())); - graphType.field(field); - interfaceBuilder.field(field); - - var directives = entityProcessor.getDirectives(); - var codeRegistry = entityProcessor.getCodeRegistry(); - - if (method.getParameterCount() > 0 || directives.target(method, innerMeta)) { - codeRegistry.dataFetcher(FieldCoordinates.coordinates(typeName, name.get()), buildDirectiveWrapper(directives, method, innerMeta)); - } + var f = entityProcessor.getMethodProcessor().process(null, FieldCoordinates.coordinates(typeName, name.get()), meta, method); + graphType.field(f); + interfaceBuilder.field(f); } catch (RuntimeException e) { throw new RuntimeException("Failed to process method " + method, e); } @@ -309,30 +219,9 @@ protected void processFields(String typeName, Builder graphType, graphql.schema. //getter type String name = field.getName(); - GraphQLFieldDefinition.Builder fieldBuilder = GraphQLFieldDefinition.newFieldDefinition(); - fieldBuilder.name(name); - entityProcessor.addSchemaDirective(method, type, fieldBuilder::withAppliedDirective); - var deprecated = field.getAnnotation(GraphQLDeprecated.class); - if (deprecated != null) { - fieldBuilder.deprecate(deprecated.value()); - } - var description = field.getAnnotation(GraphQLDescription.class); - if (description != null) { - fieldBuilder.description(description.value()); - } - - TypeMeta innerMeta = new TypeMeta(meta, method.getReturnType(), method.getGenericReturnType()); - fieldBuilder.type(entityProcessor.getType(innerMeta, method.getAnnotations())); - graphType.field(fieldBuilder); - interfaceBuilder.field(fieldBuilder); - - var directives = entityProcessor.getDirectives(); - - if (method.getParameterCount() > 0 || directives.target(method, innerMeta)) { - entityProcessor - .getCodeRegistry() - .dataFetcher(FieldCoordinates.coordinates(typeName, name), buildDirectiveWrapper(directives, method, innerMeta)); - } + var f = entityProcessor.getMethodProcessor().process(null, FieldCoordinates.coordinates(typeName, name), meta, method); + graphType.field(f); + interfaceBuilder.field(f); } } catch (RuntimeException e) { throw new RuntimeException("Failed to process method " + field, e); diff --git a/src/test/java/com/fleetpin/graphql/builder/AuthorizerTest.java b/src/test/java/com/fleetpin/graphql/builder/AuthorizerTest.java index 8516418..1166304 100644 --- a/src/test/java/com/fleetpin/graphql/builder/AuthorizerTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/AuthorizerTest.java @@ -53,17 +53,13 @@ private ExecutionResult execute(String query) { } private ExecutionResult execute(String query, Map variables) { - try { - GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.authorizer")).build(); - var input = ExecutionInput.newExecutionInput(); - input.query(query); - if (variables != null) { - input.variables(variables); - } - ExecutionResult result = schema.execute(input); - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.authorizer")).build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); } + ExecutionResult result = schema.execute(input); + return result; } } diff --git a/src/test/java/com/fleetpin/graphql/builder/ContextTest.java b/src/test/java/com/fleetpin/graphql/builder/ContextTest.java index a6fb735..e6e56e6 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ContextTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ContextTest.java @@ -67,17 +67,13 @@ public void testMissing() throws ReflectiveOperationException { } private ExecutionResult execute(String query, Consumer modify) { - try { - GraphQL schema = GraphQL - .newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.context"))) - .build(); - var input = ExecutionInput.newExecutionInput(); - input.query(query); - modify.accept(input); - ExecutionResult result = schema.execute(input); - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + GraphQL schema = GraphQL + .newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.context"))) + .build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + modify.accept(input); + ExecutionResult result = schema.execute(input); + return result; } } diff --git a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java index 961f1f7..1d0ba67 100644 --- a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java @@ -12,9 +12,16 @@ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import graphql.ExecutionInput; +import graphql.ExecutionResult; import graphql.GraphQL; +import graphql.introspection.IntrospectionWithDirectivesSupport; +import graphql.scalars.ExtendedScalars; import graphql.schema.FieldCoordinates; +import java.util.Map; import org.junit.jupiter.api.Test; public class DirectiveTest { @@ -37,4 +44,34 @@ public void testPresentOnSchema() throws ReflectiveOperationException { var color = argument.getValue(); assertEquals("top", color); } + + @Test + public void testDirectivePass() throws ReflectiveOperationException { + Map response = execute("query allowed($name: String!){allowed(name: $name)} ", Map.of("name", "tabby")).getData(); + assertEquals("tabby", response.get("allowed")); + } + + @Test + public void testDirectiveFail() throws ReflectiveOperationException { + var response = execute("query allowed($name: String!){allowed(name: $name)} ", Map.of("name", "calico")); + + assertNull(response.getData()); + + assertTrue(response.getErrors().get(0).getMessage().contains("forbidden")); + } + + private ExecutionResult execute(String query, Map variables) { + GraphQL schema = GraphQL + .newGraphQL( + new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.builder().classpath("com.fleetpin.graphql.builder.type.directive").build().build()) + ) + .build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); + } + ExecutionResult result = schema.execute(input); + return result; + } } diff --git a/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java index 3e56fd8..a5be89f 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ParameterParsingTest.java @@ -87,6 +87,12 @@ public void testRequiredListString() throws ReflectiveOperationException { assertEquals(Arrays.asList("free"), response.get("requiredListString")); } + @Test + public void testRequiredArrayString() throws ReflectiveOperationException { + Map>> response = execute("query {requiredArrayString(type: [\"free\"])} ").getData(); + assertEquals(Arrays.asList("free"), response.get("requiredArrayString")); + } + @Test public void testOptionalListStringEmpty() throws ReflectiveOperationException { Map>> response = execute("query {optionalListString(type: [])} ").getData(); diff --git a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java index 0e7ccb0..137670d 100644 --- a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java @@ -36,19 +36,13 @@ public void testEntireContext() { } private ExecutionResult execute(String query, Map variables) { - try { - GraphQL schema = GraphQL - .newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.record"))) - .build(); - var input = ExecutionInput.newExecutionInput(); - input.query(query); - if (variables != null) { - input.variables(variables); - } - ExecutionResult result = schema.execute(input); - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); + GraphQL schema = GraphQL.newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.record"))).build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); } + ExecutionResult result = schema.execute(input); + return result; } } diff --git a/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java b/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java index cbe2535..76bd04e 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ScalarTest.java @@ -95,25 +95,21 @@ private ExecutionResult execute(String query) { } private ExecutionResult execute(String query, Map variables) { - try { - GraphQL schema = GraphQL - .newGraphQL( - new IntrospectionWithDirectivesSupport() - .apply(SchemaBuilder.builder().classpath("com.fleetpin.graphql.builder.scalar").scalar(ExtendedScalars.GraphQLLong).build().build()) - ) - .build(); - var input = ExecutionInput.newExecutionInput(); - input.query(query); - if (variables != null) { - input.variables(variables); - } - ExecutionResult result = schema.execute(input); - if (!result.getErrors().isEmpty()) { - throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup - } - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); + GraphQL schema = GraphQL + .newGraphQL( + new IntrospectionWithDirectivesSupport() + .apply(SchemaBuilder.builder().classpath("com.fleetpin.graphql.builder.scalar").scalar(ExtendedScalars.GraphQLLong).build().build()) + ) + .build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); } + ExecutionResult result = schema.execute(input); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup + } + return result; } } diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java index 0e36b8a..b8b0ba8 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputParsingTest.java @@ -195,15 +195,11 @@ public void testQueryCatFurGeneric() throws ReflectiveOperationException { } private ExecutionResult execute(String query) { - try { - GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.inputgenerics")).build(); - ExecutionResult result = schema.execute(query); - if (!result.getErrors().isEmpty()) { - throw new RuntimeException(result.getErrors().toString()); - } - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.inputgenerics")).build(); + ExecutionResult result = schema.execute(query); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); } + return result; } } diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java index fccf72c..4d588ee 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeGenericParsingTest.java @@ -308,15 +308,11 @@ public void testMutationCatFur() throws ReflectiveOperationException { } private ExecutionResult execute(String query) { - try { - GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.generics")).build(); - ExecutionResult result = schema.execute(query); - if (!result.getErrors().isEmpty()) { - throw new RuntimeException(result.getErrors().toString()); - } - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.generics")).build(); + ExecutionResult result = schema.execute(query); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); } + return result; } } diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java index ad1ed37..a598614 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java @@ -420,20 +420,16 @@ private ExecutionResult execute(String query) { } private ExecutionResult execute(String query, Map variables) { - try { - GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type")).build(); - var input = ExecutionInput.newExecutionInput(); - input.query(query); - if (variables != null) { - input.variables(variables); - } - ExecutionResult result = schema.execute(input); - if (!result.getErrors().isEmpty()) { - throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup - } - return result; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type")).build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); + } + ExecutionResult result = schema.execute(input); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup } + return result; } } diff --git a/src/test/java/com/fleetpin/graphql/builder/methodArgs/Queries.java b/src/test/java/com/fleetpin/graphql/builder/methodArgs/Queries.java new file mode 100644 index 0000000..0d3acc5 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/methodArgs/Queries.java @@ -0,0 +1,46 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.methodArgs; + +import com.fleetpin.graphql.builder.annotations.Query; + +public class Queries { + + @Query + public static InputType passthrough(InputType type) { + return type; + } + + static final class InputType { + + private final String name; + private final int age; + + private InputType(String name, int age) { + super(); + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public int getHeight(int height) { + return height; + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/methodArgsTest.java b/src/test/java/com/fleetpin/graphql/builder/methodArgsTest.java new file mode 100644 index 0000000..69ba736 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/methodArgsTest.java @@ -0,0 +1,51 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; +import graphql.introspection.IntrospectionWithDirectivesSupport; +import java.util.Map; +import org.junit.jupiter.api.Test; + +//does not test all of records as needs newer version of java. But Classes that look like records +public class methodArgsTest { + + @Test + public void testEntireContext() { + var type = Map.of("name", "foo", "age", 4); + Map> response = execute( + "query passthrough($type: InputTypeInput!){passthrough(type: $type) {name age height(height: 12)}} ", + Map.of("type", type) + ) + .getData(); + var passthrough = response.get("passthrough"); + + assertEquals(Map.of("name", "foo", "age", 4, "height", 12), passthrough); + } + + private ExecutionResult execute(String query, Map variables) { + GraphQL schema = GraphQL + .newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.methodArgs"))) + .build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); + } + ExecutionResult result = schema.execute(input); + return result; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java b/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java index 78c435b..1480fad 100644 --- a/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java +++ b/src/test/java/com/fleetpin/graphql/builder/parameter/Parameter.java @@ -56,6 +56,11 @@ public static List requiredListString(List type) { return type; } + @Query + public static String[] requiredArrayString(String[] type) { + return type; + } + @Query public static Optional> optionalListString(Optional> type) { return type; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java new file mode 100644 index 0000000..7387f9f --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java @@ -0,0 +1,40 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.type.directive; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.fleetpin.graphql.builder.DirectiveCaller; +import com.fleetpin.graphql.builder.annotations.Directive; +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Directive(Admin.Processor.class) +@Retention(RUNTIME) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface Admin { + String value(); + + static class Processor implements DirectiveCaller { + + @Override + public Object process(Admin annotation, DataFetchingEnvironment env, DataFetcher fetcher) throws Exception { + if (env.getArgument("name").equals(annotation.value())) { + return fetcher.get(env); + } + throw new RuntimeException("forbidden"); + } + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java index a7dea4b..067b7e9 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java @@ -34,4 +34,10 @@ public boolean getFur() { public static Cat getCat() { return new Cat(); } + + @Query + @Admin("tabby") + public static String allowed(String name) { + return name; + } } From cad46a75677a1e36fbf36ed44ee444599331481a Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Wed, 8 Mar 2023 11:18:04 +1300 Subject: [PATCH 006/112] don't add directives twice --- src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java | 1 - src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index d7a039a..378390c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -101,7 +101,6 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM field.argument(argument); } - diretives.addSchemaDirective(method, method.getDeclaringClass(), field::withAppliedDirective); DataFetcher fetcher = buildFetcher(diretives, authorizer, method, meta); codeRegistry.dataFetcher(coordinates, fetcher); return field; diff --git a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java b/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java index ee83e06..57dd39d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java @@ -11,7 +11,6 @@ */ package com.fleetpin.graphql.builder; -import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; import graphql.schema.GraphQLAppliedDirective; import graphql.schema.GraphQLAppliedDirectiveArgument; import graphql.schema.GraphQLArgument; @@ -19,7 +18,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.List; From b4dc9cd2c5567533567341ff20ef2c071f85163c Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Wed, 8 Mar 2023 14:59:22 +1300 Subject: [PATCH 007/112] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 654d3f5..80cf664 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ GraphQL Builder Builds a graphql schema from a model using reflection - https://github.com/fleetpin/graphql-builder + https://github.com/ashley-taylor/graphql-builder 5.6.0 From 110f1d8a11d197bde3e39f2f3a257ebdaf627545 Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Wed, 8 Mar 2023 15:01:49 +1300 Subject: [PATCH 008/112] Update pom.xml --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 80cf664..141aafa 100644 --- a/pom.xml +++ b/pom.xml @@ -44,9 +44,9 @@ - https://github.com/fleetpin/graphql-builder - scm:git:https://github.com/fleetpin/graphql-builder.git - scm:git:https://github.com/fleetpin/graphql-builder.git + https://github.com/ashley-taylor/graphql-builder + scm:git:https://github.com/ashley-taylor/graphql-builder.git + scm:git:https://github.com/ashley-taylor/graphql-builder.git HEAD From 6e0f496da613a09093a7ea22cd723f6066d85559 Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Wed, 8 Mar 2023 16:41:54 +1300 Subject: [PATCH 009/112] Update release.yml --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ecf6987..e4c4b30 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,6 @@ name: Release +permissions: + contents: write on: workflow_dispatch jobs: From f8c3b14f68056441d382a9d82be104b88779b794 Mon Sep 17 00:00:00 2001 From: release-bot Date: Wed, 8 Mar 2023 03:43:09 +0000 Subject: [PATCH 010/112] [maven-release-plugin] prepare release graphql-builder-2.0.0 --- pom.xml | 512 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 256 insertions(+), 256 deletions(-) diff --git a/pom.xml b/pom.xml index 141aafa..093ee02 100644 --- a/pom.xml +++ b/pom.xml @@ -1,256 +1,256 @@ - - - 4.0.0 - com.fleetpin - graphql-builder - 2.0.0-SNAPSHOT - - GraphQL Builder - Builds a graphql schema from a model using reflection - https://github.com/ashley-taylor/graphql-builder - - - 5.6.0 - UTF-8 - 2.14.2 - 1.9.0 - 1.0.0 - 20.0 - - - - - sonatype - central snapshot - https://oss.sonatype.org/content/repositories/snapshots - - - sonatype - central release - https://oss.sonatype.org/service/local/staging/deploy/maven2 - - - - - https://github.com/ashley-taylor/graphql-builder - scm:git:https://github.com/ashley-taylor/graphql-builder.git - scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD - - - - - Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - - Ashley Taylor - ashley.taylor@fleetpin.co.nz - Fleetpin - http://www.fleetpin.co.nz - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - -parameters - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - - com.hubspot.maven.plugins - prettier-maven-plugin - 0.15 - - 1.4.0 - 160 - 4 - true - true - true - - - - validate - - - - - org.pitest - pitest-maven - ${pitest.version} - - - org.pitest - pitest-junit5-plugin - ${pitest-junit5-plugin.version} - - - - 4 - false - false - - STRONGER - - - - - com.mycila - license-maven-plugin - 4.1 - - - -
src/license/license.txt
-
-
-
-
- -
-
- - - - com.graphql-java - graphql-java - ${graphql.version} - - - com.graphql-java - graphql-java-extended-scalars - ${graphql.version} - test - - - org.reflections - reflections - 0.10.2 - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - test - - - com.fasterxml.jackson.module - jackson-module-parameter-names - ${jackson.version} - test - - - com.fasterxml.jackson.datatype - jackson-datatype-jdk8 - ${jackson.version} - test - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson.version} - test - - - io.reactivex.rxjava3 - rxjava - 3.1.6 - test - - - - org.junit.jupiter - junit-jupiter - ${junit.jupiter.version} - test - - - - - - - sonatype - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.1 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - sonatype - https://oss.sonatype.org/ - true - - - - - - -
+ + + 4.0.0 + com.fleetpin + graphql-builder + 2.0.0 + + GraphQL Builder + Builds a graphql schema from a model using reflection + https://github.com/ashley-taylor/graphql-builder + + + 5.6.0 + UTF-8 + 2.14.2 + 1.9.0 + 1.0.0 + 20.0 + + + + + sonatype + central snapshot + https://oss.sonatype.org/content/repositories/snapshots + + + sonatype + central release + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + + + https://github.com/ashley-taylor/graphql-builder + scm:git:https://github.com/ashley-taylor/graphql-builder.git + scm:git:https://github.com/ashley-taylor/graphql-builder.git + graphql-builder-2.0.0 + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + Ashley Taylor + ashley.taylor@fleetpin.co.nz + Fleetpin + http://www.fleetpin.co.nz + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + -parameters + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + + com.hubspot.maven.plugins + prettier-maven-plugin + 0.15 + + 1.4.0 + 160 + 4 + true + true + true + + + + validate + + + + + org.pitest + pitest-maven + ${pitest.version} + + + org.pitest + pitest-junit5-plugin + ${pitest-junit5-plugin.version} + + + + 4 + false + false + + STRONGER + + + + + com.mycila + license-maven-plugin + 4.1 + + + +
src/license/license.txt
+
+
+
+
+ +
+
+ + + + com.graphql-java + graphql-java + ${graphql.version} + + + com.graphql-java + graphql-java-extended-scalars + ${graphql.version} + test + + + org.reflections + reflections + 0.10.2 + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + + com.fasterxml.jackson.module + jackson-module-parameter-names + ${jackson.version} + test + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + ${jackson.version} + test + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + test + + + io.reactivex.rxjava3 + rxjava + 3.1.6 + test + + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + + + + + sonatype + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + sonatype + https://oss.sonatype.org/ + true + + + + + + +
From 96629a823b79459e97a4d9f0410a82acdcc08f07 Mon Sep 17 00:00:00 2001 From: release-bot Date: Wed, 8 Mar 2023 03:43:11 +0000 Subject: [PATCH 011/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 093ee02..b082ebb 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.0 + 2.0.1-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.0 + HEAD From c5b1517761958b24c6e1dd755a3fa555d30cc38f Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Fri, 10 Mar 2023 19:54:04 +1300 Subject: [PATCH 012/112] fix nulls in record creation. Add validation to one of annotation add test for zero arg directives --- .../mapper/ConstructorFieldBuilder.java | 5 +-- .../graphql/builder/mapper/OneOfBuilder.java | 4 ++ .../graphql/builder/DirectiveTest.java | 11 ++++- .../fleetpin/graphql/builder/RecordTest.java | 7 +++- .../graphql/builder/record/Queries.java | 9 ++++- .../graphql/builder/type/directive/Cat.java | 6 +++ .../builder/type/directive/Uppercase.java | 40 +++++++++++++++++++ 7 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/ConstructorFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/ConstructorFieldBuilder.java index e2a586f..3567063 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/ConstructorFieldBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/ConstructorFieldBuilder.java @@ -34,10 +34,7 @@ public ConstructorFieldBuilder(Class type, ArrayList mappers) { for (int i = 0; i < args.length; i++) { var mapper = mappers.get(i); - - if (map.containsKey(mapper.name)) { - args[i] = mapper.resolver.convert(map.get(mapper.name), context, locale); - } + args[i] = mapper.resolver.convert(map.get(mapper.name), context, locale); } return constructor.newInstance(args); diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java index c45d964..ac15758 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java @@ -26,6 +26,10 @@ public OneOfBuilder(EntityProcessor entityProcessor, Class type, OneOf oneOf) Map builders = new HashMap<>(); for (var typeOf : oneOf.value()) { + if (!type.isAssignableFrom(typeOf.type())) { + throw new RuntimeException("OneOf on " + type + " can not support type " + typeOf); + } + builders.put(typeOf.name(), entityProcessor.getResolver(typeOf.type())); } diff --git a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java index 1d0ba67..eb30a8e 100644 --- a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java @@ -12,6 +12,7 @@ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -19,7 +20,6 @@ import graphql.ExecutionResult; import graphql.GraphQL; import graphql.introspection.IntrospectionWithDirectivesSupport; -import graphql.scalars.ExtendedScalars; import graphql.schema.FieldCoordinates; import java.util.Map; import org.junit.jupiter.api.Test; @@ -36,6 +36,15 @@ public void testDirectiveAppliedToQuery() throws ReflectiveOperationException { assertEquals("meow", color); } + @Test + public void testNoArgumentDirective() throws ReflectiveOperationException { + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type.directive")).build(); + var cat = schema.getGraphQLSchema().getFieldDefinition(FieldCoordinates.coordinates(schema.getGraphQLSchema().getQueryType(), "getUpper")); + var uppercase = cat.getAppliedDirective("Uppercase"); + assertNotNull(uppercase); + assertTrue(uppercase.getArguments().isEmpty()); + } + @Test public void testPresentOnSchema() throws ReflectiveOperationException { GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type.directive")).build(); diff --git a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java index 137670d..6893fd7 100644 --- a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java @@ -17,6 +17,7 @@ import graphql.ExecutionResult; import graphql.GraphQL; import graphql.introspection.IntrospectionWithDirectivesSupport; +import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; @@ -27,12 +28,14 @@ public class RecordTest { public void testEntireContext() { var type = Map.of("name", "foo", "age", 4); Map> response = execute( - "query passthrough($type: InputTypeInput!){passthrough(type: $type) {name age}} ", + "query passthrough($type: InputTypeInput!){passthrough(type: $type) {name age weight}} ", Map.of("type", type) ) .getData(); var passthrough = response.get("passthrough"); - assertEquals(type, passthrough); + var expected = new HashMap<>(type); + expected.put("weight", null); + assertEquals(expected, passthrough); } private ExecutionResult execute(String query, Map variables) { diff --git a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java index 3bfbf4c..be93fa1 100644 --- a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java +++ b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java @@ -12,6 +12,7 @@ package com.fleetpin.graphql.builder.record; import com.fleetpin.graphql.builder.annotations.Query; +import java.util.Optional; public class Queries { @@ -24,11 +25,13 @@ static final class InputType { private final String name; private final int age; + private final Optional weight; - private InputType(String name, int age) { + private InputType(String name, int age, Optional weight) { super(); this.name = name; this.age = age; + this.weight = weight; } public String getName() { @@ -38,5 +41,9 @@ public String getName() { public int getAge() { return age; } + + public Optional getWeight() { + return weight; + } } } diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java index 067b7e9..78dba90 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java @@ -35,6 +35,12 @@ public static Cat getCat() { return new Cat(); } + @Query + @Uppercase + public static Cat getUpper() { + return new Cat(); + } + @Query @Admin("tabby") public static String allowed(String name) { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java new file mode 100644 index 0000000..a4cd891 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java @@ -0,0 +1,40 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.type.directive; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.fleetpin.graphql.builder.SDLDirective; +import com.fleetpin.graphql.builder.annotations.Directive; +import graphql.introspection.Introspection.DirectiveLocation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.List; + +@Directive(Uppercase.Processor.class) +@Retention(RUNTIME) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface Uppercase { + static class Processor implements SDLDirective { + + @Override + public List validLocations() { + return List.of(DirectiveLocation.FIELD_DEFINITION); + } + + @Override + public Uppercase build(Uppercase annotation, Class location) { + return annotation; + } + } +} From 9011465726f6fa01a4ace4f16fcd517e9266315c Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 10 Mar 2023 06:57:45 +0000 Subject: [PATCH 013/112] [maven-release-plugin] prepare release graphql-builder-2.0.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b082ebb..494b71e 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.1-SNAPSHOT + 2.0.1 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.1 From 6f6adeee1c9ec5b6638b17bfaa4b880e19d4e3d0 Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 10 Mar 2023 06:57:47 +0000 Subject: [PATCH 014/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 494b71e..9927d7e 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.1 + 2.0.2-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.1 + HEAD From 2a87b77644d8f7b5838972ed25095a1b8da7a795 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Mon, 13 Mar 2023 14:55:57 +1300 Subject: [PATCH 015/112] add @Nullable support. only works for last part of input --- .../graphql/builder/EntityHolder.java | 51 ++++++++--- .../graphql/builder/InputBuilder.java | 12 +-- .../graphql/builder/MethodProcessor.java | 6 +- .../graphql/builder/ScalarEntity.java | 2 - .../fleetpin/graphql/builder/TypeMeta.java | 89 +++++++++++-------- .../fleetpin/graphql/builder/RecordTest.java | 41 +++++++++ .../graphql/builder/record/Queries.java | 14 +++ 7 files changed, 155 insertions(+), 60 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java index 0ba3510..52cb7bf 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java @@ -30,6 +30,7 @@ import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; @@ -37,7 +38,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Stream; public abstract class EntityHolder { @@ -154,35 +154,44 @@ public final InputTypeBuilder getResolver(TypeMeta meta) { resolver = resolverPointer(); resolver = buildResolver(); } - return process(meta.getTypes().iterator(), resolver); + var flags = new ArrayList<>(meta.getFlags()); + Collections.reverse(flags); + return process(meta.getTypes().iterator(), flags.iterator(), resolver); } private InputTypeBuilder resolverPointer() { return (obj, graphQLContext, locale) -> this.resolver.convert(obj, graphQLContext, locale); } - private static InputTypeBuilder process(Iterator> iterator, InputTypeBuilder resolver) { + private static InputTypeBuilder process(Iterator> iterator, Iterator flags, InputTypeBuilder resolver) { if (iterator.hasNext()) { + Flag flag = null; + if (flags.hasNext()) { + flag = flags.next(); + } var type = iterator.next(); if (List.class.isAssignableFrom(type)) { - return processCollection(ArrayList::new, iterator, resolver); + return processCollection(ArrayList::new, iterator, flags, resolver); } if (Set.class.isAssignableFrom(type)) { - return processCollection(size -> new LinkedHashSet<>((int) (size / 0.75 + 1)), iterator, resolver); + return processCollection(size -> new LinkedHashSet<>((int) (size / 0.75 + 1)), iterator, flags, resolver); } if (Optional.class.isAssignableFrom(type)) { - return processOptional(iterator, resolver); + return processOptional(iterator, flags, resolver); } if (type.isArray()) { - return processArray(type, iterator, resolver); + return processArray(type, iterator, flags, resolver); } if (iterator.hasNext()) { throw new RuntimeException("Unsupported type " + type); } + if (flag == Flag.OPTIONAL) { + return processNull(type, resolver); + } if (type.isEnum()) { return processEnum((Class) type); @@ -207,8 +216,8 @@ private static InputTypeBuilder processEnum(Class type) { }; } - private static InputTypeBuilder processOptional(Iterator> iterator, InputTypeBuilder resolver) { - var mapper = process(iterator, resolver); + private static InputTypeBuilder processOptional(Iterator> iterator, Iterator flags, InputTypeBuilder resolver) { + var mapper = process(iterator, flags, resolver); return (obj, context, locale) -> { if (obj instanceof Optional) { if (((Optional) obj).isEmpty()) { @@ -224,13 +233,24 @@ private static InputTypeBuilder processOptional(Iterator> iterator, Inp }; } - private static InputTypeBuilder processArray(Class type, Iterator> iterator, InputTypeBuilder resolver) { + private static InputTypeBuilder processNull(Class type, InputTypeBuilder resolver) { + Iterator> iterator = List.>of(type).iterator(); + var mapper = process(iterator, Collections.emptyIterator(), resolver); + return (obj, context, locale) -> { + if (obj == null) { + return null; + } + return mapper.convert(obj, context, locale); + }; + } + + private static InputTypeBuilder processArray(Class type, Iterator> iterator, Iterator flags, InputTypeBuilder resolver) { var component = type.getComponentType(); if (component.isPrimitive()) { throw new RuntimeException("Do not support primitive array"); } - var mapper = process(iterator, resolver); + var mapper = process(iterator, flags, resolver); return (obj, context, locale) -> { if (obj instanceof Collection) { var collection = (Collection) obj; @@ -246,8 +266,13 @@ private static InputTypeBuilder processArray(Class type, Iterator> i }; } - private static InputTypeBuilder processCollection(Function create, Iterator> iterator, InputTypeBuilder resolver) { - var mapper = process(iterator, resolver); + private static InputTypeBuilder processCollection( + Function create, + Iterator> iterator, + Iterator flags, + InputTypeBuilder resolver + ) { + var mapper = process(iterator, flags, resolver); return (obj, context, locale) -> { if (obj instanceof Collection) { var collection = (Collection) obj; diff --git a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java index 4b69e26..78e4762 100644 --- a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java @@ -125,7 +125,7 @@ void processFields(Builder graphInputType) { GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); field.name(name.get()); entityProcessor.addSchemaDirective(method, meta.getType(), field::withAppliedDirective); - TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); + TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0], method.getParameters()[0]); var entity = entityProcessor.getEntity(innerMeta); var inputType = entity.getInputType(innerMeta, method.getParameterAnnotations()[0]); field.type(inputType); @@ -145,7 +145,7 @@ public InputTypeBuilder resolve() { try { var name = EntityUtil.setter(method); if (name.isPresent()) { - TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); + TypeMeta innerMeta = new TypeMeta(meta, method.getParameterTypes()[0], method.getGenericParameterTypes()[0], method.getParameters()[0]); fieldMappers.add(FieldMapper.build(entityProcessor, innerMeta, name.get(), method)); } } catch (RuntimeException e) { @@ -173,7 +173,7 @@ void processFields(Builder graphInputType) { GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); field.name(parameter.getName()); entityProcessor.addSchemaDirective(parameter, meta.getType(), field::withAppliedDirective); - TypeMeta innerMeta = new TypeMeta(meta, parameter.getType(), parameter.getParameterizedType()); + TypeMeta innerMeta = new TypeMeta(meta, parameter.getType(), parameter.getParameterizedType(), parameter); var entity = entityProcessor.getEntity(innerMeta); var inputType = entity.getInputType(innerMeta, parameter.getAnnotations()); field.type(inputType); @@ -189,7 +189,7 @@ public InputTypeBuilder resolve() { var fieldMappers = new ArrayList(); for (var parameter : constructor.getParameters()) { - TypeMeta innerMeta = new TypeMeta(meta, parameter.getType(), parameter.getParameterizedType()); + TypeMeta innerMeta = new TypeMeta(meta, parameter.getType(), parameter.getParameterizedType(), parameter); var resolver = entityProcessor.getResolver(innerMeta); fieldMappers.add(new RecordMapper(parameter.getName(), parameter.getType(), resolver)); } @@ -223,7 +223,7 @@ void processFields(Builder graphInputType) { GraphQLInputObjectField.Builder fieldBuilder = GraphQLInputObjectField.newInputObjectField(); fieldBuilder.name(name); entityProcessor.addSchemaDirective(field, meta.getType(), fieldBuilder::withAppliedDirective); - TypeMeta innerMeta = new TypeMeta(meta, field.getType(), field.getGenericType()); + TypeMeta innerMeta = new TypeMeta(meta, field.getType(), field.getGenericType(), field); var entity = entityProcessor.getEntity(innerMeta); var inputType = entity.getInputType(innerMeta, field.getAnnotations()); fieldBuilder.type(inputType); @@ -253,7 +253,7 @@ protected InputTypeBuilder resolve() { } else { //getter type if (!field.isAnnotationPresent(InputIgnore.class)) { - TypeMeta innerMeta = new TypeMeta(meta, field.getType(), field.getGenericType()); + TypeMeta innerMeta = new TypeMeta(meta, field.getType(), field.getGenericType(), field); var resolver = entityProcessor.getResolver(innerMeta); fieldMappers.add(new RecordMapper(field.getName(), field.getType(), resolver)); } diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index 378390c..701d90b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -85,7 +85,7 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM field.name(coordinates.getFieldName()); - TypeMeta meta = new TypeMeta(parentMeta, method.getReturnType(), method.getGenericReturnType()); + TypeMeta meta = new TypeMeta(parentMeta, method.getReturnType(), method.getGenericReturnType(), method); var type = entityProcessor.getType(meta, method.getAnnotations()); field.type(type); for (int i = 0; i < method.getParameterCount(); i++) { @@ -94,7 +94,7 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM continue; } - TypeMeta inputMeta = new TypeMeta(null, method.getParameterTypes()[i], method.getGenericParameterTypes()[i]); + TypeMeta inputMeta = new TypeMeta(null, method.getParameterTypes()[i], method.getGenericParameterTypes()[i], method.getParameters()[i]); argument.type(entityProcessor.getInputType(inputMeta, method.getParameterAnnotations()[i])); //TODO:dirty cast argument.name(method.getParameters()[i].getName()); //TODO: argument.defaultValue(defaultValue) @@ -125,7 +125,7 @@ private DataFetcher buildDataFetcher(TypeMeta meta, Method method) { Class type = method.getParameterTypes()[i]; var name = method.getParameters()[i].getName(); var generic = method.getGenericParameterTypes()[i]; - var argMeta = new TypeMeta(meta, type, generic); + var argMeta = new TypeMeta(meta, type, generic, method.getParameters()[i]); resolvers[i] = buildResolver(name, argMeta, method.getParameterAnnotations()[i]); } diff --git a/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java b/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java index 6ccf3f5..d1c0422 100644 --- a/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/ScalarEntity.java @@ -27,8 +27,6 @@ public ScalarEntity(GraphQLScalarType scalar) { this.scalar = scalar; } - // - public ScalarEntity(DirectivesSchema directives, TypeMeta meta) throws ReflectiveOperationException { GraphQLScalarType.Builder scalarType = GraphQLScalarType.newScalar(); String typeName = EntityUtil.getName(meta); diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java index f56d89d..5a07f4c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java @@ -11,6 +11,7 @@ */ package com.fleetpin.graphql.builder; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -21,6 +22,7 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; import org.reactivestreams.Publisher; public class TypeMeta { @@ -44,24 +46,31 @@ enum Flag { private List> types; + private AnnotatedElement element; + TypeMeta(TypeMeta parent, Class type, Type genericType) { + this(parent, type, genericType, null); + } + + TypeMeta(TypeMeta parent, Class type, Type genericType, AnnotatedElement element) { this.parent = parent; + this.element = element; flags = new ArrayList<>(); types = new ArrayList<>(); - process(type, genericType); + process(type, genericType, element); Collections.reverse(flags); } - private void processGeneric(TypeMeta target, TypeVariable type) { + private void processGeneric(TypeMeta target, TypeVariable type, AnnotatedElement element) { var owningClass = target.genericType; if (owningClass == null) { owningClass = target.type; } if (owningClass instanceof Class) { - findType(target, type, (Class) owningClass); + findType(target, type, (Class) owningClass, element); } else if (owningClass instanceof ParameterizedType) { var pt = (ParameterizedType) owningClass; - if (!matchType(target, type.getTypeName(), pt, true)) { + if (!matchType(target, type.getTypeName(), pt, true, element)) { throw new UnsupportedOperationException("Does not handle type " + owningClass); } } else { @@ -69,7 +78,7 @@ private void processGeneric(TypeMeta target, TypeVariable type) { } } - private boolean matchType(TypeMeta target, String typeName, ParameterizedType type, boolean parent) { + private boolean matchType(TypeMeta target, String typeName, ParameterizedType type, boolean parent, AnnotatedElement element) { var raw = (Class) type.getRawType(); while (raw != null) { for (int i = 0; i < raw.getTypeParameters().length; i++) { @@ -78,25 +87,25 @@ private boolean matchType(TypeMeta target, String typeName, ParameterizedType ty if (param.getTypeName().equals(typeName)) { if (arg instanceof TypeVariable) { if (parent) { - processGeneric(target.parent, (TypeVariable) arg); + processGeneric(target.parent, (TypeVariable) arg, element); } else { - processGeneric(target, (TypeVariable) arg); + processGeneric(target, (TypeVariable) arg, element); } return true; } else if (arg instanceof WildcardType) { for (var bound : param.getBounds()) { if (bound instanceof ParameterizedType) { - process((Class) ((ParameterizedType) bound).getRawType(), bound); + process((Class) ((ParameterizedType) bound).getRawType(), bound, element); } else if (bound instanceof TypeVariable) { - processGeneric(target, (TypeVariable) bound); + processGeneric(target, (TypeVariable) bound, element); } else { - process((Class) bound, null); + process((Class) bound, null, element); } } return true; } else { var klass = (Class) arg; - process(klass, klass); + process(klass, klass, element); return true; } } @@ -106,24 +115,24 @@ private boolean matchType(TypeMeta target, String typeName, ParameterizedType ty return false; } - private void findType(TypeMeta target, TypeVariable type, Class start) { + private void findType(TypeMeta target, TypeVariable type, Class start, AnnotatedElement element) { var startClass = (Class) start; var genericDeclaration = type.getGenericDeclaration(); if (start.equals(genericDeclaration)) { //we don't have any implementing logic we are at this level so take the bounds for (var bound : type.getBounds()) { if (bound instanceof ParameterizedType) { - process((Class) ((ParameterizedType) bound).getRawType(), bound); + process((Class) ((ParameterizedType) bound).getRawType(), bound, element); } else if (bound instanceof TypeVariable) { - processGeneric(target, (TypeVariable) bound); + processGeneric(target, (TypeVariable) bound, element); } else { - process((Class) bound, null); + process((Class) bound, null, element); } } } if (startClass.getSuperclass() != null && startClass.getSuperclass().equals(genericDeclaration)) { var generic = (ParameterizedType) startClass.getGenericSuperclass(); - if (matchType(target, type.getTypeName(), generic, false)) { + if (matchType(target, type.getTypeName(), generic, false, element)) { return; } } @@ -131,26 +140,26 @@ private void findType(TypeMeta target, TypeVariable type, Class start) { if (inter instanceof ParameterizedType) { var generic = (ParameterizedType) inter; if (generic.getRawType().equals(genericDeclaration)) { - if (matchType(target, type.getTypeName(), generic, false)) { + if (matchType(target, type.getTypeName(), generic, false, element)) { return; } } } } if (startClass.getSuperclass() != null) { - findType(target, type, startClass.getSuperclass()); + findType(target, type, startClass.getSuperclass(), element); } for (var inter : startClass.getInterfaces()) { - findType(target, type, inter); + findType(target, type, inter, element); } } - private void process(Class type, Type genericType) { + private void process(Class type, Type genericType, AnnotatedElement element) { if (type.isArray()) { flags.add(Flag.ARRAY); types.add(type); - process(type.getComponentType(), null); + process(type.getComponentType(), null, element); return; } @@ -159,11 +168,11 @@ private void process(Class type, Type genericType) { types.add(type); genericType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; if (genericType instanceof ParameterizedType) { - process((Class) ((ParameterizedType) genericType).getRawType(), genericType); + process((Class) ((ParameterizedType) genericType).getRawType(), genericType, element); } else if (genericType instanceof TypeVariable) { - processGeneric(parent, (TypeVariable) genericType); + processGeneric(parent, (TypeVariable) genericType, element); } else { - process((Class) genericType, null); + process((Class) genericType, null, element); } return; } @@ -172,11 +181,11 @@ private void process(Class type, Type genericType) { types.add(type); genericType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; if (genericType instanceof ParameterizedType) { - process((Class) ((ParameterizedType) genericType).getRawType(), genericType); + process((Class) ((ParameterizedType) genericType).getRawType(), genericType, element); } else if (genericType instanceof TypeVariable) { - processGeneric(parent, (TypeVariable) genericType); + processGeneric(parent, (TypeVariable) genericType, element); } else { - process((Class) genericType, null); + process((Class) genericType, null, element); } return; } @@ -186,11 +195,11 @@ private void process(Class type, Type genericType) { types.add(type); genericType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; if (genericType instanceof ParameterizedType) { - process((Class) ((ParameterizedType) genericType).getRawType(), genericType); + process((Class) ((ParameterizedType) genericType).getRawType(), genericType, element); } else if (genericType instanceof TypeVariable) { - processGeneric(parent, (TypeVariable) genericType); + processGeneric(parent, (TypeVariable) genericType, element); } else { - process((Class) genericType, null); + process((Class) genericType, null, element); } return; } @@ -200,18 +209,26 @@ private void process(Class type, Type genericType) { types.add(type); genericType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; if (genericType instanceof ParameterizedType) { - process((Class) ((ParameterizedType) genericType).getRawType(), genericType); + process((Class) ((ParameterizedType) genericType).getRawType(), genericType, element); } else if (genericType instanceof TypeVariable) { - processGeneric(parent, (TypeVariable) genericType); + processGeneric(parent, (TypeVariable) genericType, element); } else { - process((Class) genericType, null); + process((Class) genericType, null, element); } return; } + if (genericType != null && genericType instanceof TypeVariable) { - processGeneric(parent, (TypeVariable) genericType); + processGeneric(parent, (TypeVariable) genericType, element); return; } + + if (element != null && element.isAnnotationPresent(Nullable.class)) { + if (!flags.contains(Flag.OPTIONAL)) { + flags.add(Flag.OPTIONAL); + } + } + this.type = type; this.genericType = genericType; types.add(type); @@ -312,7 +329,7 @@ public void optional() { } public TypeMeta direct() { - var toReturn = new TypeMeta(parent, type, genericType); + var toReturn = new TypeMeta(parent, type, genericType, element); toReturn.direct = true; return toReturn; } @@ -322,7 +339,7 @@ public boolean isDirect() { } public TypeMeta notDirect() { - var toReturn = new TypeMeta(parent, type, genericType); + var toReturn = new TypeMeta(parent, type, genericType, element); return toReturn; } } diff --git a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java index 6893fd7..84aff8c 100644 --- a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java @@ -12,12 +12,17 @@ package com.fleetpin.graphql.builder; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; import graphql.introspection.IntrospectionWithDirectivesSupport; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; @@ -38,6 +43,42 @@ public void testEntireContext() { assertEquals(expected, passthrough); } + @Test + public void testNullable() { + var response = execute("query nullableTest($type: Boolean){nullableTest(type: $type)} ", null); + var expected = new HashMap(); + expected.put("nullableTest", null); + assertEquals(expected, response.getData()); + assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testSetNullable() { + Map response = execute("query nullableTest($type: Boolean){nullableTest(type: $type)}", Map.of("type", true)).getData(); + var passthrough = response.get("nullableTest"); + assertEquals(true, passthrough); + } + + @Test + public void testNullableArray() { + List array = new ArrayList<>(); + array.add(null); + array.add(true); + var response = execute("query nullableArrayTest($type: [Boolean]!){nullableArrayTest(type: $type)}", Map.of("type", array)); + var expected = new HashMap>(); + expected.put("nullableArrayTest", array); + assertTrue(response.getErrors().isEmpty()); + assertEquals(expected, response.getData()); + } + + @Test + public void testNullableArrayFails() { + List array = new ArrayList<>(); + array.add(true); + var response = execute("query nullableArrayTest($type: [Boolean]){nullableArrayTest(type: $type)}", Map.of("type", array)); + assertFalse(response.getErrors().isEmpty()); + } + private ExecutionResult execute(String query, Map variables) { GraphQL schema = GraphQL.newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.record"))).build(); var input = ExecutionInput.newExecutionInput(); diff --git a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java index be93fa1..8fdc8d4 100644 --- a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java +++ b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java @@ -12,7 +12,9 @@ package com.fleetpin.graphql.builder.record; import com.fleetpin.graphql.builder.annotations.Query; +import java.util.List; import java.util.Optional; +import javax.annotation.Nullable; public class Queries { @@ -21,6 +23,18 @@ public static InputType passthrough(InputType type) { return type; } + @Query + @Nullable + public static Boolean nullableTest(@Nullable Boolean type) { + return type; + } + + @Query + @Nullable + public static List nullableArrayTest(@Nullable List type) { + return type; + } + static final class InputType { private final String name; From 6726b87c279e989b68b8fea6a028e6420ecbb00b Mon Sep 17 00:00:00 2001 From: release-bot Date: Mon, 13 Mar 2023 01:59:13 +0000 Subject: [PATCH 016/112] [maven-release-plugin] prepare release graphql-builder-2.0.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9927d7e..27b7d38 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.2-SNAPSHOT + 2.0.2 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.2 From b232f6d807711499fb048a0fce119300042d2635 Mon Sep 17 00:00:00 2001 From: release-bot Date: Mon, 13 Mar 2023 01:59:15 +0000 Subject: [PATCH 017/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 27b7d38..3a5d5ed 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.2 + 2.0.3-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.2 + HEAD From 84ea7cf0242b304d354c9f5de8c7e546b4b1c9cb Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Fri, 31 Mar 2023 13:39:45 +1300 Subject: [PATCH 018/112] add description on fields --- .../fleetpin/graphql/builder/InputBuilder.java | 17 +++++++++++++++++ .../graphql/builder/MethodProcessor.java | 6 ++++++ .../fleetpin/graphql/builder/RecordTest.java | 1 - 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java index 78e4762..83883f1 100644 --- a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java @@ -129,6 +129,12 @@ void processFields(Builder graphInputType) { var entity = entityProcessor.getEntity(innerMeta); var inputType = entity.getInputType(innerMeta, method.getParameterAnnotations()[0]); field.type(inputType); + + var description = method.getAnnotation(GraphQLDescription.class); + if (description != null) { + field.description(description.value()); + } + graphInputType.field(field); } } catch (RuntimeException e) { @@ -176,6 +182,11 @@ void processFields(Builder graphInputType) { TypeMeta innerMeta = new TypeMeta(meta, parameter.getType(), parameter.getParameterizedType(), parameter); var entity = entityProcessor.getEntity(innerMeta); var inputType = entity.getInputType(innerMeta, parameter.getAnnotations()); + + var description = parameter.getAnnotation(GraphQLDescription.class); + if (description != null) { + field.description(description.value()); + } field.type(inputType); graphInputType.field(field); } catch (RuntimeException e) { @@ -226,6 +237,12 @@ void processFields(Builder graphInputType) { TypeMeta innerMeta = new TypeMeta(meta, field.getType(), field.getGenericType(), field); var entity = entityProcessor.getEntity(innerMeta); var inputType = entity.getInputType(innerMeta, field.getAnnotations()); + + var description = field.getAnnotation(GraphQLDescription.class); + if (description != null) { + fieldBuilder.description(description.value()); + } + fieldBuilder.type(inputType); graphInputType.field(fieldBuilder); } diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index 701d90b..e78f214 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -96,6 +96,12 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM TypeMeta inputMeta = new TypeMeta(null, method.getParameterTypes()[i], method.getGenericParameterTypes()[i], method.getParameters()[i]); argument.type(entityProcessor.getInputType(inputMeta, method.getParameterAnnotations()[i])); //TODO:dirty cast + + description = method.getParameters()[i].getAnnotation(GraphQLDescription.class); + if (description != null) { + field.description(description.value()); + } + argument.name(method.getParameters()[i].getName()); //TODO: argument.defaultValue(defaultValue) field.argument(argument); diff --git a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java index 84aff8c..66bc1d5 100644 --- a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java @@ -13,7 +13,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import graphql.ExecutionInput; From 8a1dd35844a310b8a9de9e47bc3e80f603bbd0d2 Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 31 Mar 2023 00:41:40 +0000 Subject: [PATCH 019/112] [maven-release-plugin] prepare release graphql-builder-2.0.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3a5d5ed..424c2ca 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.3-SNAPSHOT + 2.0.3 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.3 From 84dd735f63f680fe8815b0add14a08faac022c82 Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 31 Mar 2023 00:41:42 +0000 Subject: [PATCH 020/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 424c2ca..4efa798 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.3 + 2.0.4-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.3 + HEAD From bbc64cdaa6a0482037f83126f104e6c663a007fd Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Wed, 5 Apr 2023 10:04:49 +1200 Subject: [PATCH 021/112] fix parameter descriptions. Add tests --- .../fleetpin/graphql/builder/EnumEntity.java | 13 +++- .../graphql/builder/InputBuilder.java | 3 + .../graphql/builder/MethodProcessor.java | 2 +- .../annotations/GraphQLDescription.java | 2 +- .../graphql/builder/annotations/OneOf.java | 2 + .../builder/ParameterTypeParsingTest.java | 29 +++++++ .../fleetpin/graphql/builder/RecordTest.java | 35 +++++++++ .../builder/TypeInheritanceParsingTest.java | 76 +++++++++++++++++++ .../builder/parameter/TypeInputParameter.java | 3 + .../graphql/builder/record/Queries.java | 5 +- .../builder/type/inheritance/Animal.java | 5 +- .../graphql/builder/type/inheritance/Cat.java | 11 ++- 12 files changed, 179 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java index 0909554..0c8743f 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java @@ -11,6 +11,8 @@ */ package com.fleetpin.graphql.builder; +import static graphql.schema.GraphQLEnumValueDefinition.newEnumValueDefinition; + import com.fleetpin.graphql.builder.annotations.GraphQLDescription; import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; @@ -37,10 +39,17 @@ public EnumEntity(DirectivesSchema directives, TypeMeta meta) throws ReflectiveO Object[] enums = type.getEnumConstants(); for (Object e : enums) { Enum a = (Enum) e; - if (type.getDeclaredField(e.toString()).isAnnotationPresent(GraphQLIgnore.class)) { + var field = type.getDeclaredField(e.toString()); + if (field.isAnnotationPresent(GraphQLIgnore.class)) { continue; } - enumType.value(a.name(), a); + var valueDef = newEnumValueDefinition().name(a.name()).value(a); + var desc = field.getAnnotation(GraphQLDescription.class); + if (desc != null) { + valueDef.description(desc.value()); + } + + enumType.value(valueDef.build()); } directives.addSchemaDirective(type, type, enumType::withAppliedDirective); this.enumType = enumType.build(); diff --git a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java index 83883f1..7ef891d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java @@ -95,6 +95,9 @@ void processFields(Builder graphInputType) { var name = oneOfType.name(); GraphQLInputObjectField.Builder field = GraphQLInputObjectField.newInputObjectField(); field.name(name); + if (!oneOfType.description().isEmpty()) { + field.description(oneOfType.description()); + } TypeMeta innerMeta = new TypeMeta(meta, oneOfType.type(), oneOfType.type()); innerMeta.optional(); var type = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, new Annotation[0]); diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index e78f214..b2da18d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -99,7 +99,7 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM description = method.getParameters()[i].getAnnotation(GraphQLDescription.class); if (description != null) { - field.description(description.value()); + argument.description(description.value()); } argument.name(method.getParameters()[i].getName()); diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java index d6d7dda..ce6c6d7 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLDescription.java @@ -18,7 +18,7 @@ import java.lang.annotation.Target; @Retention(RUNTIME) -@Target({ ElementType.METHOD, ElementType.TYPE }) +@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER }) public @interface GraphQLDescription { public String value(); } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java b/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java index a01a0b2..b05736e 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/OneOf.java @@ -28,5 +28,7 @@ String name(); Class type(); + + String description() default ""; } } diff --git a/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java index 22f1c94..27e53b9 100644 --- a/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/ParameterTypeParsingTest.java @@ -52,6 +52,35 @@ public void testEnum() throws ReflectiveOperationException, JsonMappingException assertEquals("CAT", response.get("enumTest")); } + @Test + public void testDescription() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { + Map> response = execute( + "{" + + " __type(name: \"AnimalType\") {" + + " name" + + " kind" + + " description" + + " enumValues {" + + " name" + + " description" + + " }" + + " }" + + "} ", + null + ) + .getData(); + + var type = response.get("__type"); + + assertEquals("enum desc", type.get("description")); + + Map dog = new HashMap<>(); + dog.put("name", "DOG"); + dog.put("description", null); + + assertEquals(List.of(Map.of("name", "CAT", "description", "A cat"), dog), type.get("enumValues")); + } + @Test public void testOptionalTypePresent() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { Map> response = execute("query test($type: InputTestInput){optionalType(type: $type){value}} ", "{\"value\": \"There\"}") diff --git a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java index 66bc1d5..062284b 100644 --- a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java @@ -15,6 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; @@ -42,6 +44,39 @@ public void testEntireContext() { assertEquals(expected, passthrough); } + @Test + public void testDescription() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { + Map> response = execute( + "{" + + " __type(name: \"InputTypeInput\") {" + + " name" + + " kind" + + " description" + + " inputFields {" + + " name" + + " description" + + " }" + + " }" + + "} ", + null + ) + .getData(); + + var type = response.get("__type"); + System.out.println(type); + assertEquals("record Type", type.get("description")); + + Map age = new HashMap<>(); + age.put("name", "age"); + age.put("description", null); + + Map weight = new HashMap<>(); + weight.put("name", "weight"); + weight.put("description", null); + + assertEquals(List.of(Map.of("name", "name", "description", "the name"), age, weight), type.get("inputFields")); + } + @Test public void testNullable() { var response = execute("query nullableTest($type: Boolean){nullableTest(type: $type)} ", null); diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java index a598614..93f68dc 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java @@ -94,6 +94,71 @@ public void testDogAge() throws ReflectiveOperationException { confirmNumber(nonNull); } + @Test + public void testCatDescription() throws ReflectiveOperationException { + var type = getField("Cat", "OBJECT", null); + Assertions.assertEquals("cat type", type.get("description")); + } + + @Test + public void testCatFurDescription() throws ReflectiveOperationException { + var type = getField("Cat", "OBJECT", null); + + List> fields = (List>) type.get("fields"); + var field = fields.stream().filter(map -> map.get("name").equals("fur")).findAny().get(); + Assertions.assertEquals("get fur", field.get("description")); + } + + @Test + public void testCatWeightArgumentDescription() throws ReflectiveOperationException { + var type = getField("Cat", "OBJECT", null); + + List> fields = (List>) type.get("fields"); + var field = fields.stream().filter(map -> map.get("name").equals("weight")).findAny().get(); + Assertions.assertEquals(null, field.get("description")); + + List> args = (List>) field.get("args"); + var round = args.stream().filter(map -> map.get("name").equals("round")).findAny().get(); + Assertions.assertEquals("whole number", round.get("description")); + } + + @Test + public void testMutationDescription() throws ReflectiveOperationException { + var type = getField("Mutations", "OBJECT", null); + + List> fields = (List>) type.get("fields"); + var field = fields.stream().filter(map -> map.get("name").equals("getCat")).findAny().get(); + Assertions.assertEquals("cat endpoint", field.get("description")); + + List> args = (List>) field.get("args"); + var round = args.stream().filter(map -> map.get("name").equals("age")).findAny().get(); + Assertions.assertEquals("sample", round.get("description")); + } + + @Test + public void testCatFurInputDescription() throws ReflectiveOperationException { + var type = getField("CatInput", "INPUT_OBJECT", null); + + List> fields = (List>) type.get("inputFields"); + var field = fields.stream().filter(map -> map.get("name").equals("fur")).findAny().get(); + Assertions.assertEquals("set fur", field.get("description")); + } + + @Test + public void testInputCatDescription() throws ReflectiveOperationException { + var type = getField("CatInput", "INPUT_OBJECT", null); + Assertions.assertEquals("cat type", type.get("description")); + } + + @Test + public void testInputOneOfDescription() throws ReflectiveOperationException { + var type = getField("AnimalInput", "INPUT_OBJECT", null); + Assertions.assertEquals("animal desc", type.get("description")); + List> fields = (List>) type.get("inputFields"); + var field = fields.stream().filter(map -> map.get("name").equals("dog")).findAny().get(); + Assertions.assertEquals("A dog", field.get("description")); + } + private void confirmString(Map type) { Assertions.assertEquals("SCALAR", type.get("kind")); Assertions.assertEquals("String", type.get("name")); @@ -136,8 +201,14 @@ public Map getField(String typeName, String kind, String name) t "\") {" + " name" + " kind" + + " description" + " fields {" + " name" + + " description" + + " args {" + + " name" + + " description" + + " }" + " type {" + " name" + " kind" + @@ -157,6 +228,7 @@ public Map getField(String typeName, String kind, String name) t " }" + " inputFields {" + " name" + + " description" + " type {" + " name" + " kind" + @@ -182,6 +254,10 @@ public Map getField(String typeName, String kind, String name) t Assertions.assertEquals(typeName, type.get("name")); Assertions.assertEquals(kind, type.get("kind")); + if (name == null) { + return type; + } + List> fields = (List>) type.get("fields"); if (fields == null) { fields = (List>) type.get("inputFields"); diff --git a/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java b/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java index 3b9f454..81f6af8 100644 --- a/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java +++ b/src/test/java/com/fleetpin/graphql/builder/parameter/TypeInputParameter.java @@ -12,6 +12,7 @@ package com.fleetpin.graphql.builder.parameter; import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; import com.fleetpin.graphql.builder.annotations.Query; import com.fleetpin.graphql.builder.annotations.SchemaOption; import java.util.List; @@ -20,7 +21,9 @@ public class TypeInputParameter { @Entity + @GraphQLDescription("enum desc") public enum AnimalType { + @GraphQLDescription("A cat") CAT, DOG, } diff --git a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java index 8fdc8d4..c13f064 100644 --- a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java +++ b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java @@ -11,6 +11,7 @@ */ package com.fleetpin.graphql.builder.record; +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; import com.fleetpin.graphql.builder.annotations.Query; import java.util.List; import java.util.Optional; @@ -35,13 +36,15 @@ public static List nullableArrayTest(@Nullable List type) { return type; } + // once move to java 17 change this to be a real record + @GraphQLDescription("record Type") static final class InputType { private final String name; private final int age; private final Optional weight; - private InputType(String name, int age, Optional weight) { + private InputType(@GraphQLDescription("the name") String name, int age, Optional weight) { super(); this.name = name; this.age = age; diff --git a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java index 6024e7c..99b24ea 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Animal.java @@ -12,6 +12,7 @@ package com.fleetpin.graphql.builder.type.inheritance; import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; import com.fleetpin.graphql.builder.annotations.Mutation; import com.fleetpin.graphql.builder.annotations.OneOf; import com.fleetpin.graphql.builder.annotations.Query; @@ -20,11 +21,13 @@ import java.util.List; @Entity(SchemaOption.BOTH) -@OneOf(value = { @OneOf.Type(name = "cat", type = Cat.class), @OneOf.Type(name = "dog", type = Dog.class) }) +@GraphQLDescription("animal desc") +@OneOf(value = { @OneOf.Type(name = "cat", type = Cat.class), @OneOf.Type(name = "dog", type = Dog.class, description = "A dog") }) public abstract class Animal { private String name = "name"; + @GraphQLDescription("the name") public String getName() { return name; } diff --git a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java index f3ee671..e121187 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/inheritance/Cat.java @@ -12,11 +12,13 @@ package com.fleetpin.graphql.builder.type.inheritance; import com.fleetpin.graphql.builder.annotations.Entity; +import com.fleetpin.graphql.builder.annotations.GraphQLDescription; import com.fleetpin.graphql.builder.annotations.Mutation; import com.fleetpin.graphql.builder.annotations.SchemaOption; import java.util.Optional; @Entity(SchemaOption.BOTH) +@GraphQLDescription("cat type") public class Cat extends Animal { private boolean calico; @@ -52,10 +54,16 @@ public void setAge(int age) { this.age = age; } + @GraphQLDescription("get fur") public Optional getFur() { return fur; } + public Optional getWeight(@GraphQLDescription("whole number") Boolean round) { + return fur; + } + + @GraphQLDescription("set fur") public void setFur(Optional fur) { this.fur = fur; } @@ -65,7 +73,8 @@ public void setError(Optional ignore) { } @Mutation - public static Cat getCat() { + @GraphQLDescription("cat endpoint") + public static Cat getCat(@GraphQLDescription("sample") Optional age) { return null; } } From 99c2f5893bdf6921f0b766cbd5a4fc5fcffa86ab Mon Sep 17 00:00:00 2001 From: release-bot Date: Tue, 4 Apr 2023 22:06:18 +0000 Subject: [PATCH 022/112] [maven-release-plugin] prepare release graphql-builder-2.0.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4efa798..3241a66 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.4-SNAPSHOT + 2.0.4 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.4 From ab754e3b909af0cd488576f921464e22ee0ec343 Mon Sep 17 00:00:00 2001 From: release-bot Date: Tue, 4 Apr 2023 22:06:20 +0000 Subject: [PATCH 023/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3241a66..b7120f6 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.4 + 2.0.5-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.4 + HEAD From cfc028f5c4f9ca402160918e00a8cd204f21c861 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Wed, 5 Apr 2023 10:08:19 +1200 Subject: [PATCH 024/112] add jakarta support --- pom.xml | 5 +++++ src/main/java/com/fleetpin/graphql/builder/TypeMeta.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4efa798..2b56c17 100644 --- a/pom.xml +++ b/pom.xml @@ -154,6 +154,11 @@ reflections 0.10.2 + + jakarta.annotation + jakarta.annotation-api + 2.1.1 + com.fasterxml.jackson.core jackson-databind diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java index 5a07f4c..1e0ed98 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java @@ -223,7 +223,7 @@ private void process(Class type, Type genericType, AnnotatedElement element) return; } - if (element != null && element.isAnnotationPresent(Nullable.class)) { + if (element != null && (element.isAnnotationPresent(Nullable.class) || element.isAnnotationPresent(jakarta.annotation.Nullable.class))) { if (!flags.contains(Flag.OPTIONAL)) { flags.add(Flag.OPTIONAL); } From 066f5265356a476f5993be35b303bd3ecb1c31a4 Mon Sep 17 00:00:00 2001 From: release-bot Date: Tue, 4 Apr 2023 22:10:01 +0000 Subject: [PATCH 025/112] [maven-release-plugin] prepare release graphql-builder-2.0.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fca7f6b..9a667df 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.5-SNAPSHOT + 2.0.5 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.5 From dd31d266e57dc9a128fbbc6a6a6f9920a1c8ae18 Mon Sep 17 00:00:00 2001 From: release-bot Date: Tue, 4 Apr 2023 22:10:04 +0000 Subject: [PATCH 026/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9a667df..1656133 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.5 + 2.0.6-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.5 + HEAD From 0a3037c51c6d448c71374a65ea8bcbfbdd0ad574 Mon Sep 17 00:00:00 2001 From: Hamish Medlin Date: Tue, 4 Jul 2023 10:36:49 +1200 Subject: [PATCH 027/112] Added InvalidOneOfException and removed TODO from tests --- .../exceptions/InvalidOneOfException.java | 8 + .../graphql/builder/mapper/OneOfBuilder.java | 8 +- .../builder/TypeInheritanceParsingTest.java | 196 ++++++++---------- 3 files changed, 105 insertions(+), 107 deletions(-) create mode 100644 src/main/java/com/fleetpin/graphql/builder/exceptions/InvalidOneOfException.java diff --git a/src/main/java/com/fleetpin/graphql/builder/exceptions/InvalidOneOfException.java b/src/main/java/com/fleetpin/graphql/builder/exceptions/InvalidOneOfException.java new file mode 100644 index 0000000..0593e06 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/exceptions/InvalidOneOfException.java @@ -0,0 +1,8 @@ +package com.fleetpin.graphql.builder.exceptions; + +public class InvalidOneOfException extends RuntimeException { + + public InvalidOneOfException(String message) { + super(message); + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java index ac15758..0484dcb 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/OneOfBuilder.java @@ -13,6 +13,7 @@ import com.fleetpin.graphql.builder.EntityProcessor; import com.fleetpin.graphql.builder.annotations.OneOf; +import com.fleetpin.graphql.builder.exceptions.InvalidOneOfException; import graphql.GraphQLContext; import java.util.HashMap; import java.util.Locale; @@ -27,7 +28,7 @@ public OneOfBuilder(EntityProcessor entityProcessor, Class type, OneOf oneOf) for (var typeOf : oneOf.value()) { if (!type.isAssignableFrom(typeOf.type())) { - throw new RuntimeException("OneOf on " + type + " can not support type " + typeOf); + throw new InvalidOneOfException("OneOf on " + type + " can not support type " + typeOf); } builders.put(typeOf.name(), entityProcessor.getResolver(typeOf.type())); @@ -38,7 +39,8 @@ public OneOfBuilder(EntityProcessor entityProcessor, Class type, OneOf oneOf) Map map = (Map) obj; if (map.size() > 1) { - throw new RuntimeException("OneOf must only have a single field set"); + var fields = String.join(", ", map.keySet()); + throw new InvalidOneOfException("OneOf must only have a single field set. Fields: " + fields); } for (var entry : map.entrySet()) { @@ -46,7 +48,7 @@ public OneOfBuilder(EntityProcessor entityProcessor, Class type, OneOf oneOf) return builder.convert(entry.getValue(), context, locale); } - throw new RuntimeException("OneOf must only have a single field set"); + throw new InvalidOneOfException("OneOf must have a field set"); }; } diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java index 93f68dc..33ecff4 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java @@ -11,22 +11,24 @@ */ package com.fleetpin.graphql.builder; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - +import com.fleetpin.graphql.builder.exceptions.InvalidOneOfException; +import graphql.ExceptionWhileDataFetching; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; import java.util.List; import java.util.Map; + +import graphql.validation.ValidationError; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + public class TypeInheritanceParsingTest { @Test - public void findTypes() throws ReflectiveOperationException { + public void findTypes() { Map>>> response = execute("{__schema {types {name}}} ").getData(); var types = response.get("__schema").get("types"); var count = types.stream().filter(map -> map.get("name").equals("SimpleType")).count(); @@ -34,74 +36,74 @@ public void findTypes() throws ReflectiveOperationException { } @Test - public void testAnimalName() throws ReflectiveOperationException { + public void testAnimalName() { var name = getField("Animal", "INTERFACE", "name"); var nonNull = confirmNonNull(name); confirmString(nonNull); } @Test - public void testAnimalInputName() throws ReflectiveOperationException { + public void testAnimalInputName() { var name = getField("AnimalInput", "INPUT_OBJECT", "cat"); confirmInputObject(name, "CatInput"); } @Test - public void testCatName() throws ReflectiveOperationException { + public void testCatName() { var name = getField("Cat", "OBJECT", "name"); var nonNull = confirmNonNull(name); confirmString(nonNull); } @Test - public void testCatAge() throws ReflectiveOperationException { + public void testCatAge() { var name = getField("Cat", "OBJECT", "age"); var nonNull = confirmNonNull(name); confirmNumber(nonNull); } @Test - public void testCatFur() throws ReflectiveOperationException { + public void testCatFur() { var name = getField("Cat", "OBJECT", "fur"); confirmBoolean(name); } @Test - public void testCatCalico() throws ReflectiveOperationException { + public void testCatCalico() { var name = getField("Cat", "OBJECT", "calico"); var nonNull = confirmNonNull(name); confirmBoolean(nonNull); } @Test - public void testDogName() throws ReflectiveOperationException { + public void testDogName() { var name = getField("Dog", "OBJECT", "name"); var nonNull = confirmNonNull(name); confirmString(nonNull); } @Test - public void testDogFur() throws ReflectiveOperationException { + public void testDogFur() { var name = getField("Dog", "OBJECT", "fur"); var nonNull = confirmNonNull(name); confirmString(nonNull); } @Test - public void testDogAge() throws ReflectiveOperationException { + public void testDogAge() { var name = getField("Dog", "OBJECT", "age"); var nonNull = confirmNonNull(name); confirmNumber(nonNull); } @Test - public void testCatDescription() throws ReflectiveOperationException { + public void testCatDescription() { var type = getField("Cat", "OBJECT", null); Assertions.assertEquals("cat type", type.get("description")); } @Test - public void testCatFurDescription() throws ReflectiveOperationException { + public void testCatFurDescription() { var type = getField("Cat", "OBJECT", null); List> fields = (List>) type.get("fields"); @@ -110,7 +112,7 @@ public void testCatFurDescription() throws ReflectiveOperationException { } @Test - public void testCatWeightArgumentDescription() throws ReflectiveOperationException { + public void testCatWeightArgumentDescription() { var type = getField("Cat", "OBJECT", null); List> fields = (List>) type.get("fields"); @@ -123,7 +125,7 @@ public void testCatWeightArgumentDescription() throws ReflectiveOperationExcepti } @Test - public void testMutationDescription() throws ReflectiveOperationException { + public void testMutationDescription() { var type = getField("Mutations", "OBJECT", null); List> fields = (List>) type.get("fields"); @@ -136,7 +138,7 @@ public void testMutationDescription() throws ReflectiveOperationException { } @Test - public void testCatFurInputDescription() throws ReflectiveOperationException { + public void testCatFurInputDescription() { var type = getField("CatInput", "INPUT_OBJECT", null); List> fields = (List>) type.get("inputFields"); @@ -145,13 +147,13 @@ public void testCatFurInputDescription() throws ReflectiveOperationException { } @Test - public void testInputCatDescription() throws ReflectiveOperationException { + public void testInputCatDescription() { var type = getField("CatInput", "INPUT_OBJECT", null); Assertions.assertEquals("cat type", type.get("description")); } @Test - public void testInputOneOfDescription() throws ReflectiveOperationException { + public void testInputOneOfDescription() { var type = getField("AnimalInput", "INPUT_OBJECT", null); Assertions.assertEquals("animal desc", type.get("description")); List> fields = (List>) type.get("inputFields"); @@ -193,7 +195,7 @@ private Map confirmArray(Map type) { return toReturn; } - public Map getField(String typeName, String kind, String name) throws ReflectiveOperationException { + public Map getField(String typeName, String kind, String name) { Map> response = execute( "{" + " __type(name: \"" + @@ -268,7 +270,7 @@ public Map getField(String typeName, String kind, String name) t } @Test - public void testQueryCatFur() throws ReflectiveOperationException { + public void testQueryCatFur() { Map>> response = execute( "query {animals{" + "name " + "... on Cat { " + " age " + " fur " + " calico " + "} " + "... on Dog {" + " age " + "} " + "}} " ) @@ -289,7 +291,7 @@ public void testQueryCatFur() throws ReflectiveOperationException { } @Test - public void testQueryDogFur() throws ReflectiveOperationException { + public void testQueryDogFur() { Map>> response = execute( "query {animals{" + "name " + "... on Cat { " + " age " + " calico " + "} " + "... on Dog {" + " age " + " fur " + "} " + "}} " ) @@ -310,19 +312,17 @@ public void testQueryDogFur() throws ReflectiveOperationException { } @Test - public void testBothFurFails() throws ReflectiveOperationException { - Assertions.assertThrows( - RuntimeException.class, - () -> { - execute( - "query {animals{" + "name " + "... on Cat { " + " age " + " fur " + " calico " + "} " + "... on Dog {" + " age " + " fur " + "} " + "}} " - ); - } + public void testBothFurFails() { + var result = execute( + "query {animals{" + "name " + "... on Cat { " + " age " + " fur " + " calico " + "} " + "... on Dog {" + " age " + " fur " + "} " + "}} " ); + + assertFalse(result.getErrors().isEmpty()); + assertTrue(result.getErrors().get(0) instanceof ValidationError); } @Test - public void testOneOf() throws ReflectiveOperationException { + public void testOneOf() { Map>> response = execute( "mutation {myAnimals(animals: [" + "{cat: {fur: true, calico: false, name: \"socks\", age: 4}}," + @@ -356,7 +356,7 @@ public void testOneOf() throws ReflectiveOperationException { } @Test - public void testOptionalFieldNotSet() throws ReflectiveOperationException { + public void testOptionalFieldNotSet() { Map>> response = execute( "mutation {myAnimals(animals: [" + "{cat: {calico: false, name: \"socks\", age: 4}}," + @@ -385,7 +385,7 @@ public void testOptionalFieldNotSet() throws ReflectiveOperationException { } @Test - public void testOptionalFieldNull() throws ReflectiveOperationException { + public void testOptionalFieldNull() { Map>> response = execute( "mutation {myAnimals(animals: [" + "{cat: {fur: null, calico: false, name: \"socks\", age: 4}}," + @@ -410,84 +410,76 @@ public void testOptionalFieldNull() throws ReflectiveOperationException { assertEquals("socks", cat.get("name")); assertEquals(4, cat.get("age")); assertEquals(false, cat.get("calico")); - assertEquals(null, cat.get("fur")); + assertNull(cat.get("fur")); } @Test - public void testOneOfError() throws ReflectiveOperationException { - var exception = assertThrows( - RuntimeException.class, - () -> - execute( - "mutation {myAnimals(animals: [" + - "{cat: {fur: true, calico: false, name: \"socks\", age: 4}," + - "dog: {fur: \"short\", name: \"patches\", age: 5}}" + - "]){" + - "name " + - "... on Cat { " + - " age " + - " calico " + - "} " + - "... on Dog {" + - " age " + - " fur " + - "} " + - "}} " - ) - .getData() + public void testOneOfError() { + var result = execute( + "mutation {myAnimals(animals: [" + + "{cat: {fur: true, calico: false, name: \"socks\", age: 4}," + + "dog: {fur: \"short\", name: \"patches\", age: 5}}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " ); - assertTrue(exception.getMessage().contains("OneOf must only have a single field set")); + assertFalse(result.getErrors().isEmpty()); + var exception = ((ExceptionWhileDataFetching)result.getErrors().get(0)).getException(); + assertTrue(exception.getMessage().contains("OneOf must only have a single field set. Fields: cat, dog")); } @Test - public void testOneOfErrorEmpty() throws ReflectiveOperationException { - var exception = assertThrows( - RuntimeException.class, - () -> - execute( - "mutation {myAnimals(animals: [" + - "{}" + - "]){" + - "name " + - "... on Cat { " + - " age " + - " calico " + - "} " + - "... on Dog {" + - " age " + - " fur " + - "} " + - "}} " - ) - .getData() + public void testOneOfErrorEmpty() { + var result = execute( + "mutation {myAnimals(animals: [" + + "{}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " ); - assertTrue(exception.getMessage().contains("OneOf must only have a single field set")); + assertFalse(result.getErrors().isEmpty()); + var exception = ((ExceptionWhileDataFetching)result.getErrors().get(0)).getException(); + assertTrue(exception.getMessage().contains("OneOf must have a field set")); } @Test - public void testOneOfErrorField() throws ReflectiveOperationException { - var exception = assertThrows( - RuntimeException.class, - () -> - execute( - "mutation {myAnimals(animals: [" + - "{cat: {fur: null, calico: false, name: \"socks\", age: 4, error: \"fail\"}}" + - "]){" + - "name " + - "... on Cat { " + - " age " + - " calico " + - "} " + - "... on Dog {" + - " age " + - " fur " + - "} " + - "}} " - ) - .getData() + public void testOneOfErrorField() { + var result = execute( + "mutation {myAnimals(animals: [" + + "{cat: {fur: null, calico: false, name: \"socks\", age: 4, error: \"fail\"}}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " ); + + assertFalse(result.getErrors().isEmpty()); + var exception = ((ExceptionWhileDataFetching)result.getErrors().get(0)).getException(); assertTrue(exception.getMessage().contains("ERROR")); } @@ -502,10 +494,6 @@ private ExecutionResult execute(String query, Map variables) { if (variables != null) { input.variables(variables); } - ExecutionResult result = schema.execute(input); - if (!result.getErrors().isEmpty()) { - throw new RuntimeException(result.getErrors().toString()); //TODO:cleanup - } - return result; + return schema.execute(input); } } From 144241fa7aae1f534037696433efb4b8343c6b4f Mon Sep 17 00:00:00 2001 From: release-bot Date: Mon, 3 Jul 2023 22:42:19 +0000 Subject: [PATCH 028/112] [maven-release-plugin] prepare release graphql-builder-2.0.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1656133..8e7a9e0 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.6-SNAPSHOT + 2.0.6 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.6 From f936074801943c7c1ffd31415abf58367bb93830 Mon Sep 17 00:00:00 2001 From: release-bot Date: Mon, 3 Jul 2023 22:42:21 +0000 Subject: [PATCH 029/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8e7a9e0..02a1444 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.6 + 2.0.7-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.6 + HEAD From 616cc6a38c4f9cd377fbc2f897889d756f9919be Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Tue, 4 Jul 2023 11:20:59 +1200 Subject: [PATCH 030/112] Update pom.xml --- pom.xml | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/pom.xml b/pom.xml index 02a1444..a822b7d 100644 --- a/pom.xml +++ b/pom.xml @@ -83,6 +83,31 @@ maven-release-plugin 2.5.3 + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + attach-javadocs + + jar + + + + com.hubspot.maven.plugins prettier-maven-plugin @@ -204,31 +229,6 @@ sonatype - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.1 - - - attach-javadocs - - jar - - - - org.apache.maven.plugins maven-gpg-plugin From c1e50367eea0bba174c2cc6f269322c3cf23d120 Mon Sep 17 00:00:00 2001 From: release-bot Date: Mon, 3 Jul 2023 23:22:20 +0000 Subject: [PATCH 031/112] [maven-release-plugin] prepare release graphql-builder-2.0.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a822b7d..9c1d5f3 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.7-SNAPSHOT + 2.0.7 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.7 From a3b31e4c0738e1d89d5f72c4574f8d2c0bc2f432 Mon Sep 17 00:00:00 2001 From: release-bot Date: Mon, 3 Jul 2023 23:22:22 +0000 Subject: [PATCH 032/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9c1d5f3..36dd4ce 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.7 + 2.0.8-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.7 + HEAD From 5b68b0e9d86e726348a144d84417d941b64c3203 Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Tue, 4 Jul 2023 11:28:39 +1200 Subject: [PATCH 033/112] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 36dd4ce..1d00c58 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ attach-sources - jar + jar-no-fork From 05ba13050b28f1e7008d58bb126b7f66051ba7e0 Mon Sep 17 00:00:00 2001 From: release-bot Date: Mon, 3 Jul 2023 23:29:55 +0000 Subject: [PATCH 034/112] [maven-release-plugin] prepare release graphql-builder-2.0.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1d00c58..8e2f43a 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.8-SNAPSHOT + 2.0.8 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.8 From 5fffa32887dd7fbc0d8949f78bc72c39035c0c9e Mon Sep 17 00:00:00 2001 From: release-bot Date: Mon, 3 Jul 2023 23:29:57 +0000 Subject: [PATCH 035/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8e2f43a..2c60ab0 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.8 + 2.0.9-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.8 + HEAD From be6626916415f73459fb8e365dcd87471d09f8f0 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Mon, 25 Sep 2023 09:07:57 +1300 Subject: [PATCH 036/112] add ability to add Fetcher Factory to support virtual threads --- pom.xml | 8 +- .../graphql/builder/AuthorizerSchema.java | 85 +++++++++++++---- .../graphql/builder/DataFetcherRunner.java | 8 ++ .../graphql/builder/EntityProcessor.java | 4 +- .../graphql/builder/MethodProcessor.java | 6 +- .../graphql/builder/SchemaBuilder.java | 17 +++- .../exceptions/InvalidOneOfException.java | 6 +- .../builder/TypeInheritanceParsingTest.java | 95 +++++++++---------- 8 files changed, 150 insertions(+), 79 deletions(-) create mode 100644 src/main/java/com/fleetpin/graphql/builder/DataFetcherRunner.java diff --git a/pom.xml b/pom.xml index 2c60ab0..269d106 100644 --- a/pom.xml +++ b/pom.xml @@ -22,12 +22,12 @@ https://github.com/ashley-taylor/graphql-builder - 5.6.0 + 5.10.0 UTF-8 - 2.14.2 + 2.15.2 1.9.0 1.0.0 - 20.0 + 21.1 @@ -171,7 +171,7 @@ com.graphql-java graphql-java-extended-scalars - ${graphql.version} + 21.0 test diff --git a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java index 1c58fcc..5aef746 100644 --- a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java @@ -11,38 +11,48 @@ */ package com.fleetpin.graphql.builder; +import static com.fleetpin.graphql.builder.EntityUtil.isContext; + +import graphql.GraphQLContext; import graphql.GraphqlErrorException; import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; public class AuthorizerSchema { + private final DataFetcherRunner dataFetcherRunner; private final Set basePackages; private final Map targets; - private AuthorizerSchema(Set basePackages, Map targets) { + private AuthorizerSchema(DataFetcherRunner dataFetcherRunner, Set basePackages, Map targets) { + this.dataFetcherRunner = dataFetcherRunner; this.basePackages = basePackages; this.targets = targets; } - public static AuthorizerSchema build(Set basePackage, Set> authorizers) throws ReflectiveOperationException { + public static AuthorizerSchema build(DataFetcherRunner dataFetcherRunner, Set basePackage, Set> authorizers) + throws ReflectiveOperationException { Map targets = new HashMap<>(); for (var type : authorizers) { Authorizer auth = type.getDeclaredConstructor().newInstance(); targets.put(type.getPackageName(), auth); } - return new AuthorizerSchema(basePackage, targets); + return new AuthorizerSchema(dataFetcherRunner, basePackage, targets); } public Authorizer getAuthorizer(Class type) { @@ -139,21 +149,35 @@ public DataFetcher wrap(DataFetcher fetcher, Method method) { throw new RuntimeException("No authorizer found for " + method); } - return env -> { - for (Method authorizer : toRun) { - Object[] args = new Object[authorizer.getParameterCount()]; - - for (int i = 0; i < args.length; i++) { - if (authorizer.getParameterTypes()[i].isAssignableFrom(env.getClass())) { - args[i] = env; - } else if (authorizer.getParameterTypes()[i].isAssignableFrom(env.getContext().getClass())) { - args[i] = env.getContext(); - } else { - args[i] = env.getArgument(authorizer.getParameters()[i].getName()); + List> authRunners = toRun + .stream() + .>map(authorizer -> { + var count = authorizer.getParameterCount(); + + List> mappers = Arrays + .asList(authorizer.getParameters()) + .stream() + .map(parameter -> buildResolver(parameter.getName(), parameter.getType(), parameter.getAnnotations())) + .toList(); + + DataFetcher authFetcher = env -> { + Object[] args = new Object[count]; + + for (int i = 0; i < args.length; i++) { + args[i] = mappers.get(i).apply(env); } - } + + return authorizer.invoke(wrapper, args); + }; + return dataFetcherRunner.manage(authorizer, authFetcher); + }) + .toList(); + + return env -> { + for (var authorizer : authRunners) { try { - Object allow = authorizer.invoke(wrapper, args); + Object allow = authorizer.get(env); + if (allow instanceof Boolean) { if ((Boolean) allow) { return fetcher.get(env); @@ -210,4 +234,33 @@ public DataFetcher wrap(DataFetcher fetcher, Method method) { return fetcher.get(env); }; } + + private Function buildResolver(String name, Class type, Annotation[] annotations) { + if (isContext(type, annotations)) { + if (type.isAssignableFrom(DataFetchingEnvironment.class)) { + return env -> env; + } + if (type.isAssignableFrom(GraphQLContext.class)) { + return env -> env.getGraphQlContext(); + } + return env -> { + var localContext = env.getLocalContext(); + if (localContext != null && type.isAssignableFrom(localContext.getClass())) { + return localContext; + } + + var context = env.getContext(); + if (context != null && type.isAssignableFrom(context.getClass())) { + return context; + } + + context = env.getGraphQlContext().get(name); + if (context != null && type.isAssignableFrom(context.getClass())) { + return context; + } + throw new RuntimeException("Context object " + name + " not found"); + }; + } + return env -> env.getArgument(name); + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/DataFetcherRunner.java b/src/main/java/com/fleetpin/graphql/builder/DataFetcherRunner.java new file mode 100644 index 0000000..1b13479 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/DataFetcherRunner.java @@ -0,0 +1,8 @@ +package com.fleetpin.graphql.builder; + +import graphql.schema.DataFetcher; +import java.lang.reflect.Method; + +public interface DataFetcherRunner { + public DataFetcher manage(Method method, DataFetcher fetcher); +} diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java index 4bf1647..0deb63c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityProcessor.java @@ -38,8 +38,8 @@ public class EntityProcessor { private final Map entities; private final MethodProcessor methodProcessor; - EntityProcessor(List scalars, DirectivesSchema diretives) { - this.methodProcessor = new MethodProcessor(this, diretives); + EntityProcessor(DataFetcherRunner dataFetcherRunner, List scalars, DirectivesSchema diretives) { + this.methodProcessor = new MethodProcessor(dataFetcherRunner, this, diretives); this.entities = new HashMap<>(); addDefaults(); addScalars(scalars); diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index b2da18d..fecad87 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -24,6 +24,7 @@ class MethodProcessor { + private final DataFetcherRunner dataFetcherRunner; private final EntityProcessor entityProcessor; private final DirectivesSchema diretives; @@ -33,7 +34,8 @@ class MethodProcessor { private final GraphQLObjectType.Builder graphMutations; private final GraphQLObjectType.Builder graphSubscriptions; - public MethodProcessor(EntityProcessor entityProcessor, DirectivesSchema diretives) { + public MethodProcessor(DataFetcherRunner dataFetcherRunner, EntityProcessor entityProcessor, DirectivesSchema diretives) { + this.dataFetcherRunner = dataFetcherRunner; this.entityProcessor = entityProcessor; this.diretives = diretives; this.codeRegistry = GraphQLCodeRegistry.newCodeRegistry(); @@ -153,7 +155,7 @@ private DataFetcher buildDataFetcher(TypeMeta meta, Method method) { } }; - return fetcher; + return dataFetcherRunner.manage(method, fetcher); } private Function buildResolver(String name, TypeMeta argMeta, Annotation[] annotations) { diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index d4cbbb1..e382a38 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -36,11 +36,11 @@ public class SchemaBuilder { private final EntityProcessor entityProcessor; - private SchemaBuilder(List scalars, DirectivesSchema diretives, AuthorizerSchema authorizer) { + private SchemaBuilder(DataFetcherRunner dataFetcherRunner, List scalars, DirectivesSchema diretives, AuthorizerSchema authorizer) { this.diretives = diretives; this.authorizer = authorizer; - this.entityProcessor = new EntityProcessor(scalars, diretives); + this.entityProcessor = new EntityProcessor(dataFetcherRunner, scalars, diretives); diretives.processSDL(entityProcessor); } @@ -109,11 +109,17 @@ public static Builder builder() { public static class Builder { + private DataFetcherRunner dataFetcherRunner = (method, fetcher) -> fetcher; private List classpaths = new ArrayList<>(); private List scalars = new ArrayList<>(); private Builder() {} + public Builder dataFetcherRunner(DataFetcherRunner dataFetcherRunner) { + this.dataFetcherRunner = dataFetcherRunner; + return this; + } + public Builder classpath(String classpath) { this.classpaths.add(classpath); return this; @@ -129,7 +135,7 @@ public GraphQLSchema.Builder build() { Reflections reflections = new Reflections(classpaths, Scanners.SubTypes, Scanners.MethodsAnnotated, Scanners.TypesAnnotated); Set> authorizers = reflections.getSubTypesOf(Authorizer.class); //want to make everything split by package - AuthorizerSchema authorizer = AuthorizerSchema.build(new HashSet<>(classpaths), authorizers); + AuthorizerSchema authorizer = AuthorizerSchema.build(dataFetcherRunner, new HashSet<>(classpaths), authorizers); Set> schemaConfiguration = reflections.getSubTypesOf(SchemaConfiguration.class); @@ -181,7 +187,10 @@ public GraphQLSchema.Builder build() { types.removeIf(t -> t.getDeclaredAnnotation(Entity.class) == null); types.removeIf(t -> t.isAnonymousClass()); - return new SchemaBuilder(scalars, diretivesSchema, authorizer).processTypes(types).process(endPoints).build(schemaConfiguration); + return new SchemaBuilder(dataFetcherRunner, scalars, diretivesSchema, authorizer) + .processTypes(types) + .process(endPoints) + .build(schemaConfiguration); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } diff --git a/src/main/java/com/fleetpin/graphql/builder/exceptions/InvalidOneOfException.java b/src/main/java/com/fleetpin/graphql/builder/exceptions/InvalidOneOfException.java index 0593e06..e5cbf0e 100644 --- a/src/main/java/com/fleetpin/graphql/builder/exceptions/InvalidOneOfException.java +++ b/src/main/java/com/fleetpin/graphql/builder/exceptions/InvalidOneOfException.java @@ -2,7 +2,7 @@ public class InvalidOneOfException extends RuntimeException { - public InvalidOneOfException(String message) { - super(message); - } + public InvalidOneOfException(String message) { + super(message); + } } diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java index 33ecff4..36808ac 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeInheritanceParsingTest.java @@ -11,20 +11,19 @@ */ package com.fleetpin.graphql.builder; +import static org.junit.jupiter.api.Assertions.*; + import com.fleetpin.graphql.builder.exceptions.InvalidOneOfException; import graphql.ExceptionWhileDataFetching; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; +import graphql.validation.ValidationError; import java.util.List; import java.util.Map; - -import graphql.validation.ValidationError; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - public class TypeInheritanceParsingTest { @Test @@ -314,7 +313,7 @@ public void testQueryDogFur() { @Test public void testBothFurFails() { var result = execute( - "query {animals{" + "name " + "... on Cat { " + " age " + " fur " + " calico " + "} " + "... on Dog {" + " age " + " fur " + "} " + "}} " + "query {animals{" + "name " + "... on Cat { " + " age " + " fur " + " calico " + "} " + "... on Dog {" + " age " + " fur " + "} " + "}} " ); assertFalse(result.getErrors().isEmpty()); @@ -416,70 +415,70 @@ public void testOptionalFieldNull() { @Test public void testOneOfError() { var result = execute( - "mutation {myAnimals(animals: [" + - "{cat: {fur: true, calico: false, name: \"socks\", age: 4}," + - "dog: {fur: \"short\", name: \"patches\", age: 5}}" + - "]){" + - "name " + - "... on Cat { " + - " age " + - " calico " + - "} " + - "... on Dog {" + - " age " + - " fur " + - "} " + - "}} " + "mutation {myAnimals(animals: [" + + "{cat: {fur: true, calico: false, name: \"socks\", age: 4}," + + "dog: {fur: \"short\", name: \"patches\", age: 5}}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " ); assertFalse(result.getErrors().isEmpty()); - var exception = ((ExceptionWhileDataFetching)result.getErrors().get(0)).getException(); + var exception = ((ExceptionWhileDataFetching) result.getErrors().get(0)).getException(); assertTrue(exception.getMessage().contains("OneOf must only have a single field set. Fields: cat, dog")); } @Test public void testOneOfErrorEmpty() { var result = execute( - "mutation {myAnimals(animals: [" + - "{}" + - "]){" + - "name " + - "... on Cat { " + - " age " + - " calico " + - "} " + - "... on Dog {" + - " age " + - " fur " + - "} " + - "}} " + "mutation {myAnimals(animals: [" + + "{}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " ); assertFalse(result.getErrors().isEmpty()); - var exception = ((ExceptionWhileDataFetching)result.getErrors().get(0)).getException(); + var exception = ((ExceptionWhileDataFetching) result.getErrors().get(0)).getException(); assertTrue(exception.getMessage().contains("OneOf must have a field set")); } @Test public void testOneOfErrorField() { var result = execute( - "mutation {myAnimals(animals: [" + - "{cat: {fur: null, calico: false, name: \"socks\", age: 4, error: \"fail\"}}" + - "]){" + - "name " + - "... on Cat { " + - " age " + - " calico " + - "} " + - "... on Dog {" + - " age " + - " fur " + - "} " + - "}} " + "mutation {myAnimals(animals: [" + + "{cat: {fur: null, calico: false, name: \"socks\", age: 4, error: \"fail\"}}" + + "]){" + + "name " + + "... on Cat { " + + " age " + + " calico " + + "} " + + "... on Dog {" + + " age " + + " fur " + + "} " + + "}} " ); assertFalse(result.getErrors().isEmpty()); - var exception = ((ExceptionWhileDataFetching)result.getErrors().get(0)).getException(); + var exception = ((ExceptionWhileDataFetching) result.getErrors().get(0)).getException(); assertTrue(exception.getMessage().contains("ERROR")); } From be195a5a53b017e543023fec6b126f2228af3b2d Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Mon, 25 Sep 2023 09:11:09 +1300 Subject: [PATCH 037/112] support java 11 --- .../java/com/fleetpin/graphql/builder/AuthorizerSchema.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java index 5aef746..11ebf51 100644 --- a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java @@ -31,6 +31,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Function; +import java.util.stream.Collectors; public class AuthorizerSchema { @@ -158,7 +159,7 @@ public DataFetcher wrap(DataFetcher fetcher, Method method) { .asList(authorizer.getParameters()) .stream() .map(parameter -> buildResolver(parameter.getName(), parameter.getType(), parameter.getAnnotations())) - .toList(); + .collect(Collectors.toList()); DataFetcher authFetcher = env -> { Object[] args = new Object[count]; From 70a8549b4a3bce74c8ef08630406a72c62f5ea13 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Mon, 25 Sep 2023 09:13:42 +1300 Subject: [PATCH 038/112] missed one --- .../java/com/fleetpin/graphql/builder/AuthorizerSchema.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java index 11ebf51..2eff123 100644 --- a/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/AuthorizerSchema.java @@ -172,7 +172,7 @@ public DataFetcher wrap(DataFetcher fetcher, Method method) { }; return dataFetcherRunner.manage(authorizer, authFetcher); }) - .toList(); + .collect(Collectors.toList()); return env -> { for (var authorizer : authRunners) { From 20d7ff51ec9e1d4f8102b403d23ee394a668f672 Mon Sep 17 00:00:00 2001 From: release-bot Date: Sun, 24 Sep 2023 20:15:56 +0000 Subject: [PATCH 039/112] [maven-release-plugin] prepare release graphql-builder-2.0.9 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 269d106..2dffb51 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.9-SNAPSHOT + 2.0.9 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.9 From c05934f3cebdd90a7177d85a19062e89debe7abe Mon Sep 17 00:00:00 2001 From: release-bot Date: Sun, 24 Sep 2023 20:15:58 +0000 Subject: [PATCH 040/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2dffb51..a16fcb3 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.9 + 2.0.10-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.9 + HEAD From 0cf6c039fef993f9423114bb0ad2449dfe5169f6 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Mon, 4 Dec 2023 13:03:54 +1300 Subject: [PATCH 041/112] Add functionality for custom directives to be on parameters/arguments --- .../graphql/builder/MethodProcessor.java | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index fecad87..0952c66 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -2,20 +2,13 @@ import static com.fleetpin.graphql.builder.EntityUtil.isContext; -import com.fleetpin.graphql.builder.annotations.GraphQLDeprecated; -import com.fleetpin.graphql.builder.annotations.GraphQLDescription; -import com.fleetpin.graphql.builder.annotations.Mutation; -import com.fleetpin.graphql.builder.annotations.Query; -import com.fleetpin.graphql.builder.annotations.Subscription; +import com.fleetpin.graphql.builder.annotations.*; import graphql.GraphQLContext; -import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; -import graphql.schema.FieldCoordinates; -import graphql.schema.GraphQLArgument; -import graphql.schema.GraphQLCodeRegistry; -import graphql.schema.GraphQLFieldDefinition; +import graphql.Scalars; +import graphql.language.StringValue; +import graphql.schema.*; import graphql.schema.GraphQLFieldDefinition.Builder; -import graphql.schema.GraphQLObjectType; + import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -104,6 +97,31 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM argument.description(description.value()); } + var parameter = method.getParameters()[i]; + for (Annotation annotation : parameter.getAnnotations()) { + // Check to see if the annotation is a directive + if (!annotation.annotationType().isAnnotationPresent(Directive.class)) { + continue; + } + var annotationType = annotation.annotationType(); + // Get the values out of the directive annotation + var methods = annotationType.getDeclaredMethods(); + try { + var appliedDirective = new GraphQLAppliedDirective.Builder() + .name(annotationType.getName()); + for (var definedMethod : methods) { + var name = definedMethod.getName(); + var value = definedMethod.invoke(annotation); + appliedDirective.argument(GraphQLAppliedDirectiveArgument.newArgument() + .name(name) + .type(Scalars.GraphQLString) + .valueLiteral(new StringValue((String) value)) + .build()); + } + argument.withAppliedDirective(appliedDirective); + } catch (Exception ignored) {} + } + argument.name(method.getParameters()[i].getName()); //TODO: argument.defaultValue(defaultValue) field.argument(argument); From eaad1723c92d4bfdaecc47f47a4abb46f3db022d Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Mon, 4 Dec 2023 13:22:13 +1300 Subject: [PATCH 042/112] Stopped ignoring the exception when invoking method calls --- .../graphql/builder/MethodProcessor.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index 0952c66..bb1f50c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -63,7 +63,7 @@ void process(AuthorizerSchema authorizer, Method method) throws ReflectiveOperat object.field(process(authorizer, coordinates, null, method)); } - Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeMeta parentMeta, Method method) { + Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeMeta parentMeta, Method method) throws InvocationTargetException, IllegalAccessException { GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); entityProcessor.addSchemaDirective(method, method.getDeclaringClass(), field::withAppliedDirective); @@ -106,20 +106,19 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM var annotationType = annotation.annotationType(); // Get the values out of the directive annotation var methods = annotationType.getDeclaredMethods(); - try { - var appliedDirective = new GraphQLAppliedDirective.Builder() - .name(annotationType.getName()); - for (var definedMethod : methods) { - var name = definedMethod.getName(); - var value = definedMethod.invoke(annotation); - appliedDirective.argument(GraphQLAppliedDirectiveArgument.newArgument() - .name(name) - .type(Scalars.GraphQLString) - .valueLiteral(new StringValue((String) value)) - .build()); - } - argument.withAppliedDirective(appliedDirective); - } catch (Exception ignored) {} + + var appliedDirective = new GraphQLAppliedDirective.Builder() + .name(annotationType.getName()); + for (var definedMethod : methods) { + var name = definedMethod.getName(); + var value = definedMethod.invoke(annotation); + appliedDirective.argument(GraphQLAppliedDirectiveArgument.newArgument() + .name(name) + .type(Scalars.GraphQLString) + .valueLiteral(new StringValue((String) value)) + .build()); + } + argument.withAppliedDirective(appliedDirective); } argument.name(method.getParameters()[i].getName()); From 61e4ecdc9acf1152304d63fe3304d127993fe6f3 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Mon, 4 Dec 2023 13:28:20 +1300 Subject: [PATCH 043/112] Fixed typo --- .../fleetpin/graphql/builder/SchemaBuilder.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index e382a38..f843f50 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -31,18 +31,17 @@ public class SchemaBuilder { - private final DirectivesSchema diretives; + private final DirectivesSchema directives; private final AuthorizerSchema authorizer; - private final EntityProcessor entityProcessor; - private SchemaBuilder(DataFetcherRunner dataFetcherRunner, List scalars, DirectivesSchema diretives, AuthorizerSchema authorizer) { - this.diretives = diretives; + private SchemaBuilder(DataFetcherRunner dataFetcherRunner, List scalars, DirectivesSchema directives, AuthorizerSchema authorizer) { + this.directives = directives; this.authorizer = authorizer; - this.entityProcessor = new EntityProcessor(dataFetcherRunner, scalars, diretives); + this.entityProcessor = new EntityProcessor(dataFetcherRunner, scalars, directives); - diretives.processSDL(entityProcessor); + directives.processSDL(entityProcessor); } private SchemaBuilder processTypes(Set> types) { @@ -83,10 +82,10 @@ private graphql.schema.GraphQLSchema.Builder build(Set builder.additionalDirective(directive)); + directives.getSchemaDirective().forEach(directive -> builder.additionalDirective(directive)); for (var schema : schemaConfiguration) { - this.diretives.addSchemaDirective(schema, schema, builder::withSchemaAppliedDirective); + this.directives.addSchemaDirective(schema, schema, builder::withSchemaAppliedDirective); } return builder; } From a237b8b604b598e1094ac9b417ad9dc120437a2c Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Mon, 4 Dec 2023 13:30:35 +1300 Subject: [PATCH 044/112] More typos fixed --- .../java/com/fleetpin/graphql/builder/SchemaBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index f843f50..989fe67 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -138,7 +138,7 @@ public GraphQLSchema.Builder build() { Set> schemaConfiguration = reflections.getSubTypesOf(SchemaConfiguration.class); - Set> dierctivesTypes = reflections.getTypesAnnotatedWith(Directive.class); + Set> directivesTypes = reflections.getTypesAnnotatedWith(Directive.class); Set> restrict = reflections.getTypesAnnotatedWith(Restrict.class); Set> restricts = reflections.getTypesAnnotatedWith(Restricts.class); @@ -171,7 +171,7 @@ public GraphQLSchema.Builder build() { } } - DirectivesSchema diretivesSchema = DirectivesSchema.build(globalRestricts, dierctivesTypes); + DirectivesSchema directivesSchema = DirectivesSchema.build(globalRestricts, directivesTypes); Set> types = reflections.getTypesAnnotatedWith(Entity.class); @@ -186,7 +186,7 @@ public GraphQLSchema.Builder build() { types.removeIf(t -> t.getDeclaredAnnotation(Entity.class) == null); types.removeIf(t -> t.isAnonymousClass()); - return new SchemaBuilder(dataFetcherRunner, scalars, diretivesSchema, authorizer) + return new SchemaBuilder(dataFetcherRunner, scalars, directivesSchema, authorizer) .processTypes(types) .process(endPoints) .build(schemaConfiguration); From 1f3b29c3d6773d4af283571cd7f44f03e9f6e74a Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Mon, 4 Dec 2023 13:50:40 +1300 Subject: [PATCH 045/112] Fix bug where the program would raise an exception upon starting due to Classpath being used instead of Class name --- .../java/com/fleetpin/graphql/builder/MethodProcessor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index bb1f50c..2aa4d1f 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -97,6 +97,7 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM argument.description(description.value()); } + // TODO: TEST THE PARAMETER DIRECTIVE var parameter = method.getParameters()[i]; for (Annotation annotation : parameter.getAnnotations()) { // Check to see if the annotation is a directive @@ -108,10 +109,11 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM var methods = annotationType.getDeclaredMethods(); var appliedDirective = new GraphQLAppliedDirective.Builder() - .name(annotationType.getName()); + .name(annotationType.getSimpleName()); for (var definedMethod : methods) { var name = definedMethod.getName(); var value = definedMethod.invoke(annotation); + if (value == null) {continue;} appliedDirective.argument(GraphQLAppliedDirectiveArgument.newArgument() .name(name) .type(Scalars.GraphQLString) From 4681445fbae73a350fb931e6904524d9fecf6707 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 10:23:43 +1300 Subject: [PATCH 046/112] Created the @DirectiveLocations annotation as a temporary change while replacing SDLProcessor with DirectiveProcessor --- .../graphql/builder/DirectiveProcessor.java | 99 +++++++++++++++++++ .../annotations/DirectiveLocations.java | 18 ++++ 2 files changed, 117 insertions(+) create mode 100644 src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java create mode 100644 src/main/java/com/fleetpin/graphql/builder/annotations/DirectiveLocations.java diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java new file mode 100644 index 0000000..b3fcd83 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java @@ -0,0 +1,99 @@ +package com.fleetpin.graphql.builder; + +import com.fleetpin.graphql.builder.annotations.DirectiveLocations; +import graphql.introspection.Introspection; +import graphql.schema.*; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; + +public class DirectiveProcessor { + + private final GraphQLDirective directive; + private final Map> builders; +// private final Map methodTypes; TODO: Deal with this too + + public DirectiveProcessor(GraphQLDirective directive, Map> builders) { + this.directive = directive; + this.builders = builders; +// this.methodTypes = methodTypes; TODO: DEAL WITH THIS + } + + public static DirectiveProcessor build(EntityProcessor entityProcessor, Class directive) { + var processedDirectives = new ArrayList(); + + + var builder = GraphQLDirective.newDirective().name(directive.getSimpleName()); + var validLocations = directive.getAnnotation(DirectiveLocations.class).value(); + // loop through and add valid locations + for (Introspection.DirectiveLocation location : validLocations) { + builder.validLocation(location); + } + + // Save method types so when we apply the values later we don't have to go looking for them + Map methodTypes = new HashMap<>(); + + // Go through each argument and add name/type to directive + var methods = directive.getDeclaredMethods(); + Map> builders = new HashMap<>(); + for (Method method : methods) { + if (method.getParameterCount() != 0) { + continue; + } + var name = method.getName(); + + GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); + argument.name(name); + + // Get the type of the argument from the return type of the method + TypeMeta innerMeta = new TypeMeta(null, method.getReturnType(), method.getGenericReturnType()); + var argumentType = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, method.getAnnotations()); + argument.type(argumentType); + + // Add the argument to the directive builder to be used for declaration + builder.argument(argument); + + // Add a builder to the builders list (in order to populate applied directives) + builders.put(name, object -> { + try { + return GraphQLAppliedDirectiveArgument.newArgument() + .name(name) + .type(argumentType) + .valueProgrammatic(method.invoke(object)) + .build(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }); + + + + } + return new DirectiveProcessor(builder.build(), builders); + } + + public void apply(Annotation annotation, EntityProcessor entityProcessor, Consumer builder) throws InvocationTargetException, IllegalAccessException { + var methods = annotation.annotationType().getDeclaredMethods(); + + // Create a new AppliedDirective which we will populate with the set values + var arguments = GraphQLAppliedDirective.newDirective(); + arguments.name(directive.getName()); + + // To get the value we loop through each method and get the method name and value + for (Method m : methods) { + // Using the builder created earlier populate the values of each method. + arguments.argument(builders.get(m.getName()).apply(annotation)); + } + + // Add the argument to the Builder + builder.accept(arguments.build()); + } + + public GraphQLDirective getDirective() { + return this.directive; + } +} diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/DirectiveLocations.java b/src/main/java/com/fleetpin/graphql/builder/annotations/DirectiveLocations.java new file mode 100644 index 0000000..148bb26 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/DirectiveLocations.java @@ -0,0 +1,18 @@ +package com.fleetpin.graphql.builder.annotations; + +import graphql.introspection.Introspection.DirectiveLocation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface DirectiveLocations { + // TODO: Whole class should be temporary as Location can replace + // the 'Class> value();' in @Directive + + DirectiveLocation[] value(); +} From 74f1c310322f10ead36d24e962baa0bdc88c0316 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 10:27:28 +1300 Subject: [PATCH 047/112] Converting code to run through the DirectiveProcessor --- .../graphql/builder/DirectiveProcessor.java | 2 +- .../graphql/builder/DirectivesSchema.java | 84 +++++++++---------- .../graphql/builder/SchemaBuilder.java | 9 +- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java index b3fcd83..9394574 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java @@ -76,7 +76,7 @@ public static DirectiveProcessor build(EntityProcessor entityProcessor, Class builder) throws InvocationTargetException, IllegalAccessException { + public void apply(Annotation annotation, Consumer builder) throws InvocationTargetException, IllegalAccessException { var methods = annotation.annotationType().getDeclaredMethods(); // Create a new AppliedDirective which we will populate with the set values diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java index 5ef7dcd..147188d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java @@ -12,80 +12,66 @@ package com.fleetpin.graphql.builder; import com.fleetpin.graphql.builder.annotations.Directive; +import com.fleetpin.graphql.builder.annotations.DirectiveLocations; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.GraphQLAppliedDirective; import graphql.schema.GraphQLDirective; +import org.reactivestreams.Publisher; + import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.WeakHashMap; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.reactivestreams.Publisher; class DirectivesSchema { private final Collection> global; private final Map, DirectiveCaller> targets; private final Map, SDLDirective> schemaDirective; - private Map, SDLProcessor> sdlProcessors; + private Map, SDLProcessor> sdlProcessors; // TODO: REMOVE + private final Collection> directives; + private Map, DirectiveProcessor> directiveProcessors; private DirectivesSchema( Collection> global, Map, DirectiveCaller> targets, - Map, SDLDirective> schemaDirective + Map, SDLDirective> schemaDirective, + Collection> directives ) { this.global = global; this.targets = targets; this.schemaDirective = schemaDirective; + this.directives = directives; } //TODO:mess of exceptions - public static DirectivesSchema build(List> globalDirectives, Set> dierctiveTypes) throws ReflectiveOperationException { - Map, DirectiveCaller> targets = new HashMap<>(); - Map, SDLDirective> graphqlDirective = new HashMap<>(); - for (Class directiveType : dierctiveTypes) { + public static DirectivesSchema build(List> globalDirectives, Set> directiveTypes) throws ReflectiveOperationException { + Map, DirectiveCaller> targets = new HashMap<>(); // TODO: Remove this + Map, SDLDirective> graphqlDirective = new HashMap<>(); // TODO: Remove this and above line, keeping as to not cause abundant errors + + Collection> allDirectives = new ArrayList<>(); + for (Class directiveType : directiveTypes) { if (!directiveType.isAnnotationPresent(Directive.class)) { continue; } if (!directiveType.isAnnotation()) { - //TODO:better error management throw new RuntimeException("@Directive Annotation must only be placed on annotations"); } - - var directive = directiveType.getAnnotation(Directive.class); - Class> caller = directive.value(); - //TODO: if target implents other things this won't lineup right - Class target = (Class) ((ParameterizedType) caller.getGenericInterfaces()[0]).getActualTypeArguments()[0]; - if (!target.equals(directiveType)) { - //TODO:better errors - throw new RuntimeException("Annotation missmatch"); - } - - if (DirectiveCaller.class.isAssignableFrom(caller)) { - //TODO error for no zero args constructor - var callerInstance = (DirectiveCaller) caller.getConstructor().newInstance(); - targets.put((Class) directiveType, callerInstance); - } else { - var callerInstance = (SDLDirective) caller.getConstructor().newInstance(); - graphqlDirective.put((Class) directiveType, callerInstance); + if (!directiveType.isAnnotationPresent(DirectiveLocations.class)) { + throw new RuntimeException("@DirectiveLocations must be specified"); } + allDirectives.add((Class) directiveType); } - return new DirectivesSchema(globalDirectives, targets, graphqlDirective); + return new DirectivesSchema(globalDirectives, targets, graphqlDirective, allDirectives); } private DirectiveCaller get(Annotation annotation) { @@ -98,8 +84,9 @@ private DataFetcher wrap(DirectiveCaller directive, }; } - public Stream getSchemaDirective() { - return sdlProcessors.values().stream().map(f -> f.getDirective()); + public Stream getSchemaDirective() { // TODO: This is where the SchemaBuilder turns the annotations into GraphQLDirectives +// return sdlProcessors.values().stream().map(SDLProcessor::getDirective); TODO: REMOVE + return directiveProcessors.values().stream().map(DirectiveProcessor::getDirective); } private DataFetcher wrap(RestrictTypeFactory directive, DataFetcher fetcher) { @@ -210,16 +197,20 @@ private static CompletableFuture> all(List> toR ); } - public void addSchemaDirective(AnnotatedElement element, Class location, Consumer builder) { + public void addSchemaDirective(AnnotatedElement element, Class location, Consumer builder) { // TODO: This is also probably important for (Annotation annotation : element.getAnnotations()) { - var processor = this.sdlProcessors.get(annotation.annotationType()); + var processor = this.directiveProcessors.get(annotation.annotationType()); if (processor != null) { - processor.apply(annotation, location, builder); - } + try { + processor.apply(annotation, builder); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException("Could not process applied directive: " + location.getName()); + } + } } } - public void processSDL(EntityProcessor entityProcessor) { + public void processSDL(EntityProcessor entityProcessor) { // TODO: Replace this with processDirectives Map, SDLProcessor> sdlProcessors = new HashMap<>(); this.schemaDirective.forEach((k, v) -> { @@ -227,4 +218,13 @@ public void processSDL(EntityProcessor entityProcessor) { }); this.sdlProcessors = sdlProcessors; } + + public void processDirectives(EntityProcessor ep) { // Replacement of processSDL + Map, DirectiveProcessor> directiveProcessors = new HashMap<>(); + + this.directives.forEach(dir -> + directiveProcessors.put(dir, DirectiveProcessor.build(ep, dir))); + this.directiveProcessors = directiveProcessors; + + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index 989fe67..2f22ea4 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -41,7 +41,8 @@ private SchemaBuilder(DataFetcherRunner dataFetcherRunner, List> types) { @@ -82,10 +83,10 @@ private graphql.schema.GraphQLSchema.Builder build(Set builder.additionalDirective(directive)); + directives.getSchemaDirective().forEach(directive -> builder.additionalDirective(directive)); // TODO: This is where all of the directives are added into the actual schema for (var schema : schemaConfiguration) { - this.directives.addSchemaDirective(schema, schema, builder::withSchemaAppliedDirective); + this.directives.addSchemaDirective(schema, schema, builder::withSchemaAppliedDirective); // TODO: This part too. } return builder; } @@ -171,7 +172,7 @@ public GraphQLSchema.Builder build() { } } - DirectivesSchema directivesSchema = DirectivesSchema.build(globalRestricts, directivesTypes); + DirectivesSchema directivesSchema = DirectivesSchema.build(globalRestricts, directivesTypes); // Entry point for directives Set> types = reflections.getTypesAnnotatedWith(Entity.class); From e98a312624c08d9cb7f4304b0d92c5492b3bd4a1 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 11:56:39 +1300 Subject: [PATCH 048/112] Refactor testing annotations to fit new layout --- .../builder/annotations/Directive.java | 14 ++++++++++- .../graphql/builder/scalar/Capture.java | 22 +++++------------ .../graphql/builder/type/directive/Admin.java | 6 +++-- .../builder/type/directive/Capture.java | 21 ++++------------ .../builder/type/directive/Uppercase.java | 24 ++++++------------- 5 files changed, 35 insertions(+), 52 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java index 317f775..3cee74d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java @@ -13,7 +13,11 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.fleetpin.graphql.builder.DirectiveCaller; import com.fleetpin.graphql.builder.DirectiveOperation; +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -21,5 +25,13 @@ @Retention(RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Directive { - Class> value(); + Class> caller() default Directive.Processor.class; + + static class Processor implements DirectiveCaller { + + @Override + public Object process(Directive annotation, DataFetchingEnvironment env, DataFetcher fetcher) throws Exception { + return null; + } + } } diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java index 15ff89c..d61d820 100644 --- a/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java @@ -11,30 +11,20 @@ */ package com.fleetpin.graphql.builder.scalar; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.fleetpin.graphql.builder.SDLDirective; import com.fleetpin.graphql.builder.annotations.Directive; +import com.fleetpin.graphql.builder.annotations.DirectiveLocations; import graphql.introspection.Introspection.DirectiveLocation; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import java.util.List; -@Directive(Capture.Processor.class) +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Directive() @Retention(RUNTIME) @Target({ ElementType.TYPE }) +@DirectiveLocations(DirectiveLocation.OBJECT) public @interface Capture { - static class Processor implements SDLDirective { - - @Override - public List validLocations() { - return List.of(DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA); - } - @Override - public CaptureType build(Capture annotation, Class location) { - return new CaptureType(); - } - } } diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java index 7387f9f..45db20b 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java @@ -15,20 +15,22 @@ import com.fleetpin.graphql.builder.DirectiveCaller; import com.fleetpin.graphql.builder.annotations.Directive; +import com.fleetpin.graphql.builder.annotations.DirectiveLocations; +import graphql.introspection.Introspection; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Directive(Admin.Processor.class) +@Directive(caller = Admin.Processor.class) @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) +@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.SCHEMA}) public @interface Admin { String value(); static class Processor implements DirectiveCaller { - @Override public Object process(Admin annotation, DataFetchingEnvironment env, DataFetcher fetcher) throws Exception { if (env.getArgument("name").equals(annotation.value())) { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java index 39243d2..0d93908 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java @@ -13,30 +13,19 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.fleetpin.graphql.builder.SDLDirective; import com.fleetpin.graphql.builder.annotations.Directive; +import com.fleetpin.graphql.builder.annotations.DirectiveLocations; +import graphql.introspection.Introspection; import graphql.introspection.Introspection.DirectiveLocation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.List; -@Directive(Capture.Processor.class) +@Directive @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) +@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA}) public @interface Capture { - String value(); - - static class Processor implements SDLDirective { - - @Override - public List validLocations() { - return List.of(DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA); - } - - @Override - public CaptureType build(Capture annotation, Class location) { - return new CaptureType().setColor(annotation.value()); - } - } + String color(); } diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java index a4cd891..7f3c5c7 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java @@ -11,30 +11,20 @@ */ package com.fleetpin.graphql.builder.type.directive; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.fleetpin.graphql.builder.SDLDirective; import com.fleetpin.graphql.builder.annotations.Directive; -import graphql.introspection.Introspection.DirectiveLocation; +import com.fleetpin.graphql.builder.annotations.DirectiveLocations; +import graphql.introspection.Introspection; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import java.util.List; -@Directive(Uppercase.Processor.class) +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Directive @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) +@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION}) public @interface Uppercase { - static class Processor implements SDLDirective { - - @Override - public List validLocations() { - return List.of(DirectiveLocation.FIELD_DEFINITION); - } - @Override - public Uppercase build(Uppercase annotation, Class location) { - return annotation; - } - } } From 72042f1e7d6401ebc50ed12043bbae5c8eccb968 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 11:57:11 +1300 Subject: [PATCH 049/112] Refactor testing annotations to fit new layout --- .../com/fleetpin/graphql/builder/type/directive/CatSchema.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java index d2b57ca..bc463f0 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/CatSchema.java @@ -13,5 +13,5 @@ import com.fleetpin.graphql.builder.SchemaConfiguration; -@Capture("top") +@Capture(color = "top") public class CatSchema implements SchemaConfiguration {} From e3594717ca7742c275a115acd93a106c4f9d192a Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 11:57:40 +1300 Subject: [PATCH 050/112] New tests added for the Argument directives --- .../graphql/builder/DirectiveTest.java | 46 +++++++++++++++---- .../graphql/builder/type/directive/Cat.java | 7 ++- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java index eb30a8e..b560e41 100644 --- a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java @@ -11,19 +11,21 @@ */ package com.fleetpin.graphql.builder; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; import graphql.introspection.IntrospectionWithDirectivesSupport; import graphql.schema.FieldCoordinates; -import java.util.Map; +import graphql.schema.GraphQLSchema; import org.junit.jupiter.api.Test; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + public class DirectiveTest { @Test @@ -69,12 +71,38 @@ public void testDirectiveFail() throws ReflectiveOperationException { assertTrue(response.getErrors().get(0).getMessage().contains("forbidden")); } + @Test + public void testDirectiveArgument() { + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.type.directive")).build(); + var cat = schema.getGraphQLSchema().getFieldDefinition(FieldCoordinates.coordinates(schema.getGraphQLSchema().getQueryType(), "getNickname")); + var argument = cat.getArgument("nickName"); + var directive = argument.getAppliedDirective("Input"); + assertNotNull(directive); + var value = directive.getArgument("value").getValue(); + assertEquals("TT", value); + } + + @Test + public void testDirectiveArgumentDefinition() { + Map response = execute("query IntrospectionQuery { __schema { directives { name locations args { name } } } }", + null).getData(); + List> dir = (List>) ((Map)response.get("__schema")).get("directives"); + LinkedHashMap input = dir.stream().filter(map -> map.get("name").equals("Input")).collect(Collectors.toList()).get(0); + + assertEquals(7, dir.size()); + assertEquals("ARGUMENT_DEFINITION", ((List)input.get("locations")).get(0)); + assertEquals(1, ((List)input.get("args")).size()); + + //getNickname(nickName: String! @Input(value : "TT")): String! + //directive @Input(value: String!) on ARGUMENT_DEFINITION + } + private ExecutionResult execute(String query, Map variables) { + GraphQLSchema preSchema = SchemaBuilder.builder().classpath("com.fleetpin.graphql.builder.type.directive").build().build(); GraphQL schema = GraphQL - .newGraphQL( - new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.builder().classpath("com.fleetpin.graphql.builder.type.directive").build().build()) - ) + .newGraphQL(new IntrospectionWithDirectivesSupport().apply(preSchema)) .build(); + var input = ExecutionInput.newExecutionInput(); input.query(query); if (variables != null) { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java index 78dba90..9935400 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Cat.java @@ -30,7 +30,7 @@ public boolean getFur() { } @Query - @Capture("meow") + @Capture(color = "meow") public static Cat getCat() { return new Cat(); } @@ -46,4 +46,9 @@ public static Cat getUpper() { public static String allowed(String name) { return name; } + + @Query + public static String getNickname(@Input("TT") String nickName) { + return nickName; + } } From d7043a0da910f925c4cfd2617c64d0f296e9b038 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 11:57:55 +1300 Subject: [PATCH 051/112] Removing old unused classes --- .../graphql/builder/SDLDirective.java | 26 ---- .../graphql/builder/SDLProcessor.java | 115 ------------------ 2 files changed, 141 deletions(-) delete mode 100644 src/main/java/com/fleetpin/graphql/builder/SDLDirective.java delete mode 100644 src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java diff --git a/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java b/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java deleted file mode 100644 index 7307bad..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/SDLDirective.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 limitations under - * the License. - */ -package com.fleetpin.graphql.builder; - -import graphql.introspection.Introspection.DirectiveLocation; -import java.lang.annotation.Annotation; -import java.util.List; - -public interface SDLDirective extends DirectiveOperation { - public List validLocations(); - - public K build(T annotation, Class location); - - public default boolean repeatable() { - return false; - } -} diff --git a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java b/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java deleted file mode 100644 index 57dd39d..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/SDLProcessor.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 limitations under - * the License. - */ -package com.fleetpin.graphql.builder; - -import graphql.schema.GraphQLAppliedDirective; -import graphql.schema.GraphQLAppliedDirectiveArgument; -import graphql.schema.GraphQLArgument; -import graphql.schema.GraphQLDirective; -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; - -/** - * SDL directives are applied directly to the schema. They can be used to export - * extra information about methods By default graphql will not export this - * information but it can be enabled with new - * IntrospectionWithDirectivesSupport() - */ -public class SDLProcessor { - - private final SDLDirective factory; - private final GraphQLDirective directive; - private final List> builders; - - public SDLProcessor(SDLDirective factory, GraphQLDirective directive, List> builders) { - this.factory = (SDLDirective) factory; - this.directive = directive; - this.builders = builders; - } - - public static SDLProcessor build(EntityProcessor entityProcessor, SDLDirective directive) { - for (var inter : directive.getClass().getAnnotatedInterfaces()) { - if (inter.getType() instanceof ParameterizedType) { - var type = (ParameterizedType) inter.getType(); - if (type.getRawType() instanceof Class) { - var rawType = (Class) type.getRawType(); - if (SDLDirective.class.isAssignableFrom(rawType)) { - var annotation = (Class) type.getActualTypeArguments()[0]; - var arguments = (Class) type.getActualTypeArguments()[1]; - - var builder = GraphQLDirective.newDirective().name(annotation.getSimpleName()); - for (var location : directive.validLocations()) { - builder.validLocation(location); - } - builder.repeatable(directive.repeatable()); - List> builders = new ArrayList<>(); - for (Method method : arguments.getMethods()) { - if (method.getParameterCount() != 0) { - continue; - } - try { - var name = EntityUtil.getter(method); - name.ifPresent(n -> { - GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); - argument.name(n); - TypeMeta innerMeta = new TypeMeta(null, method.getReturnType(), method.getGenericReturnType()); - var argumentType = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, method.getAnnotations()); - argument.type(argumentType); - builder.argument(argument); - builders.add(object -> { - try { - return GraphQLAppliedDirectiveArgument - .newArgument() - .name(n) - .type(argumentType) - .valueProgrammatic(method.invoke(object)) - .build(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }); - }); - } catch (RuntimeException e) { - throw new RuntimeException("Failed to process method " + method, e); - } - } - return new SDLProcessor(directive, builder.build(), builders); - } - } - } - } - return null; - } - - public void apply(Annotation annotation, Class location, Consumer builder) { - var built = factory.build(annotation, location); - if (built != null) { - // need to call the methods building out the arguments - var arguments = GraphQLAppliedDirective.newDirective(); - arguments.name(directive.getName()); - for (var b : this.builders) { - arguments.argument(b.apply(built)); - } - builder.accept(arguments.build()); - } - } - - public GraphQLDirective getDirective() { - return directive; - } -} From d02db0ebd3f6159a1906feb34d03647426de38ab Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 11:58:12 +1300 Subject: [PATCH 052/112] New Tests added --- .../graphql/builder/type/directive/Input.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java new file mode 100644 index 0000000..60c962d --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java @@ -0,0 +1,20 @@ +package com.fleetpin.graphql.builder.type.directive; + +import com.fleetpin.graphql.builder.annotations.Directive; +import com.fleetpin.graphql.builder.annotations.DirectiveLocations; +import graphql.introspection.Introspection; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Directive +@Retention(RUNTIME) +@Target({ ElementType.PARAMETER }) +@DirectiveLocations(Introspection.DirectiveLocation.ARGUMENT_DEFINITION) +public @interface Input { + + String value(); +} From 47ed0cddd28fa423423956c6be98a7e50ffac602 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 12:00:03 +1300 Subject: [PATCH 053/112] Added back functionality for DirectiveCaller whilst waiting for more direction --- .../graphql/builder/DirectivesSchema.java | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java index 147188d..831dc3f 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java @@ -23,6 +23,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -35,27 +36,23 @@ class DirectivesSchema { private final Collection> global; private final Map, DirectiveCaller> targets; - private final Map, SDLDirective> schemaDirective; - private Map, SDLProcessor> sdlProcessors; // TODO: REMOVE private final Collection> directives; private Map, DirectiveProcessor> directiveProcessors; private DirectivesSchema( Collection> global, Map, DirectiveCaller> targets, - Map, SDLDirective> schemaDirective, Collection> directives ) { this.global = global; this.targets = targets; - this.schemaDirective = schemaDirective; this.directives = directives; } //TODO:mess of exceptions public static DirectivesSchema build(List> globalDirectives, Set> directiveTypes) throws ReflectiveOperationException { - Map, DirectiveCaller> targets = new HashMap<>(); // TODO: Remove this - Map, SDLDirective> graphqlDirective = new HashMap<>(); // TODO: Remove this and above line, keeping as to not cause abundant errors + Map, DirectiveCaller> targets = new HashMap<>(); + Collection> allDirectives = new ArrayList<>(); for (Class directiveType : directiveTypes) { @@ -68,10 +65,28 @@ public static DirectivesSchema build(List> globalDirectiv if (!directiveType.isAnnotationPresent(DirectiveLocations.class)) { throw new RuntimeException("@DirectiveLocations must be specified"); } - allDirectives.add((Class) directiveType); + + var directive = directiveType.getAnnotation(Directive.class); + Class> caller = directive.caller(); + + // If the caller hasn't been set and therefore is a default value means it is a regular GraphQLDirective + if (caller == Directive.Processor.class) { + allDirectives.add((Class) directiveType); + continue; + } + + Class target = (Class) ((ParameterizedType) caller.getGenericInterfaces()[0]).getActualTypeArguments()[0]; + + if (DirectiveCaller.class.isAssignableFrom(caller)) { + // TODO error for no zero args constructor + var callerInstance = (DirectiveCaller) caller.getConstructor().newInstance(); + targets.put((Class) directiveType, callerInstance); + } else { + allDirectives.add((Class) directiveType); + } } - return new DirectivesSchema(globalDirectives, targets, graphqlDirective, allDirectives); + return new DirectivesSchema(globalDirectives, targets, allDirectives); } private DirectiveCaller get(Annotation annotation) { @@ -210,15 +225,6 @@ public void addSchemaDirective(AnnotatedElement element, Class location, Cons } } - public void processSDL(EntityProcessor entityProcessor) { // TODO: Replace this with processDirectives - Map, SDLProcessor> sdlProcessors = new HashMap<>(); - - this.schemaDirective.forEach((k, v) -> { - sdlProcessors.put(k, SDLProcessor.build(entityProcessor, v)); - }); - this.sdlProcessors = sdlProcessors; - } - public void processDirectives(EntityProcessor ep) { // Replacement of processSDL Map, DirectiveProcessor> directiveProcessors = new HashMap<>(); From 5491b780e57b2649559a15c1b94e0c85bbd7f4d0 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 14:00:32 +1300 Subject: [PATCH 054/112] Merged @DirectiveLocations into @Directive --- .../graphql/builder/annotations/Directive.java | 4 +++- .../annotations/DirectiveLocations.java | 18 ------------------ .../graphql/builder/scalar/Capture.java | 4 +--- .../graphql/builder/type/directive/Admin.java | 5 ++--- .../builder/type/directive/Capture.java | 5 +---- .../graphql/builder/type/directive/Input.java | 4 +--- .../builder/type/directive/Uppercase.java | 4 +--- 7 files changed, 9 insertions(+), 35 deletions(-) delete mode 100644 src/main/java/com/fleetpin/graphql/builder/annotations/DirectiveLocations.java diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java index 3cee74d..b12782a 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java @@ -15,6 +15,7 @@ import com.fleetpin.graphql.builder.DirectiveCaller; import com.fleetpin.graphql.builder.DirectiveOperation; +import graphql.introspection.Introspection; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; @@ -26,9 +27,10 @@ @Target(ElementType.ANNOTATION_TYPE) public @interface Directive { Class> caller() default Directive.Processor.class; + Introspection.DirectiveLocation[] locations(); + // All of this below is ignored and checked for when building the schema/directives static class Processor implements DirectiveCaller { - @Override public Object process(Directive annotation, DataFetchingEnvironment env, DataFetcher fetcher) throws Exception { return null; diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/DirectiveLocations.java b/src/main/java/com/fleetpin/graphql/builder/annotations/DirectiveLocations.java deleted file mode 100644 index 148bb26..0000000 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/DirectiveLocations.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.fleetpin.graphql.builder.annotations; - -import graphql.introspection.Introspection.DirectiveLocation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -@Retention(RUNTIME) -@Target(ElementType.ANNOTATION_TYPE) -public @interface DirectiveLocations { - // TODO: Whole class should be temporary as Location can replace - // the 'Class> value();' in @Directive - - DirectiveLocation[] value(); -} diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java index d61d820..f557288 100644 --- a/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java @@ -12,7 +12,6 @@ package com.fleetpin.graphql.builder.scalar; import com.fleetpin.graphql.builder.annotations.Directive; -import com.fleetpin.graphql.builder.annotations.DirectiveLocations; import graphql.introspection.Introspection.DirectiveLocation; import java.lang.annotation.ElementType; @@ -21,10 +20,9 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; -@Directive() +@Directive(locations = DirectiveLocation.OBJECT) @Retention(RUNTIME) @Target({ ElementType.TYPE }) -@DirectiveLocations(DirectiveLocation.OBJECT) public @interface Capture { } diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java index 45db20b..e57e137 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java @@ -15,7 +15,6 @@ import com.fleetpin.graphql.builder.DirectiveCaller; import com.fleetpin.graphql.builder.annotations.Directive; -import com.fleetpin.graphql.builder.annotations.DirectiveLocations; import graphql.introspection.Introspection; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; @@ -23,10 +22,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Directive(caller = Admin.Processor.class) +@Directive(caller = Admin.Processor.class, + locations = {Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.SCHEMA}) @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) -@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.SCHEMA}) public @interface Admin { String value(); diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java index 0d93908..e8ce9dc 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java @@ -14,18 +14,15 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.fleetpin.graphql.builder.annotations.Directive; -import com.fleetpin.graphql.builder.annotations.DirectiveLocations; import graphql.introspection.Introspection; import graphql.introspection.Introspection.DirectiveLocation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import java.util.List; -@Directive +@Directive(locations = {Introspection.DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA}) @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) -@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA}) public @interface Capture { String color(); } diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java index 60c962d..747a5f1 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java @@ -1,7 +1,6 @@ package com.fleetpin.graphql.builder.type.directive; import com.fleetpin.graphql.builder.annotations.Directive; -import com.fleetpin.graphql.builder.annotations.DirectiveLocations; import graphql.introspection.Introspection; import java.lang.annotation.ElementType; @@ -10,10 +9,9 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; -@Directive +@Directive(locations = Introspection.DirectiveLocation.ARGUMENT_DEFINITION) @Retention(RUNTIME) @Target({ ElementType.PARAMETER }) -@DirectiveLocations(Introspection.DirectiveLocation.ARGUMENT_DEFINITION) public @interface Input { String value(); diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java index 7f3c5c7..0039b23 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java @@ -12,7 +12,6 @@ package com.fleetpin.graphql.builder.type.directive; import com.fleetpin.graphql.builder.annotations.Directive; -import com.fleetpin.graphql.builder.annotations.DirectiveLocations; import graphql.introspection.Introspection; import java.lang.annotation.ElementType; @@ -21,10 +20,9 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; -@Directive +@Directive(locations = Introspection.DirectiveLocation.FIELD_DEFINITION) @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) -@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION}) public @interface Uppercase { } From b1c2ffd17f0451424a7aa7762aa2980d47db3443 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 14:00:46 +1300 Subject: [PATCH 055/112] Merged @DirectiveLocations into @Directive --- .../java/com/fleetpin/graphql/builder/DirectiveProcessor.java | 4 ++-- .../java/com/fleetpin/graphql/builder/DirectivesSchema.java | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java index 9394574..6584d75 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java @@ -1,6 +1,6 @@ package com.fleetpin.graphql.builder; -import com.fleetpin.graphql.builder.annotations.DirectiveLocations; +import com.fleetpin.graphql.builder.annotations.Directive; import graphql.introspection.Introspection; import graphql.schema.*; @@ -28,7 +28,7 @@ public static DirectiveProcessor build(EntityProcessor entityProcessor, Class> globalDirectiv if (!directiveType.isAnnotation()) { throw new RuntimeException("@Directive Annotation must only be placed on annotations"); } - if (!directiveType.isAnnotationPresent(DirectiveLocations.class)) { - throw new RuntimeException("@DirectiveLocations must be specified"); - } var directive = directiveType.getAnnotation(Directive.class); Class> caller = directive.caller(); From 21c721f156a89a33fc4dc008a8207077ce33b6cc Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Tue, 5 Dec 2023 14:06:19 +1300 Subject: [PATCH 056/112] Code cleanup + removing TODOs that have been done --- .../graphql/builder/DirectiveProcessor.java | 7 ---- .../graphql/builder/DirectivesSchema.java | 39 ++++++++----------- .../graphql/builder/MethodProcessor.java | 1 - .../graphql/builder/SchemaBuilder.java | 7 ++-- 4 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java index 6584d75..40c1a10 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java @@ -15,17 +15,13 @@ public class DirectiveProcessor { private final GraphQLDirective directive; private final Map> builders; -// private final Map methodTypes; TODO: Deal with this too public DirectiveProcessor(GraphQLDirective directive, Map> builders) { this.directive = directive; this.builders = builders; -// this.methodTypes = methodTypes; TODO: DEAL WITH THIS } public static DirectiveProcessor build(EntityProcessor entityProcessor, Class directive) { - var processedDirectives = new ArrayList(); - var builder = GraphQLDirective.newDirective().name(directive.getSimpleName()); var validLocations = directive.getAnnotation(Directive.class).locations(); @@ -34,9 +30,6 @@ public static DirectiveProcessor build(EntityProcessor entityProcessor, Class methodTypes = new HashMap<>(); - // Go through each argument and add name/type to directive var methods = directive.getDeclaredMethods(); Map> builders = new HashMap<>(); diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java index 93aa7d8..9fa0e3b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java @@ -71,8 +71,6 @@ public static DirectivesSchema build(List> globalDirectiv continue; } - Class target = (Class) ((ParameterizedType) caller.getGenericInterfaces()[0]).getActualTypeArguments()[0]; - if (DirectiveCaller.class.isAssignableFrom(caller)) { // TODO error for no zero args constructor var callerInstance = (DirectiveCaller) caller.getConstructor().newInstance(); @@ -95,8 +93,7 @@ private DataFetcher wrap(DirectiveCaller directive, }; } - public Stream getSchemaDirective() { // TODO: This is where the SchemaBuilder turns the annotations into GraphQLDirectives -// return sdlProcessors.values().stream().map(SDLProcessor::getDirective); TODO: REMOVE + public Stream getSchemaDirective() { return directiveProcessors.values().stream().map(DirectiveProcessor::getDirective); } @@ -104,24 +101,22 @@ private DataFetcher wrap(RestrictTypeFactory directive, DataFetcher fet //TODO: hate having this cache here would love to scope against the env object but nothing to hook into dataload caused global leak Map> cache = Collections.synchronizedMap(new WeakHashMap<>()); - return env -> { - return cache - .computeIfAbsent(env, key -> directive.create(key).thenApply(t -> t)) - .thenCompose(restrict -> { - try { - Object response = fetcher.get(env); - if (response instanceof CompletionStage) { - return ((CompletionStage) response).thenCompose(r -> applyRestrict(restrict, r)); - } - return applyRestrict(restrict, response); - } catch (Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } - throw new RuntimeException(e); + return env -> cache + .computeIfAbsent(env, key -> directive.create(key).thenApply(t -> t)) + .thenCompose(restrict -> { + try { + Object response = fetcher.get(env); + if (response instanceof CompletionStage) { + return ((CompletionStage) response).thenCompose(r -> applyRestrict(restrict, r)); } - }); - }; + return applyRestrict(restrict, response); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + throw new RuntimeException(e); + } + }); } public boolean target(Method method, TypeMeta meta) { @@ -208,7 +203,7 @@ private static CompletableFuture> all(List> toR ); } - public void addSchemaDirective(AnnotatedElement element, Class location, Consumer builder) { // TODO: This is also probably important + public void addSchemaDirective(AnnotatedElement element, Class location, Consumer builder) { for (Annotation annotation : element.getAnnotations()) { var processor = this.directiveProcessors.get(annotation.annotationType()); if (processor != null) { diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index 2aa4d1f..fbf633b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -97,7 +97,6 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM argument.description(description.value()); } - // TODO: TEST THE PARAMETER DIRECTIVE var parameter = method.getParameters()[i]; for (Annotation annotation : parameter.getAnnotations()) { // Check to see if the annotation is a directive diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index 2f22ea4..98aeacc 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -41,8 +41,7 @@ private SchemaBuilder(DataFetcherRunner dataFetcherRunner, List> types) { @@ -83,10 +82,10 @@ private graphql.schema.GraphQLSchema.Builder build(Set builder.additionalDirective(directive)); // TODO: This is where all of the directives are added into the actual schema + directives.getSchemaDirective().forEach(directive -> builder.additionalDirective(directive)); for (var schema : schemaConfiguration) { - this.directives.addSchemaDirective(schema, schema, builder::withSchemaAppliedDirective); // TODO: This part too. + this.directives.addSchemaDirective(schema, schema, builder::withSchemaAppliedDirective); } return builder; } From be45bd989622b3ced7ec7b5217fbe4722f5fb21b Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Wed, 6 Dec 2023 08:31:00 +1300 Subject: [PATCH 057/112] Refactoring Directive annotation and the logic behind it to separate locations and DataFetcherWrapper --- .../graphql/builder/DirectiveProcessor.java | 5 +++- .../graphql/builder/DirectivesSchema.java | 29 ++++++++----------- .../annotations/DataFetcherWrapper.java | 15 ++++++++++ .../builder/annotations/Directive.java | 16 ++++------ .../graphql/builder/scalar/Capture.java | 2 +- .../graphql/builder/type/directive/Admin.java | 4 +-- .../builder/type/directive/Capture.java | 2 +- .../graphql/builder/type/directive/Input.java | 2 +- .../builder/type/directive/Uppercase.java | 2 +- 9 files changed, 43 insertions(+), 34 deletions(-) create mode 100644 src/main/java/com/fleetpin/graphql/builder/annotations/DataFetcherWrapper.java diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java index 40c1a10..f2e140c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java @@ -24,12 +24,15 @@ public DirectiveProcessor(GraphQLDirective directive, Map directive) { var builder = GraphQLDirective.newDirective().name(directive.getSimpleName()); - var validLocations = directive.getAnnotation(Directive.class).locations(); + var validLocations = directive.getAnnotation(Directive.class).value(); // loop through and add valid locations for (Introspection.DirectiveLocation location : validLocations) { builder.validLocation(location); } + // Check for repeatable tag in annotation and add it + builder.repeatable(directive.getAnnotation(Directive.class).repeatable()); + // Go through each argument and add name/type to directive var methods = directive.getDeclaredMethods(); Map> builders = new HashMap<>(); diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java index 9fa0e3b..d35b54b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java @@ -11,6 +11,7 @@ */ package com.fleetpin.graphql.builder; +import com.fleetpin.graphql.builder.annotations.DataFetcherWrapper; import com.fleetpin.graphql.builder.annotations.Directive; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; @@ -55,29 +56,23 @@ public static DirectivesSchema build(List> globalDirectiv Collection> allDirectives = new ArrayList<>(); for (Class directiveType : directiveTypes) { + if (directiveType.isAnnotationPresent(DataFetcherWrapper.class)) { + // TODO: Add logic for DataFetcherWrapper here + Class> caller = directiveType.getAnnotation(DataFetcherWrapper.class).value(); + if (DirectiveCaller.class.isAssignableFrom(caller)) { + // TODO error for no zero args constructor + var callerInstance = (DirectiveCaller) caller.getConstructor().newInstance(); + targets.put((Class) directiveType, callerInstance); + } + continue; + } if (!directiveType.isAnnotationPresent(Directive.class)) { continue; } if (!directiveType.isAnnotation()) { throw new RuntimeException("@Directive Annotation must only be placed on annotations"); } - - var directive = directiveType.getAnnotation(Directive.class); - Class> caller = directive.caller(); - - // If the caller hasn't been set and therefore is a default value means it is a regular GraphQLDirective - if (caller == Directive.Processor.class) { - allDirectives.add((Class) directiveType); - continue; - } - - if (DirectiveCaller.class.isAssignableFrom(caller)) { - // TODO error for no zero args constructor - var callerInstance = (DirectiveCaller) caller.getConstructor().newInstance(); - targets.put((Class) directiveType, callerInstance); - } else { - allDirectives.add((Class) directiveType); - } + allDirectives.add((Class) directiveType); } return new DirectivesSchema(globalDirectives, targets, allDirectives); diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/DataFetcherWrapper.java b/src/main/java/com/fleetpin/graphql/builder/annotations/DataFetcherWrapper.java new file mode 100644 index 0000000..2b2d5fe --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/DataFetcherWrapper.java @@ -0,0 +1,15 @@ +package com.fleetpin.graphql.builder.annotations; + +import com.fleetpin.graphql.builder.DirectiveOperation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface DataFetcherWrapper { + Class> value(); +} diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java index b12782a..a49189d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java @@ -26,14 +26,10 @@ @Retention(RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Directive { - Class> caller() default Directive.Processor.class; - Introspection.DirectiveLocation[] locations(); - - // All of this below is ignored and checked for when building the schema/directives - static class Processor implements DirectiveCaller { - @Override - public Object process(Directive annotation, DataFetchingEnvironment env, DataFetcher fetcher) throws Exception { - return null; - } - } + Introspection.DirectiveLocation[] value(); + + boolean repeatable() default false; + } + + diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java index f557288..b910067 100644 --- a/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java @@ -20,7 +20,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; -@Directive(locations = DirectiveLocation.OBJECT) +@Directive(DirectiveLocation.OBJECT) @Retention(RUNTIME) @Target({ ElementType.TYPE }) public @interface Capture { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java index e57e137..7628bfe 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java @@ -14,6 +14,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.fleetpin.graphql.builder.DirectiveCaller; +import com.fleetpin.graphql.builder.annotations.DataFetcherWrapper; import com.fleetpin.graphql.builder.annotations.Directive; import graphql.introspection.Introspection; import graphql.schema.DataFetcher; @@ -22,8 +23,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Directive(caller = Admin.Processor.class, - locations = {Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.SCHEMA}) +@DataFetcherWrapper(Admin.Processor.class) @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) public @interface Admin { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java index e8ce9dc..6e25446 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java @@ -20,7 +20,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Directive(locations = {Introspection.DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA}) +@Directive({Introspection.DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA}) @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) public @interface Capture { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java index 747a5f1..07d8eef 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java @@ -9,7 +9,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; -@Directive(locations = Introspection.DirectiveLocation.ARGUMENT_DEFINITION) +@Directive(Introspection.DirectiveLocation.ARGUMENT_DEFINITION) @Retention(RUNTIME) @Target({ ElementType.PARAMETER }) public @interface Input { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java index 0039b23..f420885 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java @@ -20,7 +20,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; -@Directive(locations = Introspection.DirectiveLocation.FIELD_DEFINITION) +@Directive(Introspection.DirectiveLocation.FIELD_DEFINITION) @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) public @interface Uppercase { From 49b075df82c03deb2e3b6ef5a4bf3ab1d1ae6917 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Wed, 6 Dec 2023 08:31:55 +1300 Subject: [PATCH 058/112] Refactoring Directive annotation and the logic behind it to separate locations and DataFetcherWrapper --- .../com/fleetpin/graphql/builder/SchemaBuilder.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index 98aeacc..5736a97 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -11,14 +11,7 @@ */ package com.fleetpin.graphql.builder; -import com.fleetpin.graphql.builder.annotations.Directive; -import com.fleetpin.graphql.builder.annotations.Entity; -import com.fleetpin.graphql.builder.annotations.Mutation; -import com.fleetpin.graphql.builder.annotations.Query; -import com.fleetpin.graphql.builder.annotations.Restrict; -import com.fleetpin.graphql.builder.annotations.Restricts; -import com.fleetpin.graphql.builder.annotations.SchemaOption; -import com.fleetpin.graphql.builder.annotations.Subscription; +import com.fleetpin.graphql.builder.annotations.*; import graphql.schema.GraphQLScalarType; import graphql.schema.GraphQLSchema; import java.lang.reflect.Method; @@ -139,6 +132,7 @@ public GraphQLSchema.Builder build() { Set> schemaConfiguration = reflections.getSubTypesOf(SchemaConfiguration.class); Set> directivesTypes = reflections.getTypesAnnotatedWith(Directive.class); + directivesTypes.addAll(reflections.getTypesAnnotatedWith(DataFetcherWrapper.class)); Set> restrict = reflections.getTypesAnnotatedWith(Restrict.class); Set> restricts = reflections.getTypesAnnotatedWith(Restricts.class); From 4566b29227672549d36c78332e406a3ee9fe77a3 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Wed, 6 Dec 2023 08:51:26 +1300 Subject: [PATCH 059/112] Update the argument level directive handling so that they can take more than just Strings as arguments --- .../graphql/builder/MethodProcessor.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index fbf633b..5bbc042 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -107,18 +107,8 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM // Get the values out of the directive annotation var methods = annotationType.getDeclaredMethods(); - var appliedDirective = new GraphQLAppliedDirective.Builder() - .name(annotationType.getSimpleName()); - for (var definedMethod : methods) { - var name = definedMethod.getName(); - var value = definedMethod.invoke(annotation); - if (value == null) {continue;} - appliedDirective.argument(GraphQLAppliedDirectiveArgument.newArgument() - .name(name) - .type(Scalars.GraphQLString) - .valueLiteral(new StringValue((String) value)) - .build()); - } + // Get the applied directive and add it to the argument + var appliedDirective = getAppliedDirective(annotation, annotationType, methods); argument.withAppliedDirective(appliedDirective); } @@ -132,6 +122,25 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM return field; } + private GraphQLAppliedDirective getAppliedDirective(Annotation annotation, Class annotationType, Method[] methods) throws IllegalAccessException, InvocationTargetException { + var appliedDirective = new GraphQLAppliedDirective.Builder() + .name(annotationType.getSimpleName()); + for (var definedMethod : methods) { + var name = definedMethod.getName(); + var value = definedMethod.invoke(annotation); + if (value == null) {continue;} + + TypeMeta innerMeta = new TypeMeta(null, definedMethod.getReturnType(), definedMethod.getGenericReturnType()); + var argumentType = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, definedMethod.getAnnotations()); + appliedDirective.argument(GraphQLAppliedDirectiveArgument.newArgument() + .name(name) + .type(argumentType) + .valueProgrammatic(value) + .build()); + } + return appliedDirective.build(); + } + private DataFetcher buildFetcher(DirectivesSchema diretives, AuthorizerSchema authorizer, Method method, TypeMeta meta) { DataFetcher fetcher = buildDataFetcher(meta, method); fetcher = diretives.wrap(method, meta, fetcher); From 7ae274328c0990516d6c33652bb51682a087d00e Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Wed, 6 Dec 2023 08:52:34 +1300 Subject: [PATCH 060/112] Remove finished // TODO --- src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java index d35b54b..6fb526e 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java @@ -57,7 +57,6 @@ public static DirectivesSchema build(List> globalDirectiv Collection> allDirectives = new ArrayList<>(); for (Class directiveType : directiveTypes) { if (directiveType.isAnnotationPresent(DataFetcherWrapper.class)) { - // TODO: Add logic for DataFetcherWrapper here Class> caller = directiveType.getAnnotation(DataFetcherWrapper.class).value(); if (DirectiveCaller.class.isAssignableFrom(caller)) { // TODO error for no zero args constructor From d71378f7a1ff5fa51e1f60f2b3381e7e8ff2900f Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Thu, 7 Dec 2023 09:18:38 +1300 Subject: [PATCH 061/112] Update release.yml --- .github/workflows/release.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e4c4b30..edb0fb3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,19 @@ name: Release permissions: contents: write -on: - workflow_dispatch +on: + workflow_dispatch: + inputs: + releaseType: + type: choice + description: "Release type" + required: true + default: minor + options: + - patch + - minor + - major + jobs: release: runs-on: ubuntu-latest @@ -13,7 +24,7 @@ jobs: with: java-version: 11 - name: Release - uses: qcastel/github-actions-maven-release@v1.11.1 + uses: qcastel/github-actions-maven-release@v1.12.41 with: release-branch-name: "master" maven-args: "-P sonatype" @@ -27,5 +38,7 @@ jobs: maven-repo-server-id: sonatype maven-repo-server-username: ${{ secrets.MVN_REPO_PRIVATE_REPO_USER }} maven-repo-server-password: ${{ secrets.MVN_REPO_PRIVATE_REPO_PASSWORD }} - + version-major: ${{ github.event.inputs.releaseType == 'major '}} + version-minor: ${{ github.event.inputs.releaseType == 'minor '}} + version-patch: ${{ github.event.inputs.releaseType == 'patch '}} access-token: ${{ secrets.GITHUB_TOKEN }} From 8994d9fea7b1ecf084e0a686778356ed55932b1d Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Thu, 7 Dec 2023 10:53:31 +1300 Subject: [PATCH 062/112] Ran mvn prettier --- .../graphql/builder/DirectiveProcessor.java | 154 +++++++++--------- .../graphql/builder/DirectivesSchema.java | 41 +++-- .../graphql/builder/MethodProcessor.java | 20 +-- .../annotations/DataFetcherWrapper.java | 7 +- .../builder/annotations/Directive.java | 4 - .../graphql/builder/DirectiveTest.java | 21 +-- .../graphql/builder/scalar/Capture.java | 6 +- .../graphql/builder/type/directive/Admin.java | 1 + .../builder/type/directive/Capture.java | 2 +- .../graphql/builder/type/directive/Input.java | 8 +- .../builder/type/directive/Uppercase.java | 6 +- 11 files changed, 122 insertions(+), 148 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java index f2e140c..18d429d 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectiveProcessor.java @@ -3,7 +3,6 @@ import com.fleetpin.graphql.builder.annotations.Directive; import graphql.introspection.Introspection; import graphql.schema.*; - import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -13,83 +12,78 @@ public class DirectiveProcessor { - private final GraphQLDirective directive; - private final Map> builders; - - public DirectiveProcessor(GraphQLDirective directive, Map> builders) { - this.directive = directive; - this.builders = builders; - } - - public static DirectiveProcessor build(EntityProcessor entityProcessor, Class directive) { - - var builder = GraphQLDirective.newDirective().name(directive.getSimpleName()); - var validLocations = directive.getAnnotation(Directive.class).value(); - // loop through and add valid locations - for (Introspection.DirectiveLocation location : validLocations) { - builder.validLocation(location); - } - - // Check for repeatable tag in annotation and add it - builder.repeatable(directive.getAnnotation(Directive.class).repeatable()); - - // Go through each argument and add name/type to directive - var methods = directive.getDeclaredMethods(); - Map> builders = new HashMap<>(); - for (Method method : methods) { - if (method.getParameterCount() != 0) { - continue; - } - var name = method.getName(); - - GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); - argument.name(name); - - // Get the type of the argument from the return type of the method - TypeMeta innerMeta = new TypeMeta(null, method.getReturnType(), method.getGenericReturnType()); - var argumentType = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, method.getAnnotations()); - argument.type(argumentType); - - // Add the argument to the directive builder to be used for declaration - builder.argument(argument); - - // Add a builder to the builders list (in order to populate applied directives) - builders.put(name, object -> { - try { - return GraphQLAppliedDirectiveArgument.newArgument() - .name(name) - .type(argumentType) - .valueProgrammatic(method.invoke(object)) - .build(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }); - - - - } - return new DirectiveProcessor(builder.build(), builders); - } - - public void apply(Annotation annotation, Consumer builder) throws InvocationTargetException, IllegalAccessException { - var methods = annotation.annotationType().getDeclaredMethods(); - - // Create a new AppliedDirective which we will populate with the set values - var arguments = GraphQLAppliedDirective.newDirective(); - arguments.name(directive.getName()); - - // To get the value we loop through each method and get the method name and value - for (Method m : methods) { - // Using the builder created earlier populate the values of each method. - arguments.argument(builders.get(m.getName()).apply(annotation)); - } - - // Add the argument to the Builder - builder.accept(arguments.build()); - } - - public GraphQLDirective getDirective() { - return this.directive; - } + private final GraphQLDirective directive; + private final Map> builders; + + public DirectiveProcessor(GraphQLDirective directive, Map> builders) { + this.directive = directive; + this.builders = builders; + } + + public static DirectiveProcessor build(EntityProcessor entityProcessor, Class directive) { + var builder = GraphQLDirective.newDirective().name(directive.getSimpleName()); + var validLocations = directive.getAnnotation(Directive.class).value(); + // loop through and add valid locations + for (Introspection.DirectiveLocation location : validLocations) { + builder.validLocation(location); + } + + // Check for repeatable tag in annotation and add it + builder.repeatable(directive.getAnnotation(Directive.class).repeatable()); + + // Go through each argument and add name/type to directive + var methods = directive.getDeclaredMethods(); + Map> builders = new HashMap<>(); + for (Method method : methods) { + if (method.getParameterCount() != 0) { + continue; + } + var name = method.getName(); + + GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); + argument.name(name); + + // Get the type of the argument from the return type of the method + TypeMeta innerMeta = new TypeMeta(null, method.getReturnType(), method.getGenericReturnType()); + var argumentType = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, method.getAnnotations()); + argument.type(argumentType); + + // Add the argument to the directive builder to be used for declaration + builder.argument(argument); + + // Add a builder to the builders list (in order to populate applied directives) + builders.put( + name, + object -> { + try { + return GraphQLAppliedDirectiveArgument.newArgument().name(name).type(argumentType).valueProgrammatic(method.invoke(object)).build(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + ); + } + return new DirectiveProcessor(builder.build(), builders); + } + + public void apply(Annotation annotation, Consumer builder) throws InvocationTargetException, IllegalAccessException { + var methods = annotation.annotationType().getDeclaredMethods(); + + // Create a new AppliedDirective which we will populate with the set values + var arguments = GraphQLAppliedDirective.newDirective(); + arguments.name(directive.getName()); + + // To get the value we loop through each method and get the method name and value + for (Method m : methods) { + // Using the builder created earlier populate the values of each method. + arguments.argument(builders.get(m.getName()).apply(annotation)); + } + + // Add the argument to the Builder + builder.accept(arguments.build()); + } + + public GraphQLDirective getDirective() { + return this.directive; + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java index 6fb526e..ce6e59e 100644 --- a/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java +++ b/src/main/java/com/fleetpin/graphql/builder/DirectivesSchema.java @@ -17,8 +17,6 @@ import graphql.schema.DataFetchingEnvironment; import graphql.schema.GraphQLAppliedDirective; import graphql.schema.GraphQLDirective; -import org.reactivestreams.Publisher; - import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.InvocationTargetException; @@ -31,6 +29,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.reactivestreams.Publisher; class DirectivesSchema { @@ -53,7 +52,6 @@ private DirectivesSchema( public static DirectivesSchema build(List> globalDirectives, Set> directiveTypes) throws ReflectiveOperationException { Map, DirectiveCaller> targets = new HashMap<>(); - Collection> allDirectives = new ArrayList<>(); for (Class directiveType : directiveTypes) { if (directiveType.isAnnotationPresent(DataFetcherWrapper.class)) { @@ -95,22 +93,23 @@ private DataFetcher wrap(RestrictTypeFactory directive, DataFetcher fet //TODO: hate having this cache here would love to scope against the env object but nothing to hook into dataload caused global leak Map> cache = Collections.synchronizedMap(new WeakHashMap<>()); - return env -> cache - .computeIfAbsent(env, key -> directive.create(key).thenApply(t -> t)) - .thenCompose(restrict -> { - try { - Object response = fetcher.get(env); - if (response instanceof CompletionStage) { - return ((CompletionStage) response).thenCompose(r -> applyRestrict(restrict, r)); - } - return applyRestrict(restrict, response); - } catch (Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; + return env -> + cache + .computeIfAbsent(env, key -> directive.create(key).thenApply(t -> t)) + .thenCompose(restrict -> { + try { + Object response = fetcher.get(env); + if (response instanceof CompletionStage) { + return ((CompletionStage) response).thenCompose(r -> applyRestrict(restrict, r)); + } + return applyRestrict(restrict, response); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + throw new RuntimeException(e); } - throw new RuntimeException(e); - } - }); + }); } public boolean target(Method method, TypeMeta meta) { @@ -206,16 +205,14 @@ public void addSchemaDirective(AnnotatedElement element, Class location, Cons } catch (InvocationTargetException | IllegalAccessException e) { throw new RuntimeException("Could not process applied directive: " + location.getName()); } - } + } } } public void processDirectives(EntityProcessor ep) { // Replacement of processSDL Map, DirectiveProcessor> directiveProcessors = new HashMap<>(); - this.directives.forEach(dir -> - directiveProcessors.put(dir, DirectiveProcessor.build(ep, dir))); + this.directives.forEach(dir -> directiveProcessors.put(dir, DirectiveProcessor.build(ep, dir))); this.directiveProcessors = directiveProcessors; - } } diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index 5bbc042..b1a203a 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -8,7 +8,6 @@ import graphql.language.StringValue; import graphql.schema.*; import graphql.schema.GraphQLFieldDefinition.Builder; - import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -63,7 +62,8 @@ void process(AuthorizerSchema authorizer, Method method) throws ReflectiveOperat object.field(process(authorizer, coordinates, null, method)); } - Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeMeta parentMeta, Method method) throws InvocationTargetException, IllegalAccessException { + Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeMeta parentMeta, Method method) + throws InvocationTargetException, IllegalAccessException { GraphQLFieldDefinition.Builder field = GraphQLFieldDefinition.newFieldDefinition(); entityProcessor.addSchemaDirective(method, method.getDeclaringClass(), field::withAppliedDirective); @@ -122,21 +122,19 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM return field; } - private GraphQLAppliedDirective getAppliedDirective(Annotation annotation, Class annotationType, Method[] methods) throws IllegalAccessException, InvocationTargetException { - var appliedDirective = new GraphQLAppliedDirective.Builder() - .name(annotationType.getSimpleName()); + private GraphQLAppliedDirective getAppliedDirective(Annotation annotation, Class annotationType, Method[] methods) + throws IllegalAccessException, InvocationTargetException { + var appliedDirective = new GraphQLAppliedDirective.Builder().name(annotationType.getSimpleName()); for (var definedMethod : methods) { var name = definedMethod.getName(); var value = definedMethod.invoke(annotation); - if (value == null) {continue;} + if (value == null) { + continue; + } TypeMeta innerMeta = new TypeMeta(null, definedMethod.getReturnType(), definedMethod.getGenericReturnType()); var argumentType = entityProcessor.getEntity(innerMeta).getInputType(innerMeta, definedMethod.getAnnotations()); - appliedDirective.argument(GraphQLAppliedDirectiveArgument.newArgument() - .name(name) - .type(argumentType) - .valueProgrammatic(value) - .build()); + appliedDirective.argument(GraphQLAppliedDirectiveArgument.newArgument().name(name).type(argumentType).valueProgrammatic(value).build()); } return appliedDirective.build(); } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/DataFetcherWrapper.java b/src/main/java/com/fleetpin/graphql/builder/annotations/DataFetcherWrapper.java index 2b2d5fe..1f11f01 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/DataFetcherWrapper.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/DataFetcherWrapper.java @@ -1,15 +1,14 @@ package com.fleetpin.graphql.builder.annotations; -import com.fleetpin.graphql.builder.DirectiveOperation; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.fleetpin.graphql.builder.DirectiveOperation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - @Retention(RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface DataFetcherWrapper { - Class> value(); + Class> value(); } diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java index a49189d..7dbd689 100644 --- a/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/Directive.java @@ -18,7 +18,6 @@ import graphql.introspection.Introspection; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -29,7 +28,4 @@ Introspection.DirectiveLocation[] value(); boolean repeatable() default false; - } - - diff --git a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java index b560e41..96dcb16 100644 --- a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java @@ -11,20 +11,19 @@ */ package com.fleetpin.graphql.builder; +import static org.junit.jupiter.api.Assertions.*; + import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; import graphql.introspection.IntrospectionWithDirectivesSupport; import graphql.schema.FieldCoordinates; import graphql.schema.GraphQLSchema; -import org.junit.jupiter.api.Test; - import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class DirectiveTest { @@ -84,24 +83,20 @@ public void testDirectiveArgument() { @Test public void testDirectiveArgumentDefinition() { - Map response = execute("query IntrospectionQuery { __schema { directives { name locations args { name } } } }", - null).getData(); - List> dir = (List>) ((Map)response.get("__schema")).get("directives"); + Map response = execute("query IntrospectionQuery { __schema { directives { name locations args { name } } } }", null).getData(); + List> dir = (List>) ((Map) response.get("__schema")).get("directives"); LinkedHashMap input = dir.stream().filter(map -> map.get("name").equals("Input")).collect(Collectors.toList()).get(0); assertEquals(7, dir.size()); - assertEquals("ARGUMENT_DEFINITION", ((List)input.get("locations")).get(0)); - assertEquals(1, ((List)input.get("args")).size()); - + assertEquals("ARGUMENT_DEFINITION", ((List) input.get("locations")).get(0)); + assertEquals(1, ((List) input.get("args")).size()); //getNickname(nickName: String! @Input(value : "TT")): String! //directive @Input(value: String!) on ARGUMENT_DEFINITION } private ExecutionResult execute(String query, Map variables) { GraphQLSchema preSchema = SchemaBuilder.builder().classpath("com.fleetpin.graphql.builder.type.directive").build().build(); - GraphQL schema = GraphQL - .newGraphQL(new IntrospectionWithDirectivesSupport().apply(preSchema)) - .build(); + GraphQL schema = GraphQL.newGraphQL(new IntrospectionWithDirectivesSupport().apply(preSchema)).build(); var input = ExecutionInput.newExecutionInput(); input.query(query); diff --git a/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java index b910067..94a1f68 100644 --- a/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/scalar/Capture.java @@ -11,18 +11,16 @@ */ package com.fleetpin.graphql.builder.scalar; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + import com.fleetpin.graphql.builder.annotations.Directive; import graphql.introspection.Introspection.DirectiveLocation; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - @Directive(DirectiveLocation.OBJECT) @Retention(RUNTIME) @Target({ ElementType.TYPE }) public @interface Capture { - } diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java index 7628bfe..a1e1cfa 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Admin.java @@ -30,6 +30,7 @@ String value(); static class Processor implements DirectiveCaller { + @Override public Object process(Admin annotation, DataFetchingEnvironment env, DataFetcher fetcher) throws Exception { if (env.getArgument("name").equals(annotation.value())) { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java index 6e25446..daa0d34 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Capture.java @@ -20,7 +20,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Directive({Introspection.DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA}) +@Directive({ Introspection.DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.SCHEMA }) @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) public @interface Capture { diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java index 07d8eef..c83a360 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Input.java @@ -1,18 +1,16 @@ package com.fleetpin.graphql.builder.type.directive; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + import com.fleetpin.graphql.builder.annotations.Directive; import graphql.introspection.Introspection; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - @Directive(Introspection.DirectiveLocation.ARGUMENT_DEFINITION) @Retention(RUNTIME) @Target({ ElementType.PARAMETER }) public @interface Input { - - String value(); + String value(); } diff --git a/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java index f420885..9886ba3 100644 --- a/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java +++ b/src/test/java/com/fleetpin/graphql/builder/type/directive/Uppercase.java @@ -11,18 +11,16 @@ */ package com.fleetpin.graphql.builder.type.directive; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + import com.fleetpin.graphql.builder.annotations.Directive; import graphql.introspection.Introspection; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - @Directive(Introspection.DirectiveLocation.FIELD_DEFINITION) @Retention(RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) public @interface Uppercase { - } From 0c27e75e87887feaf91c7bb7367661e5584ab827 Mon Sep 17 00:00:00 2001 From: "callum.rutledge" Date: Thu, 7 Dec 2023 11:11:22 +1300 Subject: [PATCH 063/112] Updated README.md to represent new changes --- README.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 44171fe..8b5d6ed 100644 --- a/README.md +++ b/README.md @@ -337,12 +337,42 @@ public class AssetRestrict implements RestrictType { ## Directives These are similar to GraphQL directives but just implemented on the java model -You define a custom annotation and add the `@Directive` to it -This annotation in then passed into the DirectiveCaller allowing you to add options to the annotation if need be +You define a custom annotation and add the `@Directive` to it. +The directive annotation must contain an array of DirectiveLocations which will be used in the GraphQL definition. +Any function defined in the annotation will be placed on the schema definition as an argument. ```java @Retention(RUNTIME) -@Directive(AdminOnly.AdminOnlyDirective.class) +@Directive( { Introspection.DirectiveLocation.FIELD_DEFINITION } ) +public @interface CustomDirective { + String input(); +} +``` +This directive can now be placed where set: +```java +@Query +@CustomDirective(input = "Custom Directive Contents") +public static String sayHello() { + return "Hello world"; +} +``` +Which will then end up on the schema like so +```graphql +directive @CustomDirective(input: String!) on FIELD_DEFINITION + +type Query { + sayHello: String! @CustomDirective(input: "Custom Directive Contents") +} +``` + +## DataFetcherWrapper +Similar to the setup of a Directive the DataFetcherWrapper is created as an +annotation. This annotation is then passed into the DirectiveCaller allowing +you to add options to the annotation if need be + +```java +@Retention(RUNTIME) +@DataFetcherWrapper(AdminOnly.AdminOnlyDirective.class) public @interface AdminOnly { ... } From 4f46c12994788ef316d68f2fa18b2674115c0ab1 Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:09:01 +1300 Subject: [PATCH 064/112] Create release.sh --- scripts/release.sh | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 scripts/release.sh diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100644 index 0000000..54aa9a0 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +set -e + +# avoid the release loop by checking if the latest commit is a release commit +readonly local last_release_commit_hash=$(git log --author="$GIT_RELEASE_BOT_NAME" --pretty=format:"%H" -1) +echo "Last $GIT_RELEASE_BOT_NAME commit: ${last_release_commit_hash}" +echo "Current commit: ${GITHUB_SHA}" +if [[ "${last_release_commit_hash}" = "${GITHUB_SHA}" ]]; then + echo "Skipping for $GIT_RELEASE_BOT_NAME commit. Release failed as no changes since last release." + exit 1 +fi + +# This script will do a release of the artifact according to http://maven.apache.org/maven-release/maven-release-plugin/ +echo "Setup git user name to '$GIT_RELEASE_BOT_NAME'" +git config --global user.name "$GIT_RELEASE_BOT_NAME"; +echo "Setup git user email to '$GIT_RELEASE_BOT_EMAIL'" +git config --global user.email "$GIT_RELEASE_BOT_EMAIL"; + +# Setup next version +MAVEN_OPTION=$( + case "$RELEASE_TYPE" in + ("minor") echo "$MAVEN_OPTION \ + -DdevelopmentVersion=\${parsedVersion.majorVersion}.\${parsedVersion.nextMinorVersion}.1-SNAPSHOT \ + -DreleaseVersion=\${parsedVersion.majorVersion}.\${parsedVersion.nextMinorVersion}.0" ;; + ("major") echo "$MAVEN_OPTION + -DdevelopmentVersion=\${parsedVersion.nextMajorVersion}.0.1-SNAPSHOT \ + -DreleaseVersion=\${parsedVersion.nextMajorVersion}.0.0" ;; + (*) echo "$MAVEN_OPTION" ;; + esac +) +echo "Performing a $RELEASE_TYPE release of branch $RELEASE_BRANCH_NAME" + +if [[ -n "$GITREPO_ACCESS_TOKEN" && -z "${SSH_PRIVATE_KEY}" ]]; then + echo "Git repo access token defined and no SSH setup. We then use the git repo access token via maven release to commit in the repo." + MAVEN_OPTION="$MAVEN_OPTION -Dusername=$GITREPO_ACCESS_TOKEN" +else + echo "Not using access token authentication, as no access token (via env GITREPO_ACCESS_TOKEN) defined or SSH key setup (via env SSH_PRIVATE_KEY)" +fi + +# Prepare the release +echo "Do mvn release:prepare with options $MAVEN_OPTION and arguments $MAVEN_ARGS" +mvn $MAVEN_OPTION $MAVEN_REPO_LOCAL build-helper:parse-version release:prepare -B -Darguments="$MAVEN_ARGS" + +# Do release if prepare did not fail +if [[ ("$?" -eq 0) ]]; then + echo "Do mvn release:perform with options $MAVEN_OPTION and arguments $MAVEN_ARGS" + mvn $MAVEN_OPTION $MAVEN_PERFORM_OPTION $MAVEN_REPO_LOCAL build-helper:parse-version release:perform -B -Darguments="$MAVEN_ARGS -Dmaven.test.skip=true" +fi + +# rollback release if prepare failed +if [[ "$?" -ne 0 ]] ; then + echo "Rolling back release after failure" + mvn $MAVEN_OPTION $MAVEN_REPO_LOCAL release:rollback -B -Darguments="$MAVEN_ARGS" +fi From ad7d4c5ea3e381354863f133f4be25777efa9f11 Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:13:27 +1300 Subject: [PATCH 065/112] Delete scripts/release.sh --- scripts/release.sh | 54 ---------------------------------------------- 1 file changed, 54 deletions(-) delete mode 100644 scripts/release.sh diff --git a/scripts/release.sh b/scripts/release.sh deleted file mode 100644 index 54aa9a0..0000000 --- a/scripts/release.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -set -e - -# avoid the release loop by checking if the latest commit is a release commit -readonly local last_release_commit_hash=$(git log --author="$GIT_RELEASE_BOT_NAME" --pretty=format:"%H" -1) -echo "Last $GIT_RELEASE_BOT_NAME commit: ${last_release_commit_hash}" -echo "Current commit: ${GITHUB_SHA}" -if [[ "${last_release_commit_hash}" = "${GITHUB_SHA}" ]]; then - echo "Skipping for $GIT_RELEASE_BOT_NAME commit. Release failed as no changes since last release." - exit 1 -fi - -# This script will do a release of the artifact according to http://maven.apache.org/maven-release/maven-release-plugin/ -echo "Setup git user name to '$GIT_RELEASE_BOT_NAME'" -git config --global user.name "$GIT_RELEASE_BOT_NAME"; -echo "Setup git user email to '$GIT_RELEASE_BOT_EMAIL'" -git config --global user.email "$GIT_RELEASE_BOT_EMAIL"; - -# Setup next version -MAVEN_OPTION=$( - case "$RELEASE_TYPE" in - ("minor") echo "$MAVEN_OPTION \ - -DdevelopmentVersion=\${parsedVersion.majorVersion}.\${parsedVersion.nextMinorVersion}.1-SNAPSHOT \ - -DreleaseVersion=\${parsedVersion.majorVersion}.\${parsedVersion.nextMinorVersion}.0" ;; - ("major") echo "$MAVEN_OPTION - -DdevelopmentVersion=\${parsedVersion.nextMajorVersion}.0.1-SNAPSHOT \ - -DreleaseVersion=\${parsedVersion.nextMajorVersion}.0.0" ;; - (*) echo "$MAVEN_OPTION" ;; - esac -) -echo "Performing a $RELEASE_TYPE release of branch $RELEASE_BRANCH_NAME" - -if [[ -n "$GITREPO_ACCESS_TOKEN" && -z "${SSH_PRIVATE_KEY}" ]]; then - echo "Git repo access token defined and no SSH setup. We then use the git repo access token via maven release to commit in the repo." - MAVEN_OPTION="$MAVEN_OPTION -Dusername=$GITREPO_ACCESS_TOKEN" -else - echo "Not using access token authentication, as no access token (via env GITREPO_ACCESS_TOKEN) defined or SSH key setup (via env SSH_PRIVATE_KEY)" -fi - -# Prepare the release -echo "Do mvn release:prepare with options $MAVEN_OPTION and arguments $MAVEN_ARGS" -mvn $MAVEN_OPTION $MAVEN_REPO_LOCAL build-helper:parse-version release:prepare -B -Darguments="$MAVEN_ARGS" - -# Do release if prepare did not fail -if [[ ("$?" -eq 0) ]]; then - echo "Do mvn release:perform with options $MAVEN_OPTION and arguments $MAVEN_ARGS" - mvn $MAVEN_OPTION $MAVEN_PERFORM_OPTION $MAVEN_REPO_LOCAL build-helper:parse-version release:perform -B -Darguments="$MAVEN_ARGS -Dmaven.test.skip=true" -fi - -# rollback release if prepare failed -if [[ "$?" -ne 0 ]] ; then - echo "Rolling back release after failure" - mvn $MAVEN_OPTION $MAVEN_REPO_LOCAL release:rollback -B -Darguments="$MAVEN_ARGS" -fi From 3c68d01cdf016da8634c106b6cdd0d14df8df344 Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:14:43 +1300 Subject: [PATCH 066/112] Update release.yml --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index edb0fb3..d245edb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,4 +41,4 @@ jobs: version-major: ${{ github.event.inputs.releaseType == 'major '}} version-minor: ${{ github.event.inputs.releaseType == 'minor '}} version-patch: ${{ github.event.inputs.releaseType == 'patch '}} - access-token: ${{ secrets.GITHUB_TOKEN }} + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} From e3486379cf6aed7f692f1fb7ecfa77a72a184333 Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:17:49 +1300 Subject: [PATCH 067/112] Update release.yml --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d245edb..7e02c4f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,6 +25,8 @@ jobs: java-version: 11 - name: Release uses: qcastel/github-actions-maven-release@v1.12.41 + env: + JAVA_HOME: /usr/lib/jvm/java-11-openjdk/ with: release-branch-name: "master" maven-args: "-P sonatype" From e16e8454715e8bba1acf13cb13b7e20a4c18b153 Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 8 Dec 2023 02:19:02 +0000 Subject: [PATCH 068/112] [maven-release-plugin] prepare release graphql-builder-2.0.10 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a16fcb3..417f1d4 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.10-SNAPSHOT + 2.0.10 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.10 From d54b4ef733d5cca0748d14b45f2005921818df2a Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 8 Dec 2023 02:19:03 +0000 Subject: [PATCH 069/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 417f1d4..9435ae1 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.10 + 2.0.11-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.10 + HEAD From d67d815df711fbd33bf309eaa4e49a8210ba4e73 Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:20:53 +1300 Subject: [PATCH 070/112] Update release.yml --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e02c4f..f02ec18 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,7 +40,7 @@ jobs: maven-repo-server-id: sonatype maven-repo-server-username: ${{ secrets.MVN_REPO_PRIVATE_REPO_USER }} maven-repo-server-password: ${{ secrets.MVN_REPO_PRIVATE_REPO_PASSWORD }} - version-major: ${{ github.event.inputs.releaseType == 'major '}} - version-minor: ${{ github.event.inputs.releaseType == 'minor '}} - version-patch: ${{ github.event.inputs.releaseType == 'patch '}} + version-major: ${{ github.event.inputs.releaseType == 'major'}} + version-minor: ${{ github.event.inputs.releaseType == 'minor'}} + version-patch: ${{ github.event.inputs.releaseType == 'patch'}} ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} From 6e52ea82a027a59ad2dd89645a8e03947facfe89 Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 8 Dec 2023 02:22:19 +0000 Subject: [PATCH 071/112] [maven-release-plugin] prepare release graphql-builder-2.0.11 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9435ae1..a0ebd5f 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.11-SNAPSHOT + 2.0.11 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-2.0.11 From 8064a2d951a6367d02cfed6a01c49565f88eaad9 Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 8 Dec 2023 02:22:20 +0000 Subject: [PATCH 072/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a0ebd5f..9e36df1 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.11 + 3.0.0-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-2.0.11 + HEAD From c0d194aa9726daca78d614e833d34fa1fb853039 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Tue, 20 Aug 2024 08:05:01 +1200 Subject: [PATCH 073/112] bump graphql to 22.2, compile fixes, test updates --- .gitignore | 4 +++- pom.xml | 2 +- .../fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java | 2 -- src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 7f52411..0369bb8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /target/ /.classpath /.project -.settings \ No newline at end of file +.settings +.idea/* + diff --git a/pom.xml b/pom.xml index 9e36df1..c6bf7ba 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 2.15.2 1.9.0 1.0.0 - 21.1 + 22.2 diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java index 9224df5..c51bc24 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java @@ -14,7 +14,6 @@ import com.fleetpin.graphql.builder.EntityProcessor; import com.fleetpin.graphql.builder.TypeMeta; import graphql.GraphQLContext; -import graphql.com.google.common.base.Throwables; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -44,7 +43,6 @@ public ObjectFieldBuilder(Class type, ArrayList mappers) { return toReturn; } catch (Throwable e) { - Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } }; diff --git a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java index 96dcb16..a374f6a 100644 --- a/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/DirectiveTest.java @@ -87,7 +87,7 @@ public void testDirectiveArgumentDefinition() { List> dir = (List>) ((Map) response.get("__schema")).get("directives"); LinkedHashMap input = dir.stream().filter(map -> map.get("name").equals("Input")).collect(Collectors.toList()).get(0); - assertEquals(7, dir.size()); + assertEquals(8, dir.size()); assertEquals("ARGUMENT_DEFINITION", ((List) input.get("locations")).get(0)); assertEquals(1, ((List) input.get("args")).size()); //getNickname(nickName: String! @Input(value : "TT")): String! From f85050f4cc1c7a8ed9be1237987467090d56f1d6 Mon Sep 17 00:00:00 2001 From: Colin O'Brien Date: Thu, 22 Aug 2024 20:20:28 +1200 Subject: [PATCH 074/112] throw if unchecked --- .../graphql/builder/mapper/ObjectFieldBuilder.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java index c51bc24..3bd3b73 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java @@ -14,6 +14,8 @@ import com.fleetpin.graphql.builder.EntityProcessor; import com.fleetpin.graphql.builder.TypeMeta; import graphql.GraphQLContext; +import graphql.com.google.common.base.Preconditions; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -43,6 +45,7 @@ public ObjectFieldBuilder(Class type, ArrayList mappers) { return toReturn; } catch (Throwable e) { + throwIfUnchecked(e); throw new RuntimeException(e); } }; @@ -84,4 +87,14 @@ public static FieldMapper build(EntityProcessor entityProcessor, TypeMeta inputT return new FieldMapper(name, method, entityProcessor.getResolver(inputType)); } } + + // copied from guava + public static void throwIfUnchecked(Throwable throwable) { + Preconditions.checkNotNull(throwable); + if (throwable instanceof RuntimeException) { + throw (RuntimeException)throwable; + } else if (throwable instanceof Error) { + throw (Error)throwable; + } + } } From 9b8aab4269dfccbdb060c229c8bd533d1f60c5ae Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Fri, 23 Aug 2024 14:16:35 +1200 Subject: [PATCH 075/112] fix bug with input generics --- pom.xml | 6 +-- .../fleetpin/graphql/builder/TypeMeta.java | 9 ++-- .../builder/TypeGenericInputRecords.java | 49 +++++++++++++++++++ .../builder/inputgenericsRecords/Change.java | 26 ++++++++++ .../builder/inputgenericsRecords/Wrapper.java | 16 ++++++ 5 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Change.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Wrapper.java diff --git a/pom.xml b/pom.xml index 269d106..e4d2f89 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 2.0.9-SNAPSHOT + 3.0.0-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -73,8 +73,8 @@ org.apache.maven.plugins maven-compiler-plugin - 11 - 11 + 21 + 21 -parameters diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java index 1e0ed98..178c843 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java @@ -103,6 +103,9 @@ private boolean matchType(TypeMeta target, String typeName, ParameterizedType ty } } return true; + } else if (arg instanceof ParameterizedType pType) { + process((Class) pType.getRawType(), pType, element); + return true; } else { var klass = (Class) arg; process(klass, klass, element); @@ -119,7 +122,7 @@ private void findType(TypeMeta target, TypeVariable type, Class start, Annotated var startClass = (Class) start; var genericDeclaration = type.getGenericDeclaration(); if (start.equals(genericDeclaration)) { - //we don't have any implementing logic we are at this level so take the bounds + // we don't have any implementing logic we are at this level so take the bounds for (var bound : type.getBounds()) { if (bound instanceof ParameterizedType) { process((Class) ((ParameterizedType) bound).getRawType(), bound, element); @@ -284,7 +287,7 @@ public Class resolveToType(TypeVariable variable) { for (int i = 0; i < pt.length; i++) { var p = pt[i]; if (p.equals(variable)) { - var generic = (ParameterizedType) genericType; //safe as has to if equal vaiable + var generic = (ParameterizedType) genericType; // safe as has to if equal vaiable var implementingType = generic.getActualTypeArguments()[i]; if (implementingType instanceof Class) { return (Class) implementingType; @@ -301,7 +304,7 @@ public Class resolveToType(TypeVariable variable) { for (int i = 0; i < pt.length; i++) { var p = pt[i]; if (p.equals(variable)) { - var superClass = (ParameterizedType) parentType.getGenericSuperclass(); //safe as has to if equal vaiable + var superClass = (ParameterizedType) parentType.getGenericSuperclass(); // safe as has to if equal vaiable var implementingType = superClass.getActualTypeArguments()[i]; if (implementingType instanceof Class) { diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java new file mode 100644 index 0000000..090f484 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java @@ -0,0 +1,49 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import graphql.ExecutionResult; +import graphql.GraphQL; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class TypeGenericInputRecords { + + @Test + public void textQuery() throws ReflectiveOperationException { + Map response = execute(""" + query {doChange(input: {name: { wrap: ["felix"]}})} + """).getData(); + var change = response.get("doChange"); + assertEquals("felix", change); + } + + @Test + public void textQueryNull() throws ReflectiveOperationException { + Map response = execute(""" + query {doChange(input: {})} + """).getData(); + var change = response.get("doChange"); + assertEquals("empty", change); + } + + private ExecutionResult execute(String query) { + GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.inputgenericsRecords")).build(); + ExecutionResult result = schema.execute(query); + if (!result.getErrors().isEmpty()) { + throw new RuntimeException(result.getErrors().toString()); + } + return result; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Change.java b/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Change.java new file mode 100644 index 0000000..31560c7 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Change.java @@ -0,0 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.fleetpin.graphql.builder.inputgenericsRecords; + +import com.fleetpin.graphql.builder.annotations.Query; +import jakarta.annotation.Nullable; +import java.util.List; + +public record Change(@Nullable Wrapper> name) { + @Query + public static String doChange(Change input) { + if (input.name == null) { + return "empty"; + } + return input.name.wrap().getFirst(); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Wrapper.java b/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Wrapper.java new file mode 100644 index 0000000..74365b8 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Wrapper.java @@ -0,0 +1,16 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.inputgenericsRecords; + +import jakarta.annotation.Nullable; + +public record Wrapper(@Nullable T wrap) {} From e3eb8d88f912648ddc36e224290b57e7a14230d8 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Sun, 25 Aug 2024 22:00:28 +1200 Subject: [PATCH 076/112] add rename CICD java 21 updarte --- .github/workflows/release.yml | 63 ++++++++------ .github/workflows/test.yml | 33 ++++---- pom.xml | 8 ++ renovate.json | 3 + .../fleetpin/graphql/builder/EntityUtil.java | 67 ++++++--------- .../fleetpin/graphql/builder/EnumEntity.java | 1 + .../graphql/builder/InputBuilder.java | 9 +- .../graphql/builder/MethodProcessor.java | 28 ++++--- .../graphql/builder/ObjectEntity.java | 4 +- .../fleetpin/graphql/builder/TypeBuilder.java | 8 +- .../builder/annotations/GraphQLName.java | 24 ++++++ .../fleetpin/graphql/builder/RenameTest.java | 83 +++++++++++++++++++ .../graphql/builder/record/Queries.java | 27 +----- .../graphql/builder/rename/Queries.java | 47 +++++++++++ 14 files changed, 275 insertions(+), 130 deletions(-) create mode 100644 renovate.json create mode 100644 src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLName.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/RenameTest.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/rename/Queries.java diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e4c4b30..99794fc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,31 +1,46 @@ name: Release permissions: contents: write -on: - workflow_dispatch +on: workflow_dispatch jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Release - uses: qcastel/github-actions-maven-release@v1.11.1 - with: - release-branch-name: "master" - maven-args: "-P sonatype" - git-release-bot-name: "release-bot" - git-release-bot-email: "release-bot@fleetpin.co.nz" - - gpg-enabled: "true" - gpg-key-id: ${{ secrets.GPG_KEY_ID }} - gpg-key: ${{ secrets.GPG_KEY }} - - maven-repo-server-id: sonatype - maven-repo-server-username: ${{ secrets.MVN_REPO_PRIVATE_REPO_USER }} - maven-repo-server-password: ${{ secrets.MVN_REPO_PRIVATE_REPO_PASSWORD }} - - access-token: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v4 + + - uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: zulu + - uses: whelk-io/maven-settings-xml-action@v22 + with: + servers: > + [ + { "id": "sonatype", "username": "${{ secrets.MVN_REPO_PRIVATE_REPO_USER }}", "password": "${{ secrets.MVN_REPO_PRIVATE_REPO_PASSWORD }}" } + ] + - name: set name + run: | + git config --global user.name "release-bot"; + git config --global user.email "release-bot@fleetpin.co.nz"; + + - name: add key + run: | + echo "${{ secrets.GPG_KEY }}" | base64 -d > private.key + gpg --batch --import ./private.key + rm ./private.key + gpg --list-secret-keys --keyid-format LONG + + - name: prepare + run: | + mvn release:prepare -Dusername=${{ secrets.GITHUB_TOKEN }} -P sonatype + + - name: release + run: | + mvn release:perform -Dusername=${{ secrets.GITHUB_TOKEN }} -P sonatype diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6290649..75e8a83 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,21 +1,26 @@ name: Test -on: +on: push: jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v1 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Build with Maven - run: mvn -B test --file pom.xml + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: zulu + - name: Build with Maven + run: mvn -B test --file pom.xml + - uses: ashley-taylor/junit-report-annotations-action@master + if: always() + with: + access-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/pom.xml b/pom.xml index e4d2f89..52b22db 100644 --- a/pom.xml +++ b/pom.xml @@ -221,6 +221,14 @@ ${junit.jupiter.version} test + + + org.skyscreamer + jsonassert + 1.5.3 + test + + diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..dbc10d2 --- /dev/null +++ b/renovate.json @@ -0,0 +1,3 @@ +{ + "forkProcessing": "enabled" +} diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java index 2916bd9..1451e9e 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java @@ -13,11 +13,12 @@ import com.fleetpin.graphql.builder.annotations.Context; import com.fleetpin.graphql.builder.annotations.GraphQLIgnore; +import com.fleetpin.graphql.builder.annotations.GraphQLName; import com.fleetpin.graphql.builder.annotations.InputIgnore; -import com.fleetpin.graphql.builder.mapper.ObjectFieldBuilder.FieldMapper; import graphql.GraphQLContext; import graphql.schema.DataFetchingEnvironment; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; @@ -26,30 +27,6 @@ class EntityUtil { - private static final Method IS_RECORD_METHOD; - - static { - Class classClass = Class.class; - Method isRecord; - try { - isRecord = classClass.getMethod("isRecord"); - } catch (NoSuchMethodException e) { - isRecord = null; - } - IS_RECORD_METHOD = isRecord; - } - - static boolean isRecord(Class type) { - if (IS_RECORD_METHOD == null) { - return false; - } - try { - return (Boolean) IS_RECORD_METHOD.invoke(type); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - static String getName(TypeMeta meta) { var type = meta.getType(); @@ -94,22 +71,20 @@ public static Optional getter(Method method) { if (method.isAnnotationPresent(GraphQLIgnore.class)) { return Optional.empty(); } - //will also be on implementing class + // will also be on implementing class if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { return Optional.empty(); } if (Modifier.isStatic(method.getModifiers())) { return Optional.empty(); - } else { - if (method.getName().matches("(get|is)[A-Z].*")) { - String name; - if (method.getName().startsWith("get")) { - name = method.getName().substring("get".length(), "get".length() + 1).toLowerCase() + method.getName().substring("get".length() + 1); - } else { - name = method.getName().substring("is".length(), "is".length() + 1).toLowerCase() + method.getName().substring("is".length() + 1); - } - return Optional.of(name); + } else if (method.getName().matches("(get|is)[A-Z].*")) { + String name; + if (method.getName().startsWith("get")) { + name = method.getName().substring("get".length(), "get".length() + 1).toLowerCase() + method.getName().substring("get".length() + 1); + } else { + name = method.getName().substring("is".length(), "is".length() + 1).toLowerCase() + method.getName().substring("is".length() + 1); } + return Optional.of(getName(name, method)); } return Optional.empty(); } @@ -124,24 +99,30 @@ public static Optional setter(Method method) { if (method.isAnnotationPresent(GraphQLIgnore.class)) { return Optional.empty(); } - //will also be on implementing class + // will also be on implementing class if (Modifier.isAbstract(method.getModifiers()) || method.getDeclaringClass().isInterface()) { return Optional.empty(); } if (Modifier.isStatic(method.getModifiers())) { return Optional.empty(); - } else { - //getter type - if (method.getName().matches("set[A-Z].*")) { - if (method.getParameterCount() == 1 && !method.isAnnotationPresent(InputIgnore.class)) { - String name = method.getName().substring("set".length(), "set".length() + 1).toLowerCase() + method.getName().substring("set".length() + 1); - return Optional.of(name); - } + } else if (method.getName().matches("set[A-Z].*")) { + if (method.getParameterCount() == 1 && !method.isAnnotationPresent(InputIgnore.class)) { + String name = method.getName().substring("set".length(), "set".length() + 1).toLowerCase() + method.getName().substring("set".length() + 1); + return Optional.of(getName(name, method)); } } return Optional.empty(); } + static String getName(String fallback, AnnotatedElement... annotated) { + for (var anno : annotated) { + if (anno.isAnnotationPresent(GraphQLName.class)) { + return anno.getAnnotation(GraphQLName.class).value(); + } + } + return fallback; + } + static boolean isContext(Class class1, Annotation[] annotations) { for (var annotation : annotations) { if (annotation instanceof Context) { diff --git a/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java index 0c8743f..b05bc20 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/EnumEntity.java @@ -43,6 +43,7 @@ public EnumEntity(DirectivesSchema directives, TypeMeta meta) throws ReflectiveO if (field.isAnnotationPresent(GraphQLIgnore.class)) { continue; } + var name = EntityUtil.getName(a.name(), field); var valueDef = newEnumValueDefinition().name(a.name()).value(a); var desc = field.getAnnotation(GraphQLDescription.class); if (desc != null) { diff --git a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java index 7ef891d..ff00057 100644 --- a/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/InputBuilder.java @@ -231,9 +231,9 @@ void processFields(Builder graphInputType) { if (Modifier.isStatic(field.getModifiers())) { continue; } else { - //getter type + // getter type if (!field.isAnnotationPresent(InputIgnore.class)) { - String name = field.getName(); + String name = EntityUtil.getName(field.getName(), field); GraphQLInputObjectField.Builder fieldBuilder = GraphQLInputObjectField.newInputObjectField(); fieldBuilder.name(name); entityProcessor.addSchemaDirective(field, meta.getType(), fieldBuilder::withAppliedDirective); @@ -271,11 +271,12 @@ protected InputTypeBuilder resolve() { if (Modifier.isStatic(field.getModifiers())) { continue; } else { - //getter type + // getter type if (!field.isAnnotationPresent(InputIgnore.class)) { TypeMeta innerMeta = new TypeMeta(meta, field.getType(), field.getGenericType(), field); var resolver = entityProcessor.getResolver(innerMeta); - fieldMappers.add(new RecordMapper(field.getName(), field.getType(), resolver)); + var name = EntityUtil.getName(field.getName(), field); + fieldMappers.add(new RecordMapper(name, field.getType(), resolver)); } } } diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index fecad87..630d16b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -55,13 +55,13 @@ void process(AuthorizerSchema authorizer, Method method) throws ReflectiveOperat FieldCoordinates coordinates; GraphQLObjectType.Builder object; if (method.isAnnotationPresent(Query.class)) { - coordinates = FieldCoordinates.coordinates("Query", method.getName()); + coordinates = FieldCoordinates.coordinates("Query", EntityUtil.getName(method.getName(), method)); object = graphQuery; } else if (method.isAnnotationPresent(Mutation.class)) { - coordinates = FieldCoordinates.coordinates("Mutations", method.getName()); + coordinates = FieldCoordinates.coordinates("Mutations", EntityUtil.getName(method.getName(), method)); object = graphMutations; } else if (method.isAnnotationPresent(Subscription.class)) { - coordinates = FieldCoordinates.coordinates("Subscriptions", method.getName()); + coordinates = FieldCoordinates.coordinates("Subscriptions", EntityUtil.getName(method.getName(), method)); object = graphSubscriptions; } else { return; @@ -91,21 +91,22 @@ Builder process(AuthorizerSchema authorizer, FieldCoordinates coordinates, TypeM var type = entityProcessor.getType(meta, method.getAnnotations()); field.type(type); for (int i = 0; i < method.getParameterCount(); i++) { + var parameter = method.getParameters()[0]; GraphQLArgument.Builder argument = GraphQLArgument.newArgument(); - if (isContext(method.getParameterTypes()[i], method.getParameterAnnotations()[i])) { + if (isContext(parameter.getType(), parameter.getAnnotations())) { continue; } - TypeMeta inputMeta = new TypeMeta(null, method.getParameterTypes()[i], method.getGenericParameterTypes()[i], method.getParameters()[i]); - argument.type(entityProcessor.getInputType(inputMeta, method.getParameterAnnotations()[i])); //TODO:dirty cast + TypeMeta inputMeta = new TypeMeta(null, parameter.getType(), method.getGenericParameterTypes()[i], parameter); + argument.type(entityProcessor.getInputType(inputMeta, method.getParameterAnnotations()[i])); // TODO:dirty cast - description = method.getParameters()[i].getAnnotation(GraphQLDescription.class); + description = parameter.getAnnotation(GraphQLDescription.class); if (description != null) { argument.description(description.value()); } - argument.name(method.getParameters()[i].getName()); - //TODO: argument.defaultValue(defaultValue) + argument.name(EntityUtil.getName(parameter.getName(), parameter)); + // TODO: argument.defaultValue(defaultValue) field.argument(argument); } @@ -130,11 +131,12 @@ private DataFetcher buildDataFetcher(TypeMeta meta, Method method) { method.setAccessible(true); for (int i = 0; i < resolvers.length; i++) { - Class type = method.getParameterTypes()[i]; - var name = method.getParameters()[i].getName(); + var parameter = method.getParameters()[i]; + Class type = parameter.getType(); + var name = EntityUtil.getName(parameter.getName(), parameter); var generic = method.getGenericParameterTypes()[i]; - var argMeta = new TypeMeta(meta, type, generic, method.getParameters()[i]); - resolvers[i] = buildResolver(name, argMeta, method.getParameterAnnotations()[i]); + var argMeta = new TypeMeta(meta, type, generic, parameter); + resolvers[i] = buildResolver(name, argMeta, parameter.getAnnotations()); } DataFetcher fetcher = env -> { diff --git a/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java b/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java index 89d76b3..cffab6b 100644 --- a/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java +++ b/src/main/java/com/fleetpin/graphql/builder/ObjectEntity.java @@ -24,7 +24,7 @@ public class ObjectEntity extends EntityHolder { private TypeBuilder typeBuilder; public ObjectEntity(EntityProcessor entityProcessor, TypeMeta meta) { - if (EntityUtil.isRecord(meta.getType())) { + if (meta.getType().isRecord()) { typeBuilder = new TypeBuilder.Record(entityProcessor, meta); } else { typeBuilder = new TypeBuilder.ObjectType(entityProcessor, meta); @@ -32,7 +32,7 @@ public ObjectEntity(EntityProcessor entityProcessor, TypeMeta meta) { if (meta.getType().isAnnotationPresent(OneOf.class)) { inputBuilder = new InputBuilder.OneOfInputBuilder(entityProcessor, meta); - } else if (EntityUtil.isRecord(meta.getType())) { + } else if (meta.getType().isRecord()) { inputBuilder = new InputBuilder.Record(entityProcessor, meta); } else { var constructors = meta.getType().getDeclaredConstructors(); diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java index a2ff1e0..7d35b9c 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeBuilder.java @@ -82,7 +82,7 @@ public GraphQLNamedOutputType buildType() throws ReflectiveOperationException { } parent = parent.getSuperclass(); } - //generics + // generics TypeMeta innerMeta = new TypeMeta(meta, type, type); if (!EntityUtil.getName(innerMeta).equals(typeName)) { var interfaceName = entityProcessor.getEntity(innerMeta).getInnerType(innerMeta); @@ -205,7 +205,7 @@ protected void processFields(String typeName, Builder graphType, graphql.schema. if (field.isAnnotationPresent(GraphQLIgnore.class)) { continue; } - //will also be on implementing class + // will also be on implementing class if (Modifier.isAbstract(field.getModifiers()) || field.getDeclaringClass().isInterface()) { continue; } @@ -216,8 +216,8 @@ protected void processFields(String typeName, Builder graphType, graphql.schema. if (method.isAnnotationPresent(GraphQLIgnore.class)) { continue; } - //getter type - String name = field.getName(); + + var name = EntityUtil.getName(field.getName(), field, method); var f = entityProcessor.getMethodProcessor().process(null, FieldCoordinates.coordinates(typeName, name), meta, method); graphType.field(f); diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLName.java b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLName.java new file mode 100644 index 0000000..726f7ee --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/GraphQLName.java @@ -0,0 +1,24 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) +public @interface GraphQLName { + public String value(); +} diff --git a/src/test/java/com/fleetpin/graphql/builder/RenameTest.java b/src/test/java/com/fleetpin/graphql/builder/RenameTest.java new file mode 100644 index 0000000..5133f27 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/RenameTest.java @@ -0,0 +1,83 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; +import graphql.introspection.IntrospectionWithDirectivesSupport; +import java.util.Map; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.skyscreamer.jsonassert.JSONAssert; + +public class RenameTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Test + public void testClassRename() throws JsonProcessingException, JSONException { + var type = Map.of("nameSet", "foo"); + var response = execute(""" + query passthroughClass($type: ClassTypeInput!) { + passthroughClass(type: $type) { + nameGet + } + } + """, Map.of("type", type)) + .toSpecification(); + JSONAssert.assertEquals(""" + { + "data": { + "passthroughClass": { + "nameGet": "foo" + } + } + } + """, MAPPER.writeValueAsString(response), false); + } + + @Test + public void testRecordRename() throws JsonProcessingException, JSONException { + var type = Map.of("name", "foo"); + var response = execute(""" + query passthroughRecord($type: RecordTypeInput!) { + passthroughRecord(type: $type) { + name + } + } + """, Map.of("type", type)) + .toSpecification(); + JSONAssert.assertEquals(""" + { + "data": { + "passthroughRecord": { + "name": "foo" + } + } + } + """, MAPPER.writeValueAsString(response), false); + } + + private ExecutionResult execute(String query, Map variables) { + GraphQL schema = GraphQL.newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.rename"))).build(); + var input = ExecutionInput.newExecutionInput(); + input.query(query); + if (variables != null) { + input.variables(variables); + } + ExecutionResult result = schema.execute(input); + return result; + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java index c13f064..cb6904c 100644 --- a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java +++ b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java @@ -36,31 +36,6 @@ public static List nullableArrayTest(@Nullable List type) { return type; } - // once move to java 17 change this to be a real record @GraphQLDescription("record Type") - static final class InputType { - - private final String name; - private final int age; - private final Optional weight; - - private InputType(@GraphQLDescription("the name") String name, int age, Optional weight) { - super(); - this.name = name; - this.age = age; - this.weight = weight; - } - - public String getName() { - return name; - } - - public int getAge() { - return age; - } - - public Optional getWeight() { - return weight; - } - } + static final record InputType(@GraphQLDescription("the name") String name, int age, Optional weight) {} } diff --git a/src/test/java/com/fleetpin/graphql/builder/rename/Queries.java b/src/test/java/com/fleetpin/graphql/builder/rename/Queries.java new file mode 100644 index 0000000..84be3fd --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/rename/Queries.java @@ -0,0 +1,47 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.rename; + +import com.fleetpin.graphql.builder.annotations.GraphQLName; +import com.fleetpin.graphql.builder.annotations.Query; + +public class Queries { + + @Query + @GraphQLName("passthroughClass") + public static ClassType passthroughClassWrong(@GraphQLName("type") ClassType typeWrong) { + return typeWrong; + } + + @Query + @GraphQLName("passthroughRecord") + public static RecordType passthroughRecordWrong(@GraphQLName("type") RecordType typeWrong) { + return typeWrong; + } + + public static record RecordType(@GraphQLName("name") String nameWrong) {} + + public static class ClassType { + + private String nameWrong; + + @GraphQLName("nameGet") + public String getNameWrong() { + return nameWrong; + } + + @GraphQLName("nameSet") + public void setNameWrong(String nameWrong) { + this.nameWrong = nameWrong; + } + } +} From 27b91ac4eb4802856a8b00009a0927449b4a1cdd Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Sun, 25 Aug 2024 22:01:17 +1200 Subject: [PATCH 077/112] latest graphql --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 52b22db..93fc810 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 2.15.2 1.9.0 1.0.0 - 21.1 + 22.2 From b0fc8e43bea30bf7b55e6859813e1092d4b12da8 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Sun, 25 Aug 2024 22:07:37 +1200 Subject: [PATCH 078/112] get CICD passing --- .github/workflows/test.yml | 4 ---- .../fleetpin/graphql/builder/MethodProcessor.java | 12 +++++------- .../graphql/builder/mapper/ObjectFieldBuilder.java | 5 ++--- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 75e8a83..61bac3b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,3 @@ jobs: distribution: zulu - name: Build with Maven run: mvn -B test --file pom.xml - - uses: ashley-taylor/junit-report-annotations-action@master - if: always() - with: - access-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java index 39a2301..8bef984 100644 --- a/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java +++ b/src/main/java/com/fleetpin/graphql/builder/MethodProcessor.java @@ -2,19 +2,12 @@ import static com.fleetpin.graphql.builder.EntityUtil.isContext; -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.function.Function; - import com.fleetpin.graphql.builder.annotations.Directive; import com.fleetpin.graphql.builder.annotations.GraphQLDeprecated; import com.fleetpin.graphql.builder.annotations.GraphQLDescription; import com.fleetpin.graphql.builder.annotations.Mutation; import com.fleetpin.graphql.builder.annotations.Query; import com.fleetpin.graphql.builder.annotations.Subscription; - import graphql.GraphQLContext; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; @@ -26,6 +19,11 @@ import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLFieldDefinition.Builder; import graphql.schema.GraphQLObjectType; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.function.Function; class MethodProcessor { diff --git a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java index 3bd3b73..9fb20d8 100644 --- a/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/mapper/ObjectFieldBuilder.java @@ -15,7 +15,6 @@ import com.fleetpin.graphql.builder.TypeMeta; import graphql.GraphQLContext; import graphql.com.google.common.base.Preconditions; - import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -92,9 +91,9 @@ public static FieldMapper build(EntityProcessor entityProcessor, TypeMeta inputT public static void throwIfUnchecked(Throwable throwable) { Preconditions.checkNotNull(throwable); if (throwable instanceof RuntimeException) { - throw (RuntimeException)throwable; + throw (RuntimeException) throwable; } else if (throwable instanceof Error) { - throw (Error)throwable; + throw (Error) throwable; } } } From 8b2301ced047647fddaee31c052b0f3ca94d68c6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:09:04 +0000 Subject: [PATCH 079/112] Update dependency io.reactivex.rxjava3:rxjava to v3.1.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 93fc810..7c94715 100644 --- a/pom.xml +++ b/pom.xml @@ -211,7 +211,7 @@ io.reactivex.rxjava3 rxjava - 3.1.6 + 3.1.9 test From e3f36306e578403b05e065cab390a239ad97103f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:09:08 +0000 Subject: [PATCH 080/112] Update dependency com.hubspot.maven.plugins:prettier-maven-plugin to v0.22 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 93fc810..e9a13e6 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ com.hubspot.maven.plugins prettier-maven-plugin - 0.15 + 0.22 1.4.0 160 From bf647cf798ec6898d72afba560ef50b3c95e6c64 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:12:36 +0000 Subject: [PATCH 081/112] Update dependency com.mycila:license-maven-plugin to v4.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b06cb2..8045b66 100644 --- a/pom.xml +++ b/pom.xml @@ -149,7 +149,7 @@ com.mycila license-maven-plugin - 4.1 + 4.5 From 9084324291ad2eabc7d2a19358376d63a36a044b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:12:40 +0000 Subject: [PATCH 082/112] Update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3.8.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b06cb2..9934dde 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.1.1 + 3.8.0 attach-javadocs From 90bc4e987c031039753a1039a20ea0cea67e0ad1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:12:44 +0000 Subject: [PATCH 083/112] Update dependency org.junit.jupiter:junit-jupiter to v5.11.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b06cb2..80a8db9 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ https://github.com/ashley-taylor/graphql-builder - 5.10.0 + 5.11.0 UTF-8 2.15.2 1.9.0 From b83cf9b2213e6163bea85cfff4e20a55413b9cf1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:12:50 +0000 Subject: [PATCH 084/112] Update dependency org.pitest:pitest-maven to v1.16.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b06cb2..8c62c82 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 5.10.0 UTF-8 2.15.2 - 1.9.0 + 1.16.1 1.0.0 22.2 From 8869c2ef454f14c7ebede124ea4c9ab10ca4af18 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:12:58 +0000 Subject: [PATCH 085/112] Update dependency org.sonatype.plugins:nexus-staging-maven-plugin to v1.7.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b06cb2..2102349 100644 --- a/pom.xml +++ b/pom.xml @@ -254,7 +254,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.7.0 true sonatype From bc8bec6503a1125bb342ae25ad1b41a4c6b9f4e4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:13:46 +0000 Subject: [PATCH 086/112] Update dependency jakarta.annotation:jakarta.annotation-api to v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b06cb2..d24c4d7 100644 --- a/pom.xml +++ b/pom.xml @@ -182,7 +182,7 @@ jakarta.annotation jakarta.annotation-api - 2.1.1 + 3.0.0 com.fasterxml.jackson.core From f089ace39e7f199955cb5f5242745e7b8ea698f7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:13:50 +0000 Subject: [PATCH 087/112] Update dependency org.apache.maven.plugins:maven-gpg-plugin to v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b06cb2..4eed9e0 100644 --- a/pom.xml +++ b/pom.xml @@ -240,7 +240,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.5 sign-artifacts From 9b17e38d3dbc2ff97faa32d7cab5d1564eefbf16 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:13:53 +0000 Subject: [PATCH 088/112] Update dependency org.apache.maven.plugins:maven-release-plugin to v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b06cb2..ff40ed8 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 + 3.1.1 org.apache.maven.plugins From f17cf20f8fe47553cb8fbc632bc206b0cda0214d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:14:26 +0000 Subject: [PATCH 089/112] Update dependency org.pitest:pitest-junit5-plugin to v1.2.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 800ef09..c3e93c8 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ UTF-8 2.15.2 1.16.1 - 1.0.0 + 1.2.1 22.2 From 1a3804d435b1204a9653991fd80742c64be45822 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 10:14:29 +0000 Subject: [PATCH 090/112] Update jackson.version to v2.17.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 800ef09..ec8f860 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 5.11.0 UTF-8 - 2.15.2 + 2.17.2 1.16.1 1.0.0 22.2 From 9245b94087c2ae8def493f533947b77dc06ff4c7 Mon Sep 17 00:00:00 2001 From: release-bot Date: Sun, 25 Aug 2024 10:33:15 +0000 Subject: [PATCH 091/112] [maven-release-plugin] prepare release graphql-builder-3.0.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index acdaa67..78b31b0 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 3.0.0-SNAPSHOT + 3.0.0 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-3.0.0 From 5ceb9012198e60aec385ac7141c6a3e1a32c6060 Mon Sep 17 00:00:00 2001 From: release-bot Date: Sun, 25 Aug 2024 10:33:16 +0000 Subject: [PATCH 092/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 78b31b0..f230e37 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 3.0.0 + 3.0.1-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-3.0.0 + HEAD From 5ec502778a1385a0fb6362e1ab5ba3b3f9ac9228 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 13:12:51 +0000 Subject: [PATCH 093/112] Update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3.10.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f230e37..9c023d4 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.8.0 + 3.10.0 attach-javadocs From 9fbecfa96cc441feab3ef07b4c1f4cf470c65a97 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:16:36 +0000 Subject: [PATCH 094/112] Update dependency org.pitest:pitest-maven to v1.16.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f230e37..2d8a85d 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 5.11.0 UTF-8 2.17.2 - 1.16.1 + 1.16.3 1.2.1 22.2 From 820cf54aa3f22153463de6a684f922387f1a2584 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Thu, 5 Sep 2024 09:35:59 +1200 Subject: [PATCH 095/112] fixing nested generic bug --- .../fleetpin/graphql/builder/EntityUtil.java | 26 ++++++++++++++----- .../builder/TypeGenericInputRecords.java | 17 +++++++++--- .../builder/inputgenericsRecords/Change.java | 4 +-- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java index 1451e9e..f137ec8 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java @@ -22,6 +22,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Optional; @@ -30,10 +31,19 @@ class EntityUtil { static String getName(TypeMeta meta) { var type = meta.getType(); - String name = type.getSimpleName(); - var genericType = meta.getGenericType(); + var name = buildUpName(meta, type, genericType); + if (meta.isDirect()) { + name += "_DIRECT"; + } + + return name; + } + + private static String buildUpName(TypeMeta meta, Class type, Type genericType) { + String name = type.getSimpleName(); + for (int i = 0; i < type.getTypeParameters().length; i++) { if (genericType instanceof ParameterizedType) { var t = ((ParameterizedType) genericType).getActualTypeArguments()[i]; @@ -46,6 +56,14 @@ static String getName(TypeMeta meta) { if (extra != null) { name += "_" + extra.getSimpleName(); } + } else if (t instanceof ParameterizedType pType) { + var rawType = pType.getRawType(); + if (rawType instanceof Class rawClass) { + var extra = buildUpName(meta, rawClass, pType); + name += "_" + extra; + } else { + throw new RuntimeException("Generics are more complex that logic currently can handle"); + } } } else { Class extra = meta.resolveToType(type.getTypeParameters()[i]); @@ -54,10 +72,6 @@ static String getName(TypeMeta meta) { } } } - if (meta.isDirect()) { - name += "_DIRECT"; - } - return name; } diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java index 090f484..c56d821 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java @@ -23,10 +23,21 @@ public class TypeGenericInputRecords { @Test public void textQuery() throws ReflectiveOperationException { Map response = execute(""" - query {doChange(input: {name: { wrap: ["felix"]}})} - """).getData(); + query {doChange(input: { + name: { + wrap: ["felix"] + }, + age: { + wrap: [234] + }, + description: { + wrap: "cat" + } + })} + """) + .getData(); var change = response.get("doChange"); - assertEquals("felix", change); + assertEquals("felix[234]cat", change); } @Test diff --git a/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Change.java b/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Change.java index 31560c7..f2f7533 100644 --- a/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Change.java +++ b/src/test/java/com/fleetpin/graphql/builder/inputgenericsRecords/Change.java @@ -15,12 +15,12 @@ import jakarta.annotation.Nullable; import java.util.List; -public record Change(@Nullable Wrapper> name) { +public record Change(@Nullable Wrapper> name, @Nullable Wrapper> age, @Nullable Wrapper description) { @Query public static String doChange(Change input) { if (input.name == null) { return "empty"; } - return input.name.wrap().getFirst(); + return input.name.wrap().getFirst() + input.age.wrap() + input.description.wrap(); } } From 01363f261eeee7771e49304e45ae070183bc9516 Mon Sep 17 00:00:00 2001 From: release-bot Date: Wed, 4 Sep 2024 21:39:44 +0000 Subject: [PATCH 096/112] [maven-release-plugin] prepare release graphql-builder-3.0.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fb4eb07..d82ceb6 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 3.0.1-SNAPSHOT + 3.0.1 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-3.0.1 From 8087504f707b707e17b9b05750743edf860caac1 Mon Sep 17 00:00:00 2001 From: release-bot Date: Wed, 4 Sep 2024 21:39:46 +0000 Subject: [PATCH 097/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d82ceb6..4753136 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 3.0.1 + 3.0.2-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-3.0.1 + HEAD From b99f42ef97594965076b043de6df4f33e764e14a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 04:31:40 +0000 Subject: [PATCH 098/112] Update dependency com.graphql-java:graphql-java to v22.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4753136..acf1820 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 2.17.2 1.16.3 1.2.1 - 22.2 + 22.3 From 71359c2b0e85b55066fa1ab19cd231d7051a2663 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Fri, 6 Sep 2024 09:41:21 +1200 Subject: [PATCH 099/112] fix nullable bug. Was applying on inner element not outer. --- .../graphql/builder/EntityHolder.java | 26 ++++++++------- .../fleetpin/graphql/builder/TypeMeta.java | 12 +++---- .../fleetpin/graphql/builder/RecordTest.java | 32 +++++++++++++++++-- .../builder/TypeGenericInputRecords.java | 21 ++++++++++++ .../graphql/builder/record/Queries.java | 5 +++ 5 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java index 52cb7bf..d585319 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityHolder.java @@ -13,10 +13,8 @@ import com.fleetpin.graphql.builder.TypeMeta.Flag; import com.fleetpin.graphql.builder.annotations.Id; -import com.fleetpin.graphql.builder.annotations.Union; import com.fleetpin.graphql.builder.mapper.InputTypeBuilder; import graphql.Scalars; -import graphql.com.google.common.collect.Sets; import graphql.schema.GraphQLInputType; import graphql.schema.GraphQLList; import graphql.schema.GraphQLNamedInputType; @@ -25,7 +23,6 @@ import graphql.schema.GraphQLNonNull; import graphql.schema.GraphQLOutputType; import graphql.schema.GraphQLTypeReference; -import graphql.schema.GraphQLUnionType; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.util.ArrayList; @@ -171,6 +168,14 @@ private static InputTypeBuilder process(Iterator> iterator, Iterator> iterator, Iterator new LinkedHashSet<>((int) (size / 0.75 + 1)), iterator, flags, resolver); } - if (Optional.class.isAssignableFrom(type)) { - return processOptional(iterator, flags, resolver); - } if (type.isArray()) { return processArray(type, iterator, flags, resolver); } @@ -189,9 +191,6 @@ private static InputTypeBuilder process(Iterator> iterator, Iterator) type); @@ -233,9 +232,12 @@ private static InputTypeBuilder processOptional(Iterator> iterator, Ite }; } - private static InputTypeBuilder processNull(Class type, InputTypeBuilder resolver) { - Iterator> iterator = List.>of(type).iterator(); - var mapper = process(iterator, Collections.emptyIterator(), resolver); + private static InputTypeBuilder processNull(Class type, Iterator> iterator, Iterator flags, InputTypeBuilder resolver) { + var classes = new ArrayList>(); + classes.add(type); + iterator.forEachRemaining(classes::add); + + var mapper = process(classes.iterator(), flags, resolver); return (obj, context, locale) -> { if (obj == null) { return null; diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java index 178c843..eb82b5a 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java @@ -159,6 +159,12 @@ private void findType(TypeMeta target, TypeVariable type, Class start, Annotated } private void process(Class type, Type genericType, AnnotatedElement element) { + if (element != null && (element.isAnnotationPresent(Nullable.class) || element.isAnnotationPresent(jakarta.annotation.Nullable.class))) { + if (!flags.contains(Flag.OPTIONAL)) { + flags.add(Flag.OPTIONAL); + } + } + if (type.isArray()) { flags.add(Flag.ARRAY); types.add(type); @@ -226,12 +232,6 @@ private void process(Class type, Type genericType, AnnotatedElement element) return; } - if (element != null && (element.isAnnotationPresent(Nullable.class) || element.isAnnotationPresent(jakarta.annotation.Nullable.class))) { - if (!flags.contains(Flag.OPTIONAL)) { - flags.add(Flag.OPTIONAL); - } - } - this.type = type; this.genericType = genericType; types.add(type); diff --git a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java index 062284b..b132f95 100644 --- a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java @@ -96,15 +96,23 @@ public void testSetNullable() { @Test public void testNullableArray() { List array = new ArrayList<>(); - array.add(null); array.add(true); - var response = execute("query nullableArrayTest($type: [Boolean]!){nullableArrayTest(type: $type)}", Map.of("type", array)); + var response = execute("query nullableArrayTest($type: [Boolean!]){nullableArrayTest(type: $type)}", Map.of("type", array)); var expected = new HashMap>(); expected.put("nullableArrayTest", array); assertTrue(response.getErrors().isEmpty()); assertEquals(expected, response.getData()); } + @Test + public void testNullable2Array() { + var response = execute("query nullableArrayTest($type: [Boolean!]){nullableArrayTest(type: $type)}", Map.of()); + var expected = new HashMap>(); + expected.put("nullableArrayTest", null); + assertTrue(response.getErrors().isEmpty()); + assertEquals(expected, response.getData()); + } + @Test public void testNullableArrayFails() { List array = new ArrayList<>(); @@ -113,6 +121,26 @@ public void testNullableArrayFails() { assertFalse(response.getErrors().isEmpty()); } + @Test + public void testNullableInnerArray() { + List array = new ArrayList<>(); + array.add(null); + array.add(true); + var response = execute("query nullableInnerArrayTest($type: [Boolean]!){nullableInnerArrayTest(type: $type)}", Map.of("type", array)); + var expected = new HashMap>(); + expected.put("nullableInnerArrayTest", array); + assertTrue(response.getErrors().isEmpty()); + assertEquals(expected, response.getData()); + } + + @Test + public void testNullableInnerArrayFails() { + List array = new ArrayList<>(); + array.add(true); + var response = execute("query nullableInnerArrayTest($type: [Boolean]){nullableInnerArrayTest(type: $type)}", Map.of("type", array)); + assertFalse(response.getErrors().isEmpty()); + } + private ExecutionResult execute(String query, Map variables) { GraphQL schema = GraphQL.newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.record"))).build(); var input = ExecutionInput.newExecutionInput(); diff --git a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java index c56d821..babab3b 100644 --- a/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java +++ b/src/test/java/com/fleetpin/graphql/builder/TypeGenericInputRecords.java @@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import graphql.ExceptionWhileDataFetching; import graphql.ExecutionResult; import graphql.GraphQL; import java.util.Map; @@ -40,6 +41,24 @@ public void textQuery() throws ReflectiveOperationException { assertEquals("felix[234]cat", change); } + @Test + public void textCorrectNullableQuery() throws ReflectiveOperationException { + Map response = execute(""" + query {doChange(input: { + name: { + wrap: ["felix"] + }, + age: { + }, + description: { + wrap: "cat" + } + })} + """).getData(); + var change = response.get("doChange"); + assertEquals("felixnullcat", change); + } + @Test public void textQueryNull() throws ReflectiveOperationException { Map response = execute(""" @@ -53,6 +72,8 @@ private ExecutionResult execute(String query) { GraphQL schema = GraphQL.newGraphQL(SchemaBuilder.build("com.fleetpin.graphql.builder.inputgenericsRecords")).build(); ExecutionResult result = schema.execute(query); if (!result.getErrors().isEmpty()) { + ExceptionWhileDataFetching d = (ExceptionWhileDataFetching) result.getErrors().get(0); + d.getException().printStackTrace(); throw new RuntimeException(result.getErrors().toString()); } return result; diff --git a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java index cb6904c..135c975 100644 --- a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java +++ b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java @@ -36,6 +36,11 @@ public static List nullableArrayTest(@Nullable List type) { return type; } + @Query + public static List> nullableInnerArrayTest(List> type) { + return type; + } + @GraphQLDescription("record Type") static final record InputType(@GraphQLDescription("the name") String name, int age, Optional weight) {} } From 9b9141cd40544c76a0e33d3cbe3c4bc08f1a71ff Mon Sep 17 00:00:00 2001 From: release-bot Date: Thu, 5 Sep 2024 21:49:06 +0000 Subject: [PATCH 100/112] [maven-release-plugin] prepare release graphql-builder-3.0.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index acf1820..e67e30f 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 3.0.2-SNAPSHOT + 3.0.2 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-3.0.2 From 89ba1668d8330a1d0fdd4e6e2e79a19096752f64 Mon Sep 17 00:00:00 2001 From: release-bot Date: Thu, 5 Sep 2024 21:49:07 +0000 Subject: [PATCH 101/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e67e30f..2ba49e3 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 3.0.2 + 3.0.3-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-3.0.2 + HEAD From e671fc93439b0bc90cda19004ada8dfab838bac8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:09:48 +0000 Subject: [PATCH 102/112] Update dependency org.pitest:pitest-maven to v1.17.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2ba49e3..85732f2 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 5.11.0 UTF-8 2.17.2 - 1.16.3 + 1.17.0 1.2.1 22.3 From 5a2df8cd84559a19e83523283ff65f45409e8a1f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:29:26 +0000 Subject: [PATCH 103/112] Update dependency com.mycila:license-maven-plugin to v4.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2ba49e3..fd1829a 100644 --- a/pom.xml +++ b/pom.xml @@ -149,7 +149,7 @@ com.mycila license-maven-plugin - 4.5 + 4.6 From 3c1747672ceec80006d7b4abbd6eceb2b0426965 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:18:08 +0000 Subject: [PATCH 104/112] Update dependency org.apache.maven.plugins:maven-gpg-plugin to v3.2.7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2ba49e3..83e2e6c 100644 --- a/pom.xml +++ b/pom.xml @@ -240,7 +240,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.5 + 3.2.7 sign-artifacts From 7cb8604fa20cba3da4cb5c6bb3deee569a2df75a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:34:34 +0000 Subject: [PATCH 105/112] Update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3.10.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2ba49e3..89d6fe6 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.10.0 + 3.10.1 attach-javadocs From 0c6dab1ff5add779828826890e1ad511b8238592 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:08:56 +0000 Subject: [PATCH 106/112] Update dependency org.junit.jupiter:junit-jupiter to v5.11.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2ba49e3..866fdb2 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ https://github.com/ashley-taylor/graphql-builder - 5.11.0 + 5.11.2 UTF-8 2.17.2 1.16.3 From b49e61be553019b9b1dc4612e8c320cdeaab2a05 Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Fri, 11 Oct 2024 13:49:47 +1300 Subject: [PATCH 107/112] add restrict to parent class --- .../fleetpin/graphql/builder/EntityUtil.java | 22 +++++++++ .../graphql/builder/SchemaBuilder.java | 4 +- .../restrictions/RestrictionTypesTest.java | 24 ++++++++++ .../RestrictedEntityInheritance.java | 31 ++++++++++++ .../parameter/RestrictedEntityParent.java | 48 +++++++++++++++++++ 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntityInheritance.java create mode 100644 src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntityParent.java diff --git a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java index f137ec8..f63efdf 100644 --- a/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java +++ b/src/main/java/com/fleetpin/graphql/builder/EntityUtil.java @@ -147,4 +147,26 @@ static boolean isContext(Class class1, Annotation[] annotations) { class1.isAssignableFrom(GraphQLContext.class) || class1.isAssignableFrom(DataFetchingEnvironment.class) || class1.isAnnotationPresent(Context.class) ); } + + static T getAnnotation(Class type, Class annotation) { + var response = type.getAnnotation(annotation); + if (response != null) { + return response; + } + + if (type.getSuperclass() != null) { + response = getAnnotation(type.getSuperclass(), annotation); + if (response != null) { + return response; + } + } + + for (var parent : type.getInterfaces()) { + response = getAnnotation(parent, annotation); + if (response != null) { + return response; + } + } + return null; + } } diff --git a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java index 5736a97..24bf2fc 100644 --- a/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java +++ b/src/main/java/com/fleetpin/graphql/builder/SchemaBuilder.java @@ -139,7 +139,7 @@ public GraphQLSchema.Builder build() { List> globalRestricts = new ArrayList<>(); for (var r : restrict) { - Restrict annotation = r.getAnnotation(Restrict.class); + Restrict annotation = EntityUtil.getAnnotation(r, Restrict.class); var factoryClass = annotation.value(); var factory = factoryClass.getConstructor().newInstance(); if (!factory.extractType().isAssignableFrom(r)) { @@ -151,7 +151,7 @@ public GraphQLSchema.Builder build() { } for (var r : restricts) { - Restricts annotations = r.getAnnotation(Restricts.class); + Restricts annotations = EntityUtil.getAnnotation(r, Restricts.class); for (Restrict annotation : annotations.value()) { var factoryClass = annotation.value(); var factory = factoryClass.getConstructor().newInstance(); diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java index 31d6f90..62c766b 100644 --- a/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/RestrictionTypesTest.java @@ -42,6 +42,13 @@ public static void init() throws ReflectiveOperationException { private static String singleOptionalQueryGql = "query entityQuery( $allowed: Boolean ) { singleOptional(allowed: $allowed) { __typename } }"; private static String listQueryGql = "query entityQuery( $allowed: [Boolean!]! ) { list(allowed: $allowed) { __typename } }"; private static String listOptionalQueryGql = "query entityQuery( $allowed: [Boolean!] ) { listOptional(allowed: $allowed) { __typename } }"; + private static final String LIST_QUERY_INHERITANCE_GPL = """ + query entityQuery( $allowed: [Boolean!]! ) { + listInheritance(allowed: $allowed) { + __typename + } + } + """; @Test public void singleEntityQuery() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { @@ -84,6 +91,23 @@ public void listEntityQuery() throws ReflectiveOperationException, JsonMappingEx Assertions.assertEquals(0, responseNoneAllowed.get("list").size()); } + @Test + public void listEntityInheritanceQuery() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { + Map variables = new HashMap<>(); + + variables.put("allowed", Arrays.asList(true, true, true)); + Map> responseAllAllowed = execute(LIST_QUERY_INHERITANCE_GPL, variables).getData(); + Assertions.assertEquals(3, responseAllAllowed.get("listInheritance").size()); + + variables.put("allowed", Arrays.asList(true, false, true)); + Map> responseSomeAllowed = execute(LIST_QUERY_INHERITANCE_GPL, variables).getData(); + Assertions.assertEquals(2, responseSomeAllowed.get("listInheritance").size()); + + variables.put("allowed", Arrays.asList(false, false, false)); + Map> responseNoneAllowed = execute(LIST_QUERY_INHERITANCE_GPL, variables).getData(); + Assertions.assertEquals(0, responseNoneAllowed.get("listInheritance").size()); + } + @Test public void optionalListEntityQuery() throws ReflectiveOperationException, JsonMappingException, JsonProcessingException { Map variables = new HashMap<>(); diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntityInheritance.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntityInheritance.java new file mode 100644 index 0000000..fa5b218 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntityInheritance.java @@ -0,0 +1,31 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.restrictions.parameter; + +import com.fleetpin.graphql.builder.annotations.Query; +import java.util.List; +import java.util.stream.Collectors; + +public class RestrictedEntityInheritance extends RestrictedEntityParent { + + @Query + public static List listInheritance(List allowed) { + return allowed + .stream() + .map(isAllowed -> { + RestrictedEntityInheritance entity = new RestrictedEntityInheritance(); + entity.setAllowed(isAllowed); + return entity; + }) + .collect(Collectors.toList()); + } +} diff --git a/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntityParent.java b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntityParent.java new file mode 100644 index 0000000..e738927 --- /dev/null +++ b/src/test/java/com/fleetpin/graphql/builder/restrictions/parameter/RestrictedEntityParent.java @@ -0,0 +1,48 @@ +/* + * 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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.restrictions.parameter; + +import com.fleetpin.graphql.builder.RestrictType; +import com.fleetpin.graphql.builder.RestrictTypeFactory; +import com.fleetpin.graphql.builder.annotations.Restrict; +import graphql.schema.DataFetchingEnvironment; +import java.util.concurrent.CompletableFuture; + +@Restrict(RestrictedEntityParent.EntityRestrictions.class) +public abstract class RestrictedEntityParent { + + private boolean allowed; + + public boolean isAllowed() { + return allowed; + } + + public void setAllowed(boolean allowed) { + this.allowed = allowed; + } + + public static class EntityRestrictions implements RestrictTypeFactory { + + @Override + public CompletableFuture> create(DataFetchingEnvironment context) { + return CompletableFuture.completedFuture(new DatabaseRestrict()); + } + + public static class DatabaseRestrict implements RestrictType { + + @Override + public CompletableFuture allow(RestrictedEntityParent obj) { + return CompletableFuture.completedFuture(obj.isAllowed()); + } + } + } +} From 2e478162d50c708f0c443bb54de94f377a593a5c Mon Sep 17 00:00:00 2001 From: ashley-taylor Date: Fri, 11 Oct 2024 14:58:54 +1300 Subject: [PATCH 108/112] ability to mark inner entity as nullable if require something more complex must use Optional --- .../fleetpin/graphql/builder/TypeMeta.java | 7 ++++++ .../builder/annotations/InnerNullable.java | 23 +++++++++++++++++++ .../fleetpin/graphql/builder/RecordTest.java | 20 ++++++++++++++++ .../graphql/builder/record/Queries.java | 7 ++++++ 4 files changed, 57 insertions(+) create mode 100644 src/main/java/com/fleetpin/graphql/builder/annotations/InnerNullable.java diff --git a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java index eb82b5a..37cf193 100644 --- a/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java +++ b/src/main/java/com/fleetpin/graphql/builder/TypeMeta.java @@ -11,6 +11,7 @@ */ package com.fleetpin.graphql.builder; +import com.fleetpin.graphql.builder.annotations.InnerNullable; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -232,6 +233,12 @@ private void process(Class type, Type genericType, AnnotatedElement element) return; } + if (element != null && (element.isAnnotationPresent(InnerNullable.class))) { + if (!flags.contains(Flag.OPTIONAL)) { + flags.add(Flag.OPTIONAL); + } + } + this.type = type; this.genericType = genericType; types.add(type); diff --git a/src/main/java/com/fleetpin/graphql/builder/annotations/InnerNullable.java b/src/main/java/com/fleetpin/graphql/builder/annotations/InnerNullable.java new file mode 100644 index 0000000..2802df1 --- /dev/null +++ b/src/main/java/com/fleetpin/graphql/builder/annotations/InnerNullable.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 limitations under + * the License. + */ +package com.fleetpin.graphql.builder.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target({ ElementType.METHOD, ElementType.PARAMETER }) +public @interface InnerNullable { +} diff --git a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java index b132f95..f077c4d 100644 --- a/src/test/java/com/fleetpin/graphql/builder/RecordTest.java +++ b/src/test/java/com/fleetpin/graphql/builder/RecordTest.java @@ -141,6 +141,26 @@ public void testNullableInnerArrayFails() { assertFalse(response.getErrors().isEmpty()); } + @Test + public void testInnerNullableArray() { + List array = new ArrayList<>(); + array.add(null); + array.add(true); + var response = execute("query innerNullableArrayTest($type: [Boolean]!){innerNullableArrayTest(type: $type)}", Map.of("type", array)); + var expected = new HashMap>(); + expected.put("innerNullableArrayTest", array); + assertTrue(response.getErrors().isEmpty()); + assertEquals(expected, response.getData()); + } + + @Test + public void testInnerNullableArrayFails() { + List array = new ArrayList<>(); + array.add(true); + var response = execute("query innerNullableArrayTest($type: [Boolean]){innerNullableArrayTest(type: $type)}", Map.of("type", array)); + assertFalse(response.getErrors().isEmpty()); + } + private ExecutionResult execute(String query, Map variables) { GraphQL schema = GraphQL.newGraphQL(new IntrospectionWithDirectivesSupport().apply(SchemaBuilder.build("com.fleetpin.graphql.builder.record"))).build(); var input = ExecutionInput.newExecutionInput(); diff --git a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java index 135c975..8c09be2 100644 --- a/src/test/java/com/fleetpin/graphql/builder/record/Queries.java +++ b/src/test/java/com/fleetpin/graphql/builder/record/Queries.java @@ -12,6 +12,7 @@ package com.fleetpin.graphql.builder.record; import com.fleetpin.graphql.builder.annotations.GraphQLDescription; +import com.fleetpin.graphql.builder.annotations.InnerNullable; import com.fleetpin.graphql.builder.annotations.Query; import java.util.List; import java.util.Optional; @@ -36,6 +37,12 @@ public static List nullableArrayTest(@Nullable List type) { return type; } + @Query + @InnerNullable + public static List innerNullableArrayTest(@InnerNullable List type) { + return type; + } + @Query public static List> nullableInnerArrayTest(List> type) { return type; From 67a8eac1c4637f5d0eda3012ed9c9b6ec7bad203 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2024 02:02:13 +0000 Subject: [PATCH 109/112] Update jackson.version to v2.18.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 54ed175..636ec7a 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 5.11.2 UTF-8 - 2.17.2 + 2.18.0 1.17.0 1.2.1 22.3 From 4e6ffafcdcfba8e03912f8d216502de37b38339a Mon Sep 17 00:00:00 2001 From: Ashley Taylor <7232476+ashley-taylor@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:12:46 +1300 Subject: [PATCH 110/112] Update release.yml add options --- .github/workflows/release.yml | 52 ++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99794fc..39fccf4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,19 @@ name: Release permissions: - contents: write -on: workflow_dispatch + contents: write + packages: write +on: + workflow_dispatch: + inputs: + releaseType: + type: choice + description: "Release type" + required: true + default: minor + options: + - patch + - minor + - major jobs: release: runs-on: ubuntu-latest @@ -37,10 +49,42 @@ jobs: rm ./private.key gpg --list-secret-keys --keyid-format LONG + - name: Get current development version + id: get_version + run: | + VERSION=$( mvn help:evaluate -Dexpression=project.version -q -DforceStdout | sed 's/-SNAPSHOT//' ) + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Generate versions + id: generate_versions + uses: WyriHaximus/github-action-next-semvers@v1.2.1 + with: + version: ${{ steps.get_version.outputs.version }} + + - name: Pick release version + id: pick_release_version + run: | + VERSION=$( + case ${{ github.event.inputs.releaseType }} in + ("minor") echo "${{ steps.generate_versions.outputs.minor }}" ;; + ("major") echo "${{ steps.generate_versions.outputs.major }}" ;; + ("patch") echo "${{ steps.generate_versions.outputs.patch }}" ;; + esac + ) + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: prepare run: | - mvn release:prepare -Dusername=${{ secrets.GITHUB_TOKEN }} -P sonatype + mvn release:prepare -Dusername=${{ secrets.GITHUB_TOKEN }} \ + -DreleaseVersion=${{ steps.pick_release_version.outputs.version }} \ + -DdevelopmentVersion=${{ steps.pick_release_version.outputs.version }}-SNAPSHOT \ + -P sonatype - name: release run: | - mvn release:perform -Dusername=${{ secrets.GITHUB_TOKEN }} -P sonatype + mvn release:perform -Dusername=${{ secrets.GITHUB_TOKEN }} \ + -DreleaseVersion=${{ steps.pick_release_version.outputs.version }} \ + -DdevelopmentVersion=${{ steps.pick_release_version.outputs.version }}-SNAPSHOT \ + -Darguments="-DskipTests" \ + -P sonatype From b6e51eb67692ccad81fa34ab3ac39eb3525a3797 Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 11 Oct 2024 02:19:28 +0000 Subject: [PATCH 111/112] [maven-release-plugin] prepare release graphql-builder-3.1.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f819ff6..b0533c2 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 3.0.3-SNAPSHOT + 3.1.0 GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - HEAD + graphql-builder-3.1.0 From ee2f11c451fbea5ef399af7b8b8296b7050fb394 Mon Sep 17 00:00:00 2001 From: release-bot Date: Fri, 11 Oct 2024 02:19:29 +0000 Subject: [PATCH 112/112] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b0533c2..e572a4c 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 4.0.0 com.fleetpin graphql-builder - 3.1.0 + 3.1.0-SNAPSHOT GraphQL Builder Builds a graphql schema from a model using reflection @@ -47,7 +47,7 @@ https://github.com/ashley-taylor/graphql-builder scm:git:https://github.com/ashley-taylor/graphql-builder.git scm:git:https://github.com/ashley-taylor/graphql-builder.git - graphql-builder-3.1.0 + HEAD