From 850c00a080f41a591b1aca514d7e604dc2b73b44 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 3 Mar 2020 16:33:10 +0800 Subject: [PATCH 001/178] Initial helidon-microprofile-graphql project setup --- dependencies/pom.xml | 25 + microprofile/graphql/pom.xml | 21 + microprofile/graphql/server/pom.xml | 86 ++++ .../application/GraphQLApplication.java | 37 ++ .../server/application/GraphQLResource.java | 61 +++ .../server/application/package-info.java | 21 + .../model/AbstractDescriptiveElement.java | 57 +++ .../graphql/server/model/Argument.java | 173 +++++++ .../server/model/DescriptiveElement.java | 57 +++ .../graphql/server/model/Directive.java | 162 +++++++ .../graphql/server/model/Enum.java | 83 ++++ .../graphql/server/model/FieldDefinition.java | 195 ++++++++ .../graphql/server/model/InputType.java | 52 ++ .../graphql/server/model/Scalar.java | 117 +++++ .../graphql/server/model/Schema.java | 448 ++++++++++++++++++ .../graphql/server/model/SchemaGenerator.java | 47 ++ .../graphql/server/model/Type.java | 263 ++++++++++ .../graphql/server/model/package-info.java | 20 + .../graphql/server/package-info.java | 20 + .../graphql/server/util/JandexUtils.java | 111 +++++ .../graphql/server/util/SchemaUtils.java | 191 ++++++++ .../graphql/server/util/package-info.java | 20 + .../server/src/main/java/module-info.java | 33 ++ .../src/main/resources/META-INF/beans.xml | 26 + .../META-INF/microprofile-config.properties | 2 + .../src/main/resources/logging.properties | 23 + .../server/src/main/resources/web/index.html | 57 +++ .../graphql/server/AbstractGraphQLIT.java | 37 ++ .../graphql/server/AbstractGraphQLTest.java | 101 ++++ .../graphql/server/model/ArgumentTest.java | 76 +++ .../graphql/server/model/DirectiveTest.java | 110 +++++ .../graphql/server/model/EnumTest.java | 67 +++ .../server/model/FieldDefinitionTest.java | 172 +++++++ .../graphql/server/model/ScalarTest.java | 45 ++ .../graphql/server/model/SchemaTest.java | 141 ++++++ .../graphql/server/model/TypeTest.java | 191 ++++++++ .../server/resource/GraphQLResourceTest.java | 10 + .../server/test/EnumTestNoEnumName.java | 32 ++ .../server/test/EnumTestWithEnumName.java | 32 ++ .../EnumTestWithNameAndNameAnnotation.java | 35 ++ .../test/EnumTestWithNameAnnotation.java | 34 ++ .../graphql/server/util/JandexUtilsTest.java | 83 ++++ .../graphql/server/util/SchemaUtilsTest.java | 63 +++ .../src/test/resources/META-INF/beans.xml | 26 + .../resources/test-results/enum-test-01.txt | 7 + .../resources/test-results/enum-test-02.txt | 6 + .../resources/test-results/schema-test-01.txt | 7 + .../resources/test-results/schema-test-02.txt | 11 + .../resources/test-results/schema-test-03.txt | 15 + .../resources/test-results/schema-test-04.txt | 17 + microprofile/pom.xml | 1 + 51 files changed, 3727 insertions(+) create mode 100644 microprofile/graphql/pom.xml create mode 100644 microprofile/graphql/server/pom.xml create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/package-info.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Directive.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/InputType.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/package-info.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/package-info.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/package-info.java create mode 100644 microprofile/graphql/server/src/main/java/module-info.java create mode 100644 microprofile/graphql/server/src/main/resources/META-INF/beans.xml create mode 100644 microprofile/graphql/server/src/main/resources/META-INF/microprofile-config.properties create mode 100644 microprofile/graphql/server/src/main/resources/logging.properties create mode 100644 microprofile/graphql/server/src/main/resources/web/index.html create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/ArgumentTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/DirectiveTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/EnumTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/FieldDefinitionTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/ScalarTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/TypeTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/resource/GraphQLResourceTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestNoEnumName.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithEnumName.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithNameAndNameAnnotation.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithNameAnnotation.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java create mode 100644 microprofile/graphql/server/src/test/resources/META-INF/beans.xml create mode 100644 microprofile/graphql/server/src/test/resources/test-results/enum-test-01.txt create mode 100644 microprofile/graphql/server/src/test/resources/test-results/enum-test-02.txt create mode 100644 microprofile/graphql/server/src/test/resources/test-results/schema-test-01.txt create mode 100644 microprofile/graphql/server/src/test/resources/test-results/schema-test-02.txt create mode 100644 microprofile/graphql/server/src/test/resources/test-results/schema-test-03.txt create mode 100644 microprofile/graphql/server/src/test/resources/test-results/schema-test-04.txt diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 73c4b7c0b8b..4a6f2ba7a68 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -52,6 +52,9 @@ 1.30.11 2.3.3 20.1.0 + 14.0 + 1.0.1 + 19.2.0 1.32.1 28.1-jre 1.4.199 @@ -83,6 +86,7 @@ 2.10 1.4 2.2 + 1.0 1.1.1 2.3.2 1.1.2 @@ -579,6 +583,17 @@ + + org.eclipse.microprofile.graphql + microprofile-graphql-api + ${version.lib.microprofile-graphql} + + + org.osgi + org.osgi.annotation.versioning + + + org.eclipse.microprofile.jwt microprofile-jwt-auth-api @@ -953,6 +968,16 @@ graal-sdk ${version.lib.graalvm} + + com.graphql-java + graphql-java + ${version.lib.graphql-java} + + + com.graphql-java + graphql-java-extended-scalars + ${version.lib.graphql-java.extended.scalars} + org.graalvm.nativeimage svm diff --git a/microprofile/graphql/pom.xml b/microprofile/graphql/pom.xml new file mode 100644 index 00000000000..ee02f4da23f --- /dev/null +++ b/microprofile/graphql/pom.xml @@ -0,0 +1,21 @@ + + + + io.helidon.microprofile + helidon-microprofile-project + 2.0.0-SNAPSHOT + + 4.0.0 + + io.helidon.microprofile.graphql + helidon-microprofile-graphql + Helidon Microprofile GraphQL + pom + + + server + + + \ No newline at end of file diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml new file mode 100644 index 00000000000..087d6e672ff --- /dev/null +++ b/microprofile/graphql/server/pom.xml @@ -0,0 +1,86 @@ + + + + io.helidon.microprofile.graphql + helidon-microprofile-graphql + 2.0.0-SNAPSHOT + + 4.0.0 + + helidon-microprofile-graphql-server + Helidon Microprofile GraphQL Server + The microprofile GraphQL Server implementation + + + + org.eclipse.microprofile.graphql + microprofile-graphql-api + + + com.graphql-java + graphql-java + + + com.graphql-java + graphql-java-extended-scalars + + + javax.enterprise + cdi-api + + + io.helidon.microprofile.server + helidon-microprofile-server + + + org.jboss.spec.javax.el + jboss-el-api_3.0_spec + + + org.jboss.spec.javax.interceptor + jboss-interceptors-api_1.2_spec + + + + + io.helidon.microprofile.config + helidon-microprofile-config + runtime + + + javax.interceptor + javax.interceptor-api + + + org.jboss + jandex + + + + + org.jboss.weld + weld-junit5 + test + + + io.helidon.microprofile.bundles + internal-test-libs + test + + + + + + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + + + + + \ No newline at end of file diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java new file mode 100644 index 00000000000..2bfd04cf770 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.application; + +import java.util.Set; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +/** + * Defines the JAX-RS endpoints for microprofile-grapghql implementation. + */ +@ApplicationScoped +@ApplicationPath("/graphql") +public class GraphQLApplication extends Application { + @Override + public Set> getClasses() { + return Set.of( + GraphQLResource.class + ); + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java new file mode 100644 index 00000000000..f60cb2d56c5 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.application; + +import javax.enterprise.context.RequestScoped; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +@Path("/") +@RequestScoped +public class GraphQLResource { + /** + * Process a GET request. + * + * @param query query to execute + * @param operation optional operation name. e.g "query name { fields }" + * @param variables optional variables + * @return a {@link Response} object. + */ + @GET + @Produces({ MediaType.APPLICATION_JSON }) + @Consumes({ MediaType.APPLICATION_JSON }) + public Response processGraphQLQueryGET(@QueryParam("query") String query, + @QueryParam("operationName") String operation, + @QueryParam("variables") String variables) { + return Response.ok("GET").build(); + } + + /** + * Process a POST request. + * + * @param body body of the request as Json + * @return a {@link Response} object. + */ + @POST + @Produces({ MediaType.APPLICATION_JSON }) + @Consumes({ MediaType.APPLICATION_JSON }) + public Response processGraphQLQueryPOST(String body) { + return Response.ok("POST").build(); + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/package-info.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/package-info.java new file mode 100644 index 00000000000..6708d7b1f2f --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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. + */ + +/** + * Microprofile GraphQL application classes. + * + */ +package io.helidon.microprofile.graphql.server.application; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java new file mode 100644 index 00000000000..ced85144a02 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.Objects; + +/** + * An abstract implementation of a {@link DescriptiveElement}. + */ +public class AbstractDescriptiveElement implements DescriptiveElement { + + /** + * The description for an element. + */ + protected String description; + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AbstractDescriptiveElement that = (AbstractDescriptiveElement) o; + return Objects.equals(description, that.description); + } + + @Override + public int hashCode() { + return Objects.hash(description); + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java new file mode 100644 index 00000000000..c1695b3c17f --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.Objects; + +/** + * The representation of a GraphQL Argument or Parameter. + */ +public class Argument extends AbstractDescriptiveElement + implements SchemaGenerator { + /** + * Argument name. + */ + private final String argumentName; + + /** + * Argument type. + */ + private String argumentType; + + /** + * Indicates if the argument is mandatory. + */ + private final boolean isMandatory; + + /** + * The default value for this argument. + */ + private final Object defaultValue; + + /** + * Construct a {@link Argument} instance. + * + * @param argumentName name of the argument + * @param argumentType type of the argument + * @param isMandatory indicates if the argument is mandatory + */ + public Argument(String argumentName, String argumentType, boolean isMandatory, Object defaultValue) { + this.argumentName = argumentName; + this.argumentType = argumentType; + this.isMandatory = isMandatory; + this.defaultValue = defaultValue; + } + + /** + * Return the GraphQL schema representation of the element. + * + * @return the GraphQL schema representation of the element. + */ + @Override + public String getSchemaAsString() { + StringBuilder sb = new StringBuilder(getSchemaElementDescription()) + .append(getArgumentName()) + .append(COLON) + .append(SPACER) + .append(getArgumentType()); + + if (isMandatory) { + sb.append(MANDATORY); + } + + if (defaultValue != null) { + sb.append(SPACER) + .append(EQUALS) + .append(SPACER); + boolean isString = defaultValue instanceof String; + if (isString) { + sb.append(QUOTE) + .append(defaultValue) + .append(QUOTE); + } else { + sb.append(defaultValue); + } + } + + return sb.toString(); + } + + /** + * Return the argument name. + * + * @return the argument name + */ + public String getArgumentName() { + return argumentName; + } + + /** + * Return the argument type. + * + * @return the argument type + */ + public String getArgumentType() { + return argumentType; + } + + /** + * Indicates if the argument is mandatory. + * + * @return indicates if the argument is mandatory + */ + public boolean isMandatory() { + return isMandatory; + } + + /** + * Set the type of the argument. + * + * @param argumentType the type of the argument + */ + public void setArgumentType(String argumentType) { + this.argumentType = argumentType; + } + + /** + * Return the default value for this argument. + * + * @return the default value for this argument + */ + public Object getDefaultValue() { + return defaultValue; + } + + @Override + public String toString() { + return "Argument{" + + "argumentName='" + argumentName + '\'' + + ", argumentType='" + argumentType + '\'' + + ", isMandatory=" + isMandatory + + ", defaultValue=" + defaultValue + + ", description='" + description + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + Argument argument = (Argument) o; + return isMandatory == argument.isMandatory && + Objects.equals(argumentName, argument.argumentName) && + Objects.equals(argumentType, argument.argumentType) && + Objects.equals(description, argument.description) && + Objects.equals(defaultValue, argument.defaultValue); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), argumentName, argumentType, isMandatory, defaultValue, description); + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java new file mode 100644 index 00000000000..35c0ade5d83 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.COMMENT; +import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.NEWLINE; +import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.NOTHING; +import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.SPACER; + +/** + * Describes an element that has a description. + */ +public interface DescriptiveElement { + + /** + * Set the description for this element. + * @param description the description for this element + */ + void setDescription(String description); + + /** + * Return the description for this element. + * @return the description for this element + */ + String getDescription(); + + /** + * Return the description of the schema element. + * Only valid for Type, Field, Method, Parameter + * @return the description of the schema element. + */ + default String getSchemaElementDescription() { + + if (getDescription() != null) { + return new StringBuilder(COMMENT) + .append(SPACER) + .append(getDescription()) + .append(NEWLINE) + .toString(); + } + return NOTHING; + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Directive.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Directive.java new file mode 100644 index 00000000000..60f5b087322 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Directive.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +/** + * The representation of a GraphQL directive. + */ +public class Directive implements SchemaGenerator { + + /** + * The name of the directive. + */ + private final String name; + + /** + * The list of arguments for the directive. + */ + private final List listArguments; + + /** + * The locations the directive applies to. + */ + private final Set setLocations; + + /** + * Construct a {@link Directive}. + * + * @param name name of the directive + */ + public Directive(String name) { + this.name = name; + this.listArguments = new ArrayList<>(); + this.setLocations = new LinkedHashSet<>(); + } + + /** + * Return the GraphQL schema representation of the element. + * + * @return the GraphQL schema representation of the element. + */ + @Override + public String getSchemaAsString() { + StringBuilder sb = new StringBuilder("directive @" + getName()); + + if (listArguments.size() > 0) { + sb.append("("); + AtomicBoolean isFirst = new AtomicBoolean(true); + listArguments.forEach(a -> { + String delim = isFirst.get() ? "" : ", "; + isFirst.set(false); + + sb.append(delim).append(a.getArgumentName()).append(": ").append(a.getArgumentType()); + if (a.isMandatory()) { + sb.append('!'); + } + }); + + sb.append(")"); + } + + sb.append(" on ") + .append(setLocations.stream().sequential().collect(Collectors.joining("|"))); + + return sb.toString(); + } + + /** + * Add an {@link Argument} to the {@link Directive}. + * + * @param argument the {@link Argument} to add + */ + public void addArgument(Argument argument) { + listArguments.add(argument); + } + + /** + * Add a location to the {@link Directive}. + * + * @param location the location to add + */ + public void addLocation(String location) { + setLocations.add(location); + } + + /** + * Return the name of the {@link Directive}. + * + * @return the name of the {@link Directive} + */ + public String getName() { + return name; + } + + /** + * Return the {@link List} of {@link Argument}s. + * + * @return the {@link List} of {@link Argument}s + */ + public List getArguments() { + return listArguments; + } + + /** + * Return the {@link Set} of locations. + * + * @return the {@link Set} of locations + */ + public Set getLocations() { + return setLocations; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Directive directive = (Directive) o; + return Objects.equals(name, directive.name) + && Objects.equals(listArguments, directive.listArguments) + && Objects.equals(setLocations, directive.setLocations); + } + + @Override + public int hashCode() { + return Objects.hash(name, listArguments, setLocations); + } + + @Override + public String toString() { + return "Directive{" + + "name='" + name + '\'' + + ", listArguments=" + listArguments + + ", setLocations=" + setLocations + + '}'; + } + +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java new file mode 100644 index 00000000000..48a1c85c1c7 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.ArrayList; +import java.util.List; + + +/** + * The representation of a GraphQL Enum. + */ +public class Enum extends AbstractDescriptiveElement + implements SchemaGenerator { + + /** + * The name of the enum. + */ + private String name; + + /** + * The values for the enum. + */ + private List values; + + public Enum(String name) { + this.name = name; + this.values = new ArrayList<>(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getValues() { + return values; + } + + public void addValue(String value) { + this.values.add(value); + } + + @Override + public String getSchemaAsString() { + StringBuilder sb = new StringBuilder(getSchemaElementDescription()) + .append("enum") + .append(SPACER) + .append(getName()) + .append(SPACER) + .append(OPEN_CURLY) + .append(NEWLINE); + + values.forEach(v -> sb.append(SPACER).append(v).append(NEWLINE)); + + return sb.append(CLOSE_CURLY).append(NEWLINE).toString(); + } + + @Override + public String toString() { + return "Enum{" + + "name='" + name + '\'' + + ", values=" + values + + ", description='" + description + '\'' + + '}'; + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java new file mode 100644 index 00000000000..47222ef82bb --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import graphql.schema.DataFetcher; + +/** + * The representation of a GraphQL Field Definition. + */ +public class FieldDefinition extends AbstractDescriptiveElement + implements SchemaGenerator { + /** + * Name of the field definition. + */ + private final String name; + + /** + * Return type. + */ + private String returnType; + + /** + * Indicates if the return type is an array type such as a native array([]) or a List, Collection, etc. + */ + private final boolean isArrayReturnType; + + /** + * Indicates if the return type is mandatory. + */ + private final boolean isReturnTypeMandatory; + + /** + * List of arguments. + */ + private final List listArguments; + + /** + * {@link DataFetcher} to override default behaviour of field. + */ + private DataFetcher dataFetcher; + + /** + * Construct a {@link FieldDefinition}. + * + * @param name field definition name + * @param returnType return type + * @param isArrayReturnType indicates if the return type is an array type such as a native array([]) or a List, Collection, etc + * @param isReturnTypeMandatory indicates if the return type is mandatory. + */ + public FieldDefinition(String name, String returnType, boolean isArrayReturnType, boolean isReturnTypeMandatory) { + this.name = name; + this.returnType = returnType; + this.listArguments = new ArrayList<>(); + this.isArrayReturnType = isArrayReturnType; + this.isReturnTypeMandatory = isReturnTypeMandatory; + } + + @Override + public String getSchemaAsString() { + StringBuilder sb = new StringBuilder(getSchemaElementDescription()) + .append(getName()); + + // set compact mode if no argument descriptions exist + boolean isCompact = listArguments.stream().filter(a -> a.getDescription() != null).count() == 0; + + if (listArguments.size() > 0) { + sb.append(OPEN_PARENTHESES).append(isCompact ? NOTHING : NEWLINE); + sb.append(listArguments.stream() + .map(Argument::getSchemaAsString) + .collect(Collectors.joining(isCompact ? COMMA_SPACE : COMMA_NEWLINE))); + sb.append(isCompact ? NOTHING : NEWLINE).append(CLOSE_PARENTHESES); + } + + sb.append(COLON); + + if (isArrayReturnType()) { + sb.append(SPACER).append(OPEN_SQUARE).append(getReturnType()).append(CLOSE_SQUARE); + } + else { + sb.append(SPACER).append(getReturnType()); + } + + if (isReturnTypeMandatory()) { + sb.append(MANDATORY); + } + + return sb.toString(); + } + + /** + * Return the name for the field definition. + * + * @return the name for the field definition + */ + public String getName() { + return name; + } + + /** + * Return the {@link List} of {@link Argument}s. + * @return the {@link List} of {@link Argument}s + */ + public List getArguments() { + return listArguments; + } + + /** + * Return the return type. + * @return the return type + */ + public String getReturnType() { + return returnType; + } + + /** + * Indicates if the return type is an array type. + * @return if the return type is an array type + */ + public boolean isArrayReturnType() { + return isArrayReturnType; + } + + /** + * Indicates if the return type is mandatory. + * @return if the return type is mandatory + */ + public boolean isReturnTypeMandatory() { + return isReturnTypeMandatory; + } + + /** + * Return the {@link DataFetcher} for this {@link FieldDefinition}. + * @return he {@link DataFetcher} for this {@link FieldDefinition} + */ + public DataFetcher getDataFetcher() { + return dataFetcher; + } + + /** + * Set the return type. + * @param sReturnType the return type + */ + public void setReturnType(String sReturnType) { + returnType = sReturnType; + } + + /** + * Set the {@link DataFetcher} which will override the default {@link DataFetcher} for the field. + * @param dataFetcher the {@link DataFetcher} + */ + public void setDataFetcher(DataFetcher dataFetcher) { + this.dataFetcher = dataFetcher; + } + + /** + * Add a {@link Argument} to this {@link FieldDefinition}. + * + * @param argument {@link Argument} to add + */ + public void addArgument(Argument argument) { + listArguments.add(argument); + } + + @Override + public String toString() { + return "FieldDefinition{" + + "name='" + name + '\'' + + ", returnType='" + returnType + '\'' + + ", isArrayReturnType=" + isArrayReturnType + + ", isReturnTypeMandatory=" + isReturnTypeMandatory + + ", listArguments=" + listArguments + + ", description='" + description + '\'' + + '}'; + } + + +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/InputType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/InputType.java new file mode 100644 index 00000000000..011eb9b343d --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/InputType.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +/** + * The representation of a GraphQL Input Type. + */ +public class InputType extends Type { + + /** + * Construct an {@link InputType}. + * + * @param name name for the input type + * @param keyClassName fully qualified key class name + * @param valueClassName fully qualified value name + */ + public InputType(String name, String keyClassName, String valueClassName) { + super(name, keyClassName, valueClassName); + } + + @Override + public void addFieldDefinition(FieldDefinition fieldDefinition) { + if (fieldDefinition.getArguments().size() > 0) { + throw new IllegalArgumentException("input types cannot have fields with arguments"); + } + super.addFieldDefinition(fieldDefinition); + } + + @Override + protected String getGraphQLName() { + return "input"; + } + + @Override + public String toString() { + return "InputType" + toStringInternal(); + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java new file mode 100644 index 00000000000..19f86e73b22 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.Objects; + +import graphql.schema.GraphQLScalarType; + +/** + * The representation of a GraphQL Scalar. + */ +public class Scalar implements SchemaGenerator { + + /** + * Name of the Scalar. + */ + private String name; + + /** + * Actual class name. + */ + private String actualClass; + + /** + * {@link GraphQLScalarType} to convert this {@link Scalar}. + */ + private GraphQLScalarType graphQLScalarType; + + /** + * Construct a {@link Scalar}. + * + * @param name name + * @param actualClass actual class name + * @param graphQLScalarType {@link GraphQLScalarType} to convert this {@link Scalar}. + */ + public Scalar(String name, String actualClass, GraphQLScalarType graphQLScalarType) { + this.name = name; + this.actualClass = actualClass; + this.graphQLScalarType = graphQLScalarType; + } + + /** + * Return the name of the {@link Scalar}. + * + * @return the name of the {@link Scalar} + */ + public String getName() { + return name; + } + + /** + * Return the actual class name of the {@link Scalar}. + * + * @return the actual class name of the {@link Scalar} + */ + public String getActualClass() { + return actualClass; + } + + /** + * Return the {@link GraphQLScalarType} instance. + * + * @return the {@link GraphQLScalarType} instance. + */ + public GraphQLScalarType getGraphQLScalarType() { + return graphQLScalarType; + } + + @Override + public String getSchemaAsString() { + return new StringBuilder("scalar ") + .append(getName()) + .toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Scalar scalar = (Scalar) o; + return Objects.equals(name, scalar.name) + && Objects.equals(actualClass, scalar.actualClass) + && Objects.equals(graphQLScalarType, scalar.graphQLScalarType); + } + + @Override + public int hashCode() { + return Objects.hash(name, actualClass, graphQLScalarType); + } + + @Override + public String toString() { + return "Scalar{" + + "name='" + name + '\'' + + ", actualClass='" + actualClass + '\'' + + ", graphQLScalarType=" + graphQLScalarType + '}'; + } + +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java new file mode 100644 index 00000000000..2732b98328a --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.ArrayList; +import java.util.List; + +import graphql.schema.idl.RuntimeWiring; + +import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring; + +/** + * The representation of a GraphQL Schema. + */ +public class Schema implements SchemaGenerator { + + /** + * Default query name. + */ + public static final String QUERY = "Query"; + + /** + * Default mutation name. + */ + public static final String MUTATION = "Mutation"; + + /** + * Default subscription name. + */ + public static final String SUBSCRIPTION = "Subscription"; + + /** + * The top level query name. + */ + private String queryName; + + /** + * The top level mutation name. + */ + private String mutationName; + + /** + * The top level subscription name. + */ + private String subscriptionName; + + /** + * List of {@link Type}s for this schema. This includes the standard schema types and other types. + */ + private final List listTypes; + + /** + * List of {@link Scalar}s that should be included in the schema. + */ + private final List listScalars; + + /** + * List of {@link Directive}s that should be included in the schema. + */ + private final List listDirectives; + + /** + * List of {@link InputType}s that should be included in the schema. + */ + private final List listInputTypes; + + /** + * List of {@link Enum}s that should be included in the schema. + */ + private final List listEnums; + + /** + * Construct the DiscoveredSchema using the defaults. + */ + public Schema() { + this(QUERY, MUTATION, SUBSCRIPTION); + } + + /** + * Construct the DiscoveredSchema using the the provided values. + * + * @param queryName name for the query type + * @param mutationName name for the mutation type + * @param subscriptionName name for the subscription type + */ + public Schema(String queryName, String mutationName, String subscriptionName) { + this.queryName = queryName; + this.mutationName = mutationName; + this.subscriptionName = subscriptionName; + this.listTypes = new ArrayList<>(); + this.listScalars = new ArrayList<>(); + this.listDirectives = new ArrayList<>(); + this.listInputTypes = new ArrayList<>(); + this.listEnums = new ArrayList<>(); + } + +// /** +// * Generates a {@link GraphQLSchema} from the current discovered schema. +// * +// * @return {@link GraphQLSchema} +// */ +// public GraphQLSchema generateGraphQLSchema() { +// SchemaParser schemaParser = new SchemaParser(); +// TypeDefinitionRegistry typeDefinitionRegistry; +// +// try { +// typeDefinitionRegistry = schemaParser.parse(generateGraphQLSchemaAsString()); +// return new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, getRuntimeWiring()); +// } +// catch (Exception e) { +// LoggingUtilities.log("Unable to parse the following generated schema:\n" + generateGraphQLSchemaAsString(), +// CacheFactory.LOG_WARN); +// throw e; +// } +// } + + + /** + * Return the GraphQL schema representation of the element. + * + * @return the GraphQL schema representation of the element. + */ + @Override + public String getSchemaAsString() { + StringBuilder sb = new StringBuilder(); + + listDirectives.forEach(d -> sb.append(d.getSchemaAsString()).append('\n')); + if (listDirectives.size() > 0) { + sb.append('\n'); + } + + sb.append("schema ").append(OPEN_CURLY).append(NEWLINE); + + // only output "query" if we have a query type + if (containsTypeWithName(queryName)) { + sb.append(SPACER).append("query: ").append(queryName).append('\n'); + } + if (containsTypeWithName(mutationName)) { + sb.append(SPACER).append("mutation: ").append(mutationName).append('\n'); + } + if (containsTypeWithName(subscriptionName)) { + sb.append(SPACER).append("subscription: ").append(subscriptionName).append('\n'); + } + + sb.append(CLOSE_CURLY).append(NEWLINE).append(NEWLINE); + + listTypes.stream() + .forEach(t -> sb.append(t.getSchemaAsString()).append("\n")); + + listInputTypes.forEach(s -> sb.append(s.getSchemaAsString()).append('\n')); + + listEnums.forEach(e -> sb.append(e.getSchemaAsString()).append('\n')); + + listScalars.forEach(s -> sb.append(s.getSchemaAsString()).append('\n')); + + return sb.toString(); + } + + /** + * Returns the {@link RuntimeWiring} for the given auto-generated schema. + * + * @return the {@link RuntimeWiring} + */ + public RuntimeWiring getRuntimeWiring() { + RuntimeWiring.Builder builder = newRuntimeWiring(); + + // Create the top level Query Runtime Wiring. + Type queryType = getTypeByName(getQueryName()); +// +// if (queryType == null) { +// throw new IllegalStateException("No type exists for query of name " + getQueryName()); +// } +// +// final TypeRuntimeWiring.Builder typeRuntimeBuilder = newTypeWiring(getQueryName()); +// +// queryType.getFieldDefinitions().forEach(fd -> { +// String cacheMapping = fd.getCacheMapping(); +// if (fd.isArrayReturnType()) { +// typeRuntimeBuilder.dataFetcher(fd.getName(), +// DataFetcherUtils.newGenericFilterDataFetcher(cacheMapping)); +// } +// else { +// typeRuntimeBuilder.dataFetcher(fd.getName(), +// DataFetcherUtils.newGenericSingleKeyDataFetcher(cacheMapping, "key")); +// } +// }); +// +// // register a type resolver for any interfaces if we have at least one +// Set setInterfaces = getTypes().stream().filter(Type::isInterface).collect(Collectors.toSet()); +// if (setInterfaces.size() > 0) { +// final Map mapTypes = new HashMap<>(); +// +// getTypes().stream().filter(t -> !t.isInterface()).forEach(t -> mapTypes.put(t.getName(), t.getValueClassName())); +// +// // generate a TypeResolver for all types that are not interfaces +// TypeResolver typeResolver = env -> { +// Object o = env.getObject(); +// for (Map.Entry entry : mapTypes.entrySet()) { +// String valueClass = entry.getValue(); +// if (valueClass != null) { +// Class typeClass = getSafeClass(entry.getValue()); +// if (typeClass != null && typeClass.isAssignableFrom(o.getClass())) { +// return (GraphQLObjectType) env.getSchema().getType(entry.getKey()); +// } +// } +// } +// return null; +// }; +// +// // add the type resolver to all interfaces and the Query object +// setInterfaces.forEach(t -> { +// builder.type(t.getName(), tr -> tr.typeResolver(typeResolver)); +// }); +// builder.type(getQueryName(), tr -> tr.typeResolver(typeResolver)); +// } +// +// // register the scalars +// getScalars().forEach(s -> builder.scalar(s.getGraphQLScalarType())); +// +// // we should now have the query runtime binding +// builder.type(typeRuntimeBuilder); +// +// // search for any types that have field definitions with DataFetchers +// getTypes().forEach(t -> { +// boolean hasDataFetchers = t.getFieldDefinitions().stream().filter(fd -> fd.getDataFetcher() != null).count() > 0; +// if (hasDataFetchers) { +// final TypeRuntimeWiring.Builder runtimeBuilder = newTypeWiring(t.getName()); +// t.getFieldDefinitions().stream() +// .filter(fd -> fd.getDataFetcher() != null) +// .forEach(fd -> runtimeBuilder.dataFetcher(fd.getName(), fd.getDataFetcher())); +// builder.type(runtimeBuilder); +// } +// }); + + return builder.build(); + } +// +// /** +// * Register the Coherence Cache {@link org.dataloader.DataLoader}s with the {@link CoherenceGraphQLContext}. +// * +// * @param context {@link CoherenceGraphQLContext} +// */ +// public void registerDataLoaders(CoherenceGraphQLContext context) { +// // Create the top level Query Runtime wiring +// Type queryType = getTypeByName(getQueryName()); +// +// if (queryType == null) { +// throw new IllegalStateException("No type exists for query of name " + getQueryName()); +// } +// +// CoherenceDataLoader coherenceDataLoader = new CoherenceDataLoader(context); +// +// queryType.getFieldDefinitions().forEach(fd -> { +// // retrieve the Type for the return type so we can get the mapped cache name +// Type returnType = getTypeByName(fd.getReturnType()); +// if (!fd.isArrayReturnType()) { +// // only register DataLoader for non-array return types +// String cacheMapping = fd.getCacheMapping(); +// +// String returnClassName = returnType == null +// ? null +// : returnType.getKeyClassName(); +// context.registerDataLoader(cacheMapping, coherenceDataLoader.newDataLoader(cacheMapping, returnClassName)); +// } +// }); +// } + + /** + * Return a {@link Type} that matches the type name. + * + * @param typeName type name to match + * @return a {@link Type} that matches the type name or null if none found + */ + public Type getTypeByName(String typeName) { + for (Type type : listTypes) { + if (type.getName().equals(typeName)) { + return type; + } + } + return null; + } + + /** + * Return a {@link Enum} that matches the enum name. + * + * @param enumName type name to match + * @return a {@link Enum} that matches the enum name or null if none found + */ + public Enum getEnumByName(String enumName) { + for (Enum enum1 : listEnums) { + if (enum1.getName().equals(enumName)) { + return enum1; + } + } + return null; + } + + /** + * Returns a {@link Scalar} which matches the provided class name. + * @param actualClazz the class name to match + * @return {@link Scalar} or null if none found + */ + public Scalar getScalarByActualClass(String actualClazz) { + for (Scalar scalar : getScalars()) { + if (scalar.getActualClass().equals(actualClazz)) { + return scalar; + } + } + return null; + } + + /** + * Returns true of the {@link Type} with the the given name is present for this {@link Schema}. + * + * @param type type name to search for + * @return true if the type name is contained within the type list + */ + public boolean containsTypeWithName(String type) { + return listTypes.stream().filter(t -> t.getName().equals(type)).count() == 1; + } + + /** + * Returns true of the {@link Scalar} with the the given name is present for this {@link Schema}. + * + * @param scalar the scalar name to search for + * @return true if the scalar name is contained within the scalar list + */ + public boolean containsScalarWithName(String scalar) { + return listScalars.stream().filter(s -> s.getName().equals(scalar)).count() == 1; + } + + /** + * Returns true of the {@link Enum} with the the given name is present for this {@link Schema}. + * + * @param enumName the enum name to search for + * @return true if the enum name is contained within the enum list + */ + public boolean containsEnumWithName(String enumName) { + return listEnums.stream().filter(e -> e.getName().equals(enumName)).count() == 1; + } + + /** + * Add a new {@link Type}. + * + * @param type new {@link Type} to add + */ + public void addType(Type type) { + listTypes.add(type); + } + + /** + * Add a new {@link Scalar}. + * + * @param scalar new {@link Scalar} to add. + */ + public void addScalar(Scalar scalar) { + listScalars.add(scalar); + } + + /** + * Add a new {@link Directive}. + * + * @param directive new {@link Directive} to add + */ + public void addDirective(Directive directive) { + listDirectives.add(directive); + } + + /** + * Add a new {@link InputType}. + * + * @param inputType new {@link InputType} to add + */ + public void addInputType(InputType inputType) { + listInputTypes.add(inputType); + } + + /** + * Add a new {@link Enum}. + * + * @param enumToAdd new {@link Enum} to add + */ + public void addEnum(Enum enumToAdd) { + listEnums.add(enumToAdd); + } + + /** + * Return the {@link List} of {@link Type}s. + * @return the {@link List} of {@link Type}s + */ + public List getTypes() { + return listTypes; + } + + /** + * Return the {@link List} of {@link Scalar}s. + * @return the {@link List} of {@link Scalar}s + */ + public List getScalars() { + return listScalars; + } + + /** + * Return the {@link List} of {@link Directive}s. + * @return the {@link List} of {@link Directive}s + */ + public List getDirectives() { + return listDirectives; + } + + /** + * Return the {@link List} of {@link InputType}s. + * @return the {@link List} of {@link InputType}s + */ + public List getInputTypes() { + return listInputTypes; + } + + /** + * Return the {@link List} of {@link Enum}s. + * @return the {@link List} of {@link Enum}s + */ + public List getEnums() { + return listEnums; + } + + /** + * Return the query name. + * @return the query name + */ + public String getQueryName() { + return queryName; + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java new file mode 100644 index 00000000000..041719848d1 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +/** + * An interface to represent a element that can generate a schema. + */ +public interface SchemaGenerator { + String NOTHING = ""; + String COMMA_NEWLINE = ",\n"; + String COMMA_SPACE = ", "; + String COMMA = ","; + String COMMENT = "#"; + char SPACER = ' '; + char NEWLINE = '\n'; + char COLON = ':'; + char EQUALS = '='; + char MANDATORY = '!'; + char QUOTE = '"'; + char OPEN_CURLY = '{'; + char CLOSE_CURLY = '}'; + char OPEN_SQUARE = '['; + char CLOSE_SQUARE = ']'; + char OPEN_PARENTHESES = '('; + char CLOSE_PARENTHESES = ')'; + + /** + * Return the GraphQL schema representation of the element. + * + * @return the GraphQL schema representation of the element. + */ + String getSchemaAsString(); +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java new file mode 100644 index 00000000000..6e969a3ca1d --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * The representation of a GraphQL Type. + */ +public class Type extends AbstractDescriptiveElement + implements SchemaGenerator { + + /** + * Name of the type. + */ + private final String name; + + /** + * Key class name. + */ + private String keyClassName; + + /** + * Value class name. + */ + private final String valueClassName; + + /** + * Indicates if the {@link Type} is an interface. + */ + private boolean isInterface; + + /** + * The interface that this {@link Type} implements. + */ + private String implementingInterface; + + /** + * {@link List} of {@link FieldDefinition}. + */ + private final List listFieldDefinitions; + + /** + * Construct a @{link Type} with the given arguments. + * + * @param name name of the Type + * @param keyClassName key class name + * @param valueClassName value class name + */ + public Type(String name, String keyClassName, String valueClassName) { + this.name = name; + this.keyClassName = keyClassName; + this.valueClassName = valueClassName; + this.listFieldDefinitions = new ArrayList<>(); + } + + /** + * Return the GraphQL schema representation of the element. + * + * @return the GraphQL schema representation of the element. + */ + @Override + public String getSchemaAsString() { + StringBuilder sb = new StringBuilder(getSchemaElementDescription()) + .append(getGraphQLName()) + .append(SPACER) + .append(getName()); + + if (implementingInterface != null) { + sb.append(" implements " + implementingInterface); + } + sb.append(SPACER).append(OPEN_CURLY).append(NEWLINE); + + listFieldDefinitions.forEach(fd -> sb.append(fd.getSchemaAsString()).append(NEWLINE)); + + // for each + sb.append(CLOSE_CURLY).append(NEWLINE); + + return sb.toString(); + } + + /** + * Generates a {@link InputType} from the current {@link Type}. + * + * @param sSuffix the suffix to add to the type as it must be unique within Type and InputType + * @return an new {@link InputType} + */ + public InputType createInputType(String sSuffix) { + InputType inputType = new InputType(getName() + sSuffix, getKeyClassName(), getValueClassName()); + getFieldDefinitions().forEach(fd -> { + fd.getArguments().clear(); + inputType.addFieldDefinition(fd); + }); + return inputType; + } + + /** + * Set the key class name for the @{link Type}. + * + * @param keyClassName the key class name for the @{link Type} + */ + public void setKeyClassName(String keyClassName) { + this.keyClassName = keyClassName; + } + + /** + * Set if the {@link Type} is an interface. + * + * @param isInterface indicates if the {@link Type} is an interface; + */ + public void setIsInterface(boolean isInterface) { + this.isInterface = isInterface; + } + + /** + * Return the name of the {@link Type}. + * + * @return the name of the {@link Type} + */ + public String getName() { + return name; + } + + /** + * Return the key class name for the @{link Type}. + * + * @return the key class name for the @{link Type}. + */ + public String getKeyClassName() { + return keyClassName; + } + + /** + * Return the value class name for the @{link Type}. + * + * @return the value class name for the @{link Type}. + */ + public String getValueClassName() { + return valueClassName; + } + + /** + * Return the {@link List} of {@link FieldDefinition}s. + * + * @return the {@link List} of {@link FieldDefinition}s + */ + public List getFieldDefinitions() { + return listFieldDefinitions; + } + + /** + * Indicates if the {@link Type} is an interface. + * + * @return if the {@link Type} is an interface. + */ + public boolean isInterface() { + return isInterface; + } + + /** + * Return the interface that this {@link Type} implements. + * + * @return the interface that this {@link Type} implements + */ + public String getImplementingInterface() { + return implementingInterface; + } + + /** + * Set the interface that this {@link Type} implements. + * + * @param implementingInterface the interface that this {@link Type} implements + */ + public void setImplementingInterface(String implementingInterface) { + this.implementingInterface = implementingInterface; + } + + /** + * Add a {@link FieldDefinition} to the {@link Type}. + * + * @param fieldDefinition {@link FieldDefinition} + */ + public void addFieldDefinition(FieldDefinition fieldDefinition) { + listFieldDefinitions.add(fieldDefinition); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Type type = (Type) o; + return isInterface == type.isInterface + && Objects.equals(name, type.name) + && Objects.equals(keyClassName, type.keyClassName) + && Objects.equals(valueClassName, type.valueClassName) + && Objects.equals(implementingInterface, type.implementingInterface) + && Objects.equals(listFieldDefinitions, type.listFieldDefinitions) + && Objects.equals(description, type.description); + } + + @Override + public int hashCode() { + int result = Objects.hash(name, + keyClassName, + valueClassName, + isInterface, + implementingInterface, + description, + listFieldDefinitions); + return result; + } + + @Override + public String toString() { + return "Type" + toStringInternal(); + } + + /** + * Internal toString() used by sub-type. + * + * @return internal toString() + */ + protected String toStringInternal() { + return "{" + + "name='" + name + '\'' + + ", keyClassName='" + keyClassName + '\'' + + ", valueClassName='" + valueClassName + '\'' + + ", isInterface='" + isInterface + '\'' + + ", description='" + description + '\'' + + ", implementingInterface='" + implementingInterface + '\'' + + ", listFieldDefinitions=" + listFieldDefinitions + '}'; + } + + /** + * Return the GraphQL name for this {@link Type}. + * + * @return the GraphQL name for this {@link Type}. + */ + protected String getGraphQLName() { + return isInterface() ? "interface" : "type"; + } + +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/package-info.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/package-info.java new file mode 100644 index 00000000000..5b00089857a --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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. + */ + +/** + * Microprofile GraphQL model classes. + */ +package io.helidon.microprofile.graphql.server.model; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/package-info.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/package-info.java new file mode 100644 index 00000000000..1c36d0f076a --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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. + */ + +/** + * Microprofile GraphQL server implementation. + */ +package io.helidon.microprofile.graphql.server; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java new file mode 100644 index 00000000000..574116da7e1 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.util; + +import java.io.File; +import java.io.FileInputStream; +import java.net.URL; +import java.util.logging.Logger; + +import org.jboss.jandex.Index; +import org.jboss.jandex.IndexReader; + +/** + * Utilities for working with Jandex indexes. + */ +public class JandexUtils { + + private static final Logger LOGGER = Logger.getLogger(JandexUtils.class.getName()); + + /** + * Default Jandex index file. + */ + protected static final String DEFAULT_INDEX_FILE = "META-INF/jandex.idx"; + + /** + * Property to override the default index file. (Normally used for functional tests) + */ + public static final String PROP_INDEX_FILE = "io.helidon.microprofile.graphql.indexfile"; + + /** + * The loaded index or null if none was found. + */ + private Index index; + + /** + * The file used to load the index. + */ + private String indexFile; + + /** + * Construct an instance of the utilities class.. + */ + public JandexUtils() { + indexFile = System.getProperty(PROP_INDEX_FILE, DEFAULT_INDEX_FILE); + } + + /** + * Load the index file. + */ + public void loadIndex() { + File file = new File(indexFile); + String actualFile; + if (file.isAbsolute()) { + actualFile = indexFile; + } else { + URL resource = JandexUtils.class.getResource(indexFile); + if (resource == null) { + return; + } + actualFile = resource.getFile(); + } + try (FileInputStream input = new FileInputStream(actualFile)) { + IndexReader reader = new IndexReader(input); + index = reader.read(); + } catch (Exception e) { + LOGGER.warning("Unable to load default Jandex index file: " + indexFile + + ": " + e.getMessage()); + } + } + + /** + * Indicates if an index was found. + * + * @return true if an index was found + */ + public boolean hasIndex() { + return index != null; + } + + /** + * Return the generated {@link Index}. + * + * @return the generated {@link Index} + */ + public Index getIndex() { + return index; + } + + /** + * The index file used to load the index. (may not exist). + * + * @return index file used to load the index + */ + public String getIndexFile() { + return indexFile; + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java new file mode 100644 index 00000000000..1cdf518cd21 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.util; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import graphql.Scalars; +import graphql.scalars.ExtendedScalars; +import io.helidon.microprofile.graphql.server.model.Enum; +import io.helidon.microprofile.graphql.server.model.Scalar; +import io.helidon.microprofile.graphql.server.model.Schema; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Input; +import org.eclipse.microprofile.graphql.Interface; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Type; + +/** + * Various utilities for generating {@link Schema}s from classes. + */ +public class SchemaUtils { + + /** + * List of supported scalars keyed by the full class name. + */ + private static final Map SUPPORTED_SCALARS = new HashMap<>() {{ + put(OffsetTime.class.getName(), new Scalar("Time", OffsetTime.class.getName(), ExtendedScalars.Time)); + put(LocalTime.class.getName(), new Scalar("LocalTime", OffsetTime.class.getName(), ExtendedScalars.Time)); + put(Object.class.getName(), new Scalar("Object", Object.class.getName(), ExtendedScalars.Object)); + put(Long.class.getName(), new Scalar("Long", Long.class.getName(), Scalars.GraphQLLong)); + put(OffsetDateTime.class.getName(), new Scalar("DateTime", OffsetDateTime.class.getName(), ExtendedScalars.DateTime)); + put(LocalDate.class.getName(), new Scalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); + put(BigDecimal.class.getName(), new Scalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); + put(BigInteger.class.getName(), new Scalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + }}; + + /** + * GraphQL Int. + */ + protected static final String INT = "Int"; + + /** + * GraphQL Float. + */ + protected static final String FLOAT = "Float"; + + /** + * GraphQL String. + */ + protected static final String STRING = "String"; + + /** + * GraphQL ID. + */ + protected static final String ID = "ID"; + + /** + * GraphQL Boolean. + */ + protected static final String BOOLEAN = "Boolean"; + + /** + * Private no-args constructor. + */ + private SchemaUtils() { + } + + /** + * Generate a {@link Schema} from a given array of classes. The classes are checked to see if they contain any of the + * annotations from the microprofile spec. + * + * @param clazzes array of classes to check + * @return a {@link Schema} + */ + public static Schema generateSchemaFromClasses(Class... clazzes) { + Schema schema = new Schema(); + + for (Class clazz : clazzes) { + // Enum + if (clazz.isAnnotationPresent(org.eclipse.microprofile.graphql.Enum.class)) { + org.eclipse.microprofile.graphql.Enum annotation = clazz + .getAnnotation(org.eclipse.microprofile.graphql.Enum.class); + + String enumName = annotation.value(); + + // only check for Name annotation if the Enum didn't have a value + if (enumName.equals("")) { + // check to see if this class has @Name annotation + Name nameAnnotation = getNameAnnotation(clazz); + if (nameAnnotation != null) { + enumName = nameAnnotation.value(); + } + } + schema.addEnum(generateEnum(clazz, enumName)); + } + + // Type, Interface, Input are all treated similarly + Type typeAnnotation = clazz.getAnnotation(Type.class); + Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); + Input inputAnnotation = clazz.getAnnotation(Input.class); + + if (typeAnnotation != null || interfaceAnnotation != null || inputAnnotation != null) { + + } + + // obtain top level query API's t + if (clazz.isAnnotationPresent(GraphQLApi.class)) { + // defines top level + + } + } + + return schema; + } + + /** + * Process a {@link Class} which has been annotated with {@link GraphQLApi}. + * + * @param schema the {@link Schema} to add the discovered information to + * @param clazz {@link Class} to introspect + * @param + */ + private static void processGraphQLApi(Schema schema, Class clazz) { + + } + + /** + * Generate an {@link Enum} from a given {@link java.lang.Enum}. + * + * @param clazz the {@link java.lang.Enum} to introspect + * @param enumName the name of the enum, if "" then use the simple class name + * @return a new {@link Enum} or null if the class provided is not an {@link java.lang.Enum} + */ + private static Enum generateEnum(Class clazz, String enumName) { + if (clazz.isEnum()) { + Enum newEnum = new Enum("".equals(enumName.strip()) ? clazz.getSimpleName() : enumName); + + Arrays.stream(clazz.getEnumConstants()) + .map(v -> v.toString()) + .forEach(newEnum::addValue); + return newEnum; + } + return null; + } + + /** + * Returns a {@link Scalar} if one matches the known list of scalars available from the {@link ExtendedScalars} helper. + * + * @param clazzName class name to check for + * @return a {@link Scalar} if one matches the known list of scalars or null if none found + */ + private static Scalar getScalar(String clazzName) { + return SUPPORTED_SCALARS.get(clazzName); + } + + /** + * Return the {@link Name} annotation if it exists on the given class. + * @param clazz {@link Class} to introspect + * @return the {@link Name} annotation or null if the class does not contain the annotation + */ + public static Name getNameAnnotation(Class clazz) { + if (clazz != null && clazz.isAnnotationPresent(Name.class)) { + return clazz.getAnnotation(Name.class); + } + return null; + } + +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/package-info.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/package-info.java new file mode 100644 index 00000000000..32439e9679e --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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. + */ + +/** + * Microprofile GraphQL utility classes. + */ +package io.helidon.microprofile.graphql.server.util; diff --git a/microprofile/graphql/server/src/main/java/module-info.java b/microprofile/graphql/server/src/main/java/module-info.java new file mode 100644 index 00000000000..bfdc794a629 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/module-info.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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. + */ + +/** + * GraphQL microprofile server module. + */ +module helidon.microprofile.graphql.server { + requires io.helidon.microprofile.cdi; + requires cdi.api; + requires java.ws.rs; + requires jandex; + requires java.logging; + requires graphql.java; + requires microprofile.graphql.api; + requires graphql.java.extended.scalars; + + exports io.helidon.microprofile.graphql.server.application; + + opens io.helidon.microprofile.graphql.server.application to weld.core.impl; +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/main/resources/META-INF/beans.xml b/microprofile/graphql/server/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..70f5bd0fe91 --- /dev/null +++ b/microprofile/graphql/server/src/main/resources/META-INF/beans.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/microprofile/graphql/server/src/main/resources/META-INF/microprofile-config.properties b/microprofile/graphql/server/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 00000000000..5b8a3ca2b2e --- /dev/null +++ b/microprofile/graphql/server/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,2 @@ +server.static.classpath.location=/web +server.static.classpath.context=/ui diff --git a/microprofile/graphql/server/src/main/resources/logging.properties b/microprofile/graphql/server/src/main/resources/logging.properties new file mode 100644 index 00000000000..81796123bfb --- /dev/null +++ b/microprofile/graphql/server/src/main/resources/logging.properties @@ -0,0 +1,23 @@ +# +# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. +# +# 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. +# + +.level=INFO + +handlers = java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n diff --git a/microprofile/graphql/server/src/main/resources/web/index.html b/microprofile/graphql/server/src/main/resources/web/index.html new file mode 100644 index 00000000000..dc15c5eb181 --- /dev/null +++ b/microprofile/graphql/server/src/main/resources/web/index.html @@ -0,0 +1,57 @@ + + + + + + + Coherence GraphiQL Interface + + + +
+ + + + + + + + \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java new file mode 100644 index 00000000000..667b6084502 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server; + +import io.helidon.microprofile.cdi.Main; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +/** + * Abstract functionality for integration tests. + */ +public abstract class AbstractGraphQLIT extends AbstractGraphQLTest { + + @BeforeAll + public static void _startup() { + Main.main(new String[0]); + } + + @AfterAll + public static void _shutdown() { + Main.shutdown(); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java new file mode 100644 index 00000000000..cf37f34d07f --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; + +import org.jboss.jandex.Index; +import org.jboss.jandex.IndexWriter; +import org.jboss.jandex.Indexer; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +/** + * Functionality for use by unit and functional tests. + * + * @author Tim Middleton 2020.02.28 + */ +public abstract class AbstractGraphQLTest { + + /** + * Create a Jandex index using the given file name and classes. + * + * @param fileName the file name to write the index to. The classes should be in the format of + * "java/lang/Thread.class" + * @param clazzes classes to index + */ + public static void createManualIndex(String fileName, String... clazzes) throws IOException { + Indexer indexer = new Indexer(); + for (String clazz : clazzes) { + InputStream stream = AbstractGraphQLTest.class.getClassLoader().getResourceAsStream(clazz); + indexer.index(stream); + stream.close(); + } + Index index = indexer.complete(); + + FileOutputStream out = new FileOutputStream(fileName); + IndexWriter writer = new IndexWriter(out); + try { + writer.write(index); + } + finally { + out.close(); + } + } + + /** + * Return a temporary file which will be used to import the Jandex index to. + * @return a new {@link File} + * @throws IOException if any IO related errors + */ + public static String getTempIndexFile() throws IOException { + return Files.createTempFile("index" + System.currentTimeMillis(), "idx").toFile().toString(); + } + + /** + * Return true if the contents of the String match the contents of the file. + * + * @param results String to compare + * @param fileName filename to compare + */ + protected static void assertResultsMatch(String results, String fileName) { + if (results == null || fileName == null) { + throw new IllegalArgumentException("sResults or sFileName cannot be null"); + } + + try { + ClassLoader classLoader = AbstractGraphQLTest.class.getClassLoader(); + URL resource = classLoader.getResource(fileName); + if (resource == null) { + throw new IllegalArgumentException("Unable to find comparison file " + fileName); + } + File file = new File(resource.getFile()); + String sFromFile = new String(Files.readAllBytes(file.toPath())); + + assertThat("Results do not match expected", results, is(sFromFile)); + } + catch (Exception e) { + throw new RuntimeException("Exception in resultsMatch sResults=[" + results + "], sFileName=" + fileName, e); + } + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/ArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/ArgumentTest.java new file mode 100644 index 00000000000..edda718f1f1 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/ArgumentTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests for {@link Argument} class. + */ +class ArgumentTest { + + @Test + public void testConstructors() { + Argument argument = new Argument("name", "Integer", true, null); + assertThat(argument.getArgumentName(), is("name")); + assertThat(argument.getArgumentType(), is("Integer")); + assertThat(argument.isMandatory(), is(true)); + assertThat(argument.getDefaultValue(), is(nullValue())); + + argument = new Argument("name2", "String", false, "Default"); + assertThat(argument.getArgumentName(), is("name2")); + assertThat(argument.getArgumentType(), is("String")); + assertThat(argument.isMandatory(), is(false)); + assertThat(argument.getDefaultValue(), is("Default")); + + argument.setArgumentType("XYZ"); + assertThat(argument.getArgumentType(), is("XYZ")); + + assertThat(argument.getDescription(), is(nullValue())); + argument.setDescription("description"); + assertThat(argument.getDescription(), is("description")); + } + + @Test + public void testSchemaGeneration() { + Argument argument = new Argument("name", "Integer", true, null); + assertThat(argument.getSchemaAsString(), is("name: Integer!")); + + argument = new Argument("name", "Integer", true, 10); + assertThat(argument.getSchemaAsString(), is("name: Integer! = 10")); + + argument = new Argument("name", "Integer", false, null); + assertThat(argument.getSchemaAsString(), is("name: Integer")); + + argument= new Argument("name", "Integer", false, 10); + assertThat(argument.getSchemaAsString(), is("name: Integer = 10")); + + argument = new Argument("name", "String", false, "The Default Value"); + assertThat(argument.getSchemaAsString(), is("name: String = \"The Default Value\"")); + + argument = new Argument("name", "String", true, "The Default Value"); + assertThat(argument.getSchemaAsString(), is("name: String! = \"The Default Value\"")); + + argument = new Argument("name", "Integer", false, 10); + argument.setDescription("Description"); + assertThat(argument.getSchemaAsString(), is("# Description\nname: Integer = 10")); + } +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/DirectiveTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/DirectiveTest.java new file mode 100644 index 00000000000..590522893ed --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/DirectiveTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import org.junit.jupiter.api.Test; + +import static graphql.introspection.Introspection.DirectiveLocation.FIELD; +import static graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION; +import static graphql.introspection.Introspection.DirectiveLocation.QUERY; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.notNullValue; + +/** + * Tests for {@link Scalar} class. + */ +class DirectiveTest { + + @Test + public void testConstructors() { + Directive directive = new Directive("auth"); + assertThat(directive.getName(), is("auth")); + assertThat(directive.getArguments().size(), is(0)); + assertThat(directive.getLocations().size(), is(0)); + + Argument arg = new Argument("name", "String", true, null); + directive.addArgument(arg); + assertThat(directive.getArguments().contains(arg), is(true)); + + directive.addLocation(FIELD_DEFINITION.name()); + assertThat(directive.getLocations().contains(FIELD_DEFINITION.name()), is(true)); + + assertThat(directive.getSchemaAsString(), is(notNullValue())); + } + + @Test + public void testDirectiveWith0Argument1Location() { + Directive directive = new Directive("directiveName"); + directive.addLocation(FIELD_DEFINITION.name()); + assertThat(directive.getSchemaAsString(), is("directive @directiveName on " + FIELD_DEFINITION.name())); + } + + @Test + public void testDirectiveWith1Argument1Location() { + Argument arg = new Argument("name", "String", true, null); + Directive directive = new Directive("directiveName"); + directive.addArgument(arg); + directive.addLocation(FIELD_DEFINITION.name()); + assertThat(directive.getSchemaAsString(), is("directive @directiveName(name: String!) on " + FIELD_DEFINITION.name())); + } + + @Test + public void testDirectiveWith2Argument1Location() { + Argument arg1 = new Argument("name", "String", true, null); + Argument arg2 = new Argument("name1", "Int", false, null); + Directive directive = new Directive("directiveName"); + directive.addArgument(arg1); + directive.addArgument(arg2); + directive.addLocation(FIELD_DEFINITION.name()); + assertThat(directive.getSchemaAsString(), + is("directive @directiveName(name: String!, name1: Int) on " + FIELD_DEFINITION.name())); + } + + @Test + public void testDirectiveWith2Argument2Location() { + Argument arg1 = new Argument("name", "String", true, null); + Argument arg2 = new Argument("name1", "Int", false, null); + Directive directive = new Directive("directiveName"); + directive.addArgument(arg1); + directive.addArgument(arg2); + directive.addLocation(FIELD_DEFINITION.name()); + directive.addLocation(FIELD.name()); + assertThat(directive.getSchemaAsString(), + is("directive @directiveName(name: String!, name1: Int) on " + FIELD_DEFINITION.name() + "|" + FIELD.name())); + } + + @Test + public void testDirectiveWith0Argument2Location() { + Directive directive = new Directive("directiveName"); + directive.addLocation(FIELD_DEFINITION.name()); + directive.addLocation(FIELD.name()); + assertThat(directive.getSchemaAsString(), + is("directive @directiveName on " + FIELD_DEFINITION.name() + "|" + FIELD.name())); + } + + @Test + + public void testDirectiveWith0Argument3Location() { + Directive directive = new Directive("directiveName"); + directive.addLocation(FIELD_DEFINITION.name()); + directive.addLocation(FIELD.name()); + directive.addLocation(QUERY.name()); + assertThat(directive.getSchemaAsString(), + is("directive @directiveName on " + FIELD_DEFINITION.name() + "|" + FIELD.name() + "|" + QUERY.name())); + } +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/EnumTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/EnumTest.java new file mode 100644 index 00000000000..bb583e31f57 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/EnumTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests for {@link Enum} class. + */ +class EnumTest extends AbstractGraphQLTest { + @Test + public void testConstructor() { + Enum enum1 = new Enum("ShirtSize"); + enum1.setDescription("This is the description of the Enum"); + enum1.addValue("S"); + enum1.addValue("M"); + enum1.addValue("L"); + enum1.addValue("XL"); + enum1.addValue("XXL"); + enum1.addValue("3XL"); + + assertThat(enum1.getDescription(), is("This is the description of the Enum")); + assertThat(enum1.getValues(), is(notNullValue())); + assertThat(enum1.getValues().size(), is(6)); + } + + @Test + public void testSchemaGenerationWithDescription() { + Enum enum1 = new Enum("ShirtSize"); + enum1.setDescription("T Shirt Size"); + enum1.addValue("Small"); + enum1.addValue("Medium"); + enum1.addValue("Large"); + enum1.addValue("XLarge"); + + assertResultsMatch(enum1.getSchemaAsString(), "test-results/enum-test-01.txt"); + } + @Test + public void testSchemaGenerationWithoutDescription() { + Enum enum1 = new Enum("ShirtSize"); + enum1.addValue("Small"); + enum1.addValue("Medium"); + enum1.addValue("Large"); + enum1.addValue("XLarge"); + + assertResultsMatch(enum1.getSchemaAsString(), "test-results/enum-test-02.txt"); + } +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/FieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/FieldDefinitionTest.java new file mode 100644 index 00000000000..3a28410adc7 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/FieldDefinitionTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import graphql.schema.DataFetcher; +import graphql.schema.StaticDataFetcher; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +/** + * Tests for {@link Argument} class. + */ +class FieldDefinitionTest { + @Test + public void testConstructors() { + FieldDefinition fieldDefinition = new FieldDefinition("name", "Integer", true, true); + assertThat(fieldDefinition.getName(), is("name")); + assertThat(fieldDefinition.getReturnType(), is("Integer")); + assertThat(fieldDefinition.getArguments(), is(notNullValue())); + assertThat(fieldDefinition.isArrayReturnType(), is(true)); + + Argument argument = new Argument("filter", "String", false, null); + fieldDefinition.addArgument(argument); + assertThat(fieldDefinition.getArguments().size(), is(1)); + assertThat(fieldDefinition.getArguments().get(0), is(argument)); + + Argument argument2 = new Argument("filter2", "Integer", true, null); + fieldDefinition.addArgument(argument2); + assertThat(fieldDefinition.getArguments().size(), is(2)); + assertThat(fieldDefinition.getArguments().contains(argument2), is(true)); + + fieldDefinition = new FieldDefinition("name2", "String", false, false); + assertThat(fieldDefinition.getName(), is("name2")); + assertThat(fieldDefinition.getReturnType(), is("String")); + assertThat(fieldDefinition.isArrayReturnType(), is(false)); + assertThat(fieldDefinition.isReturnTypeMandatory(), is(false)); + + fieldDefinition.setReturnType("BLAH"); + assertThat(fieldDefinition.getReturnType(), is("BLAH")); + } + + @Test + public void testFieldDefinitionWithNoArguments() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + assertThat(fieldDefinition.getSchemaAsString(), is("person: Person!")); + } + + @Test + public void testFieldDefinitionWithNoArgumentsAndDescription() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.setDescription("Description"); + assertThat(fieldDefinition.getSchemaAsString(), is("# Description\nperson: Person!")); + } + + @Test + public void testFieldDefinitionWithNoArgumentsAndArrayType() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", true, true); + assertThat(fieldDefinition.getSchemaAsString(), is("person: [Person]!")); + } + + @Test + public void testFieldDefinitionWith1Argument() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + assertThat(fieldDefinition.getSchemaAsString(), is("person(filter: String): Person!")); + } + + @Test + public void testFieldDefinitionWith1ArgumentAndDescription() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + Argument argument = new Argument("filter", "String", false, null); + argument.setDescription("Optional Filter"); + fieldDefinition.addArgument(argument); + assertThat(fieldDefinition.getSchemaAsString(), is("person(\n# Optional Filter\nfilter: String\n): Person!")); + } + + @Test + public void testFieldDefinitionWith1ArgumentAndArrayType() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", true, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + assertThat(fieldDefinition.getSchemaAsString(), is("person(filter: String): [Person]!")); + } + + @Test + public void testFieldDefinitionWith1MandatoryArgument() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("filter", "String", true, null)); + assertThat(fieldDefinition.getSchemaAsString(), is("person(filter: String!): Person!")); + } + + @Test + public void testFieldDefinitionWith1MandatoryArgumentAndArrayType() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", true, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + assertThat(fieldDefinition.getSchemaAsString(), is("person(filter: String): [Person]!")); + } + + @Test + public void testFieldDefinitionWithMultipleArguments() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + fieldDefinition.addArgument(new Argument("age", "Int", true, null)); + assertThat(fieldDefinition.getSchemaAsString(), is("person(filter: String, age: Int!): Person!")); + } + + @Test + public void testFieldDefinitionWithMultipleArgumentsAndDescription() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + Argument argument1 = new Argument("filter", "String", false, null); + argument1.setDescription("Optional filter"); + Argument argument2 = new Argument("age", "Int", true, null); + argument2.setDescription("Mandatory age"); + fieldDefinition.addArgument(argument1); + fieldDefinition.addArgument(argument2); + assertThat(fieldDefinition.getSchemaAsString(), + is("person(\n# Optional filter\nfilter: String,\n# Mandatory age\nage: Int!\n): Person!")); + } + + @Test + public void testFieldDefinitionWithMultipleArgumentsAndBothDescriptions() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.setDescription("Description of field definition"); + Argument argument1 = new Argument("filter", "String", false, null); + argument1.setDescription("Optional filter"); + Argument argument2 = new Argument("age", "Int", true, null); + argument2.setDescription("Mandatory age"); + fieldDefinition.addArgument(argument1); + fieldDefinition.addArgument(argument2); + assertThat(fieldDefinition.getSchemaAsString(), + is("# Description of field definition\nperson(\n# Optional filter\nfilter: String,\n# Mandatory age\nage: " + + "Int!\n): Person!")); + } + + @Test + public void testFieldDefinitionWithMultipleArgumentsWithArray() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", true, false); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + fieldDefinition.addArgument(new Argument("age", "Int", true, null)); + fieldDefinition.addArgument(new Argument("job", "String", false, null)); + assertThat(fieldDefinition.getSchemaAsString(), is("person(filter: String, age: Int!, job: String): [Person]")); + } + + @Test + @SuppressWarnings("unchecked") + public void testDataFetchers() { + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", true, false); + assertThat(fieldDefinition.getDataFetcher(), is(nullValue())); + fieldDefinition.setDataFetcher(new StaticDataFetcher("Value")); + DataFetcher dataFetcher = fieldDefinition.getDataFetcher(); + assertThat(dataFetcher, is(notNullValue())); + assertThat(dataFetcher instanceof StaticDataFetcher, is(true)); + } + +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/ScalarTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/ScalarTest.java new file mode 100644 index 00000000000..f9ef911082c --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/ScalarTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.Date; + +import graphql.scalars.ExtendedScalars; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.is; + +/** + * Tests for {@link Scalar} class. + */ +class ScalarTest { + + @Test + public void testConstructors() { + Scalar scalar = new Scalar("myName", Integer.class.getName(), ExtendedScalars.DateTime); + assertThat(scalar.getName(), is("myName")); + assertThat(scalar.getActualClass(), is(Integer.class.getName())); + assertThat(scalar.getGraphQLScalarType().equals(ExtendedScalars.DateTime), is(true)); + } + + @Test + public void testGetScalarAsString() { + assertThat(new Scalar("Test", Date.class.getName(), ExtendedScalars.DateTime).getSchemaAsString(), is("scalar Test")); + assertThat(new Scalar("Date", Date.class.getName(), ExtendedScalars.DateTime).getSchemaAsString(), is("scalar Date")); + } +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java new file mode 100644 index 00000000000..0fe3375eb28 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import graphql.scalars.ExtendedScalars; +import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.is; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Tests for {@link Schema} class. + */ +class SchemaTest extends AbstractGraphQLTest { + + @Test + public void testEmptySchemaAsString() { + Schema schema = new Schema(); + assertThat(schema.getSchemaAsString(), is("schema {\n}\n\n")); + } + + @Test + public void testTopLevelSchemaAsString() { + Schema schema = new Schema(); + schema.addType(new Type("Query", null, null)); + assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-01.txt"); + + schema.addType(new Type("Mutation", null, null)); + assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-02.txt"); + + schema.addType(new Type("Subscription", "", "")); + assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-03.txt"); + + Directive directive = new Directive("format"); + directive.addLocation(FIELD_DEFINITION.name()); + directive.addArgument(new Argument("dateFormat", "String", true, null)); + schema.addDirective(directive); + + assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-04.txt"); + } + + @Test + public void testScalars() { + Schema schema = new Schema(); + assertThat(schema.getScalars().size(), is(0)); + + Scalar scalar1 = new Scalar("Test", Date.class.getName(), ExtendedScalars.Date); + Scalar scalar2 = new Scalar("Test2", Date.class.getName(), ExtendedScalars.Date); + schema.addScalar(scalar1); + schema.addScalar(scalar2); + + assertThat(schema.getScalars().contains(scalar1), is(true)); + assertThat(schema.getScalars().contains(scalar2), is(true)); + } + + @Test + public void testDirectives() { + Schema schema = new Schema(); + assertThat(schema.getDirectives().size(), is(0)); + + Directive directive1 = new Directive("directive1"); + Directive directive2 = new Directive("directive2"); + schema.addDirective(directive1); + schema.addDirective(directive2); + assertThat(schema.getDirectives().contains(directive1), is(true)); + assertThat(schema.getDirectives().contains(directive2), is(true)); + } + + @Test + public void testInputTypes() { + Schema schema = new Schema(); + assertThat(schema.getInputTypes().size(), is(0)); + InputType type1 = new InputType("name", "keyClass", "valueClass" ); + InputType type2 = new InputType("name2", "keyClass2", "valueClass2"); + schema.addInputType(type1); + schema.addInputType(type2); + assertThat(schema.getInputTypes().size(), is(2)); + assertThat(schema.getInputTypes().contains(type1), is(true)); + assertThat(schema.getInputTypes().contains(type2), is(true)); + } + + @Test + public void tesTypes() { + Schema schema = new Schema(); + assertThat(schema.getInputTypes().size(), is(0)); + Type type1 = new Type("name", "keyClass", "valueClass"); + Type type2 = new Type("name2", "keyClass2", "valueClass2"); + schema.addType(type1); + schema.addType(type2); + assertThat(schema.getTypes().size(), is(2)); + assertThat(schema.getTypes().contains(type1), is(true)); + assertThat(schema.getTypes().contains(type2), is(true)); + } + + @Test + public void testEnums() { + Schema schema = new Schema(); + assertThat(schema.getEnums().size(), is(0)); + List listString = new ArrayList<>(); + listString.add("NEWHOPE"); + listString.add("JEDI"); + listString.add("EMPIRE"); + Enum enum1 = new Enum("Episode"); + enum1.getValues().addAll(listString); + + schema.addEnum(enum1); + assertThat(schema.getEnums().size(), is(1)); + assertThat(schema.getEnums().contains(enum1), is(true)); + + } + + @Test + @Disabled + public void testInvalidRuntimeWiring() { + Schema schema = new Schema(); + assertThrows(IllegalStateException.class, () -> schema.getRuntimeWiring()); + } +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/TypeTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/TypeTest.java new file mode 100644 index 00000000000..0a1da6d083c --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/TypeTest.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.model; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +/** + * Tests for {@link Type} and {@link InputType} classes. + */ +class TypeTest { + + @Test + public void testConstructors() { + Type type = new Type("Name", "com.oracle.test.Key", "com.oracle.test.Value"); + assertThat(type.getName(), is("Name")); + assertThat(type.getKeyClassName(), is("com.oracle.test.Key")); + assertThat(type.getValueClassName(), is("com.oracle.test.Value")); + assertThat(type.getFieldDefinitions(), is(notNullValue())); + + type.addFieldDefinition(new FieldDefinition("orderId", "Integer", false, true)); + type.addFieldDefinition(new FieldDefinition("personId", "Integer", false, true)); + type.addFieldDefinition(new FieldDefinition("personId", "Integer", false, true)); + FieldDefinition fieldDefinition = new FieldDefinition("orders", "Order", true, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + type.addFieldDefinition(fieldDefinition); + + assertThat(type.getFieldDefinitions().size(), is(4)); + assertThat(type.getFieldDefinitions().contains(fieldDefinition), is(true)); + + type.setKeyClassName("name"); + assertThat(type.getKeyClassName(), is("name")); + + assertThat(type.getImplementingInterface(), is(nullValue())); + type.setImplementingInterface("Contact"); + assertThat(type.getImplementingInterface(), is("Contact")); + } + + @Test + public void testImplementingInterface() { + Type type = new Type("Person", "java.lang.Integer", "com.oracle.Person"); + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + fieldDefinition.addArgument(new Argument("age", "Int", true, null)); + type.addFieldDefinition(fieldDefinition); + type.setImplementingInterface("Contact"); + + assertThat(type.getSchemaAsString(), is("type Person implements Contact {\n" + + "person(filter: String, age: Int!): Person!\n" + + "}\n")); + } + + @Test + public void testTypeSchemaOutput() { + Type type = new Type("Person", "java.lang.Integer", "com.oracle.Person"); + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + type.addFieldDefinition(fieldDefinition); + + assertThat(type.getFieldDefinitions().get(0).equals(fieldDefinition), is(true)); + + assertThat(type.getSchemaAsString(), is("type Person {\n" + + "person(filter: String): Person!\n" + + "}\n")); + assertThat(type.getGraphQLName(), is("type")); + } + + @Test + public void testTypeSchemaOutputWithDescription() { + Type type = new Type("Person", "java.lang.Integer", "com.oracle.Person"); + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + type.addFieldDefinition(fieldDefinition); + type.setDescription("Type Description"); + + assertThat(type.getFieldDefinitions().get(0).equals(fieldDefinition), is(true)); + + assertThat(type.getSchemaAsString(), is("# Type Description\ntype Person {\n" + + "person(filter: String): Person!\n" + + "}\n")); + assertThat(type.getGraphQLName(), is("type")); + } + + @Test + public void testTypeStringOutputWith2Arguments() { + Type type = new Type("Person", "java.lang.Integer", "com.oracle.Person"); + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + fieldDefinition.addArgument(new Argument("age", "Int", true, null)); + type.addFieldDefinition(fieldDefinition); + + assertThat(type.getSchemaAsString(), is("type Person {\n" + + "person(filter: String, age: Int!): Person!\n" + + "}\n")); + } + + @Test + public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { + Type type = new Type("Person", "java.lang.Integer", "com.oracle.Person"); + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + Argument argument1 = new Argument("filter", "String", false, null); + argument1.setDescription("Argument1 Description"); + fieldDefinition.addArgument(argument1); + + Argument argument2 = new Argument("age", "Int", true, null); + argument2.setDescription("Argument 2 Description"); + fieldDefinition.addArgument(argument2); + type.addFieldDefinition(fieldDefinition); + + assertThat(type.getSchemaAsString(), + is("type Person {\nperson(\n# Argument1 Description\nfilter: String,\n# Argument 2 Description\nage: Int!\n)" + + ": Person!\n}\n")); + } + + @Test + public void testTypeInterfaceStringOutputWith2Arguments() { + Type type = new Type("Person", "java.lang.Integer", "com.oracle.Person"); + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("filter", "String", false, null)); + fieldDefinition.addArgument(new Argument("age", "Int", true, 30)); + type.addFieldDefinition(fieldDefinition); + type.setIsInterface(true); + assertThat(type.getGraphQLName(), is("interface")); + + assertThat(type.getSchemaAsString(), is("interface Person {\n" + + "person(filter: String, age: Int! = 30): Person!\n" + + "}\n")); + } + + @Test + public void testTypeStringOutputWith2Fields() { + Type type = new Type("Person", "java.lang.Integer", "com.oracle.Person"); + + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("personId", "String", true, null)); + type.addFieldDefinition(fieldDefinition); + + FieldDefinition fieldDefinition2 = new FieldDefinition("people", "Person", true, false); + fieldDefinition2.addArgument(new Argument("filter", "String", false, null)); + fieldDefinition2.addArgument(new Argument("age", "Int", true, null)); + type.addFieldDefinition(fieldDefinition2); + + assertThat(type.getSchemaAsString(), is("type Person {\n" + + "person(personId: String!): Person!\n" + + "people(filter: String, age: Int!): [Person]\n" + + "}\n")); + } + + @Test + public void testCreatingInputTypeFromType() { + Type type = new Type("Person", "java.lang.Integer", "com.oracle.Person"); + + FieldDefinition fieldDefinition = new FieldDefinition("person", "Person", false, true); + fieldDefinition.addArgument(new Argument("personId", "String", true, null)); + type.addFieldDefinition(fieldDefinition); + + InputType inputType = type.createInputType("Input"); + assertThat(inputType, is(notNullValue())); + assertThat(inputType.getFieldDefinitions().size(), is(1)); + assertThat(inputType.getFieldDefinitions().get(0).getArguments().size(), is(0)); + assertThat(inputType.getSchemaAsString(), is("input PersonInput {\n" + + "person: Person!\n" + + "}\n")); + } + + @Test + public void testToStringInternal() { + Type type = new Type("Person", "java.lang.Integer", "com.oracle.Person"); + assertThat(type.toStringInternal(), is(notNullValue())); + assertThat(type.toString(), is(notNullValue())); + } + +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/resource/GraphQLResourceTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/resource/GraphQLResourceTest.java new file mode 100644 index 00000000000..f5f09bed65b --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/resource/GraphQLResourceTest.java @@ -0,0 +1,10 @@ +package io.helidon.microprofile.graphql.server.resource; + +import org.junit.jupiter.api.Test; + +class GraphQLResourceTest { + @Test + public void test() { + + } +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestNoEnumName.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestNoEnumName.java new file mode 100644 index 00000000000..fb482dbaa02 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestNoEnumName.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test; + +import org.eclipse.microprofile.graphql.Enum; + +/** + * Class to test enum discovery with no enum name. + */ +@Enum +public enum EnumTestNoEnumName { + S, + M, + L, + XL, + XXL, + XXXL +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithEnumName.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithEnumName.java new file mode 100644 index 00000000000..5d679d8eff2 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithEnumName.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test; + +import org.eclipse.microprofile.graphql.Enum; + +/** + * Class to test enum discovery with enum name. + */ +@Enum("TShirtSize") +public enum EnumTestWithEnumName { + S, + M, + L, + XL, + XXL, + XXXL +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithNameAndNameAnnotation.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithNameAndNameAnnotation.java new file mode 100644 index 00000000000..97ae2fdd46b --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithNameAndNameAnnotation.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test; + +import org.eclipse.microprofile.graphql.Enum; +import org.eclipse.microprofile.graphql.Name; + +/** + * Class to test enum discovery with enum name and name annotation. + * The enum name should win. + */ +@Enum("ThisShouldWin") +@Name("TShirtSize") +public enum EnumTestWithNameAndNameAnnotation { + S, + M, + L, + XL, + XXL, + XXXL +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithNameAnnotation.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithNameAnnotation.java new file mode 100644 index 00000000000..c327270b3cf --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/EnumTestWithNameAnnotation.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test; + +import org.eclipse.microprofile.graphql.Enum; +import org.eclipse.microprofile.graphql.Name; + +/** + * Class to test enum discovery with name annotation. + */ +@Enum +@Name("TShirtSize") +public enum EnumTestWithNameAnnotation { + S, + M, + L, + XL, + XXL, + XXXL +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java new file mode 100644 index 00000000000..95d336ef3a7 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.util; + +import java.io.File; +import java.io.IOException; +import java.util.Set; + +import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +/** + * Tests for {@link JandexUtils}. + */ +class JandexUtilsTest extends AbstractGraphQLTest { + + @BeforeEach + public void resetProperties() { + System.clearProperty(JandexUtils.PROP_INDEX_FILE); + } + + @Test + public void testDefaultIndexFile() { + JandexUtils utils = new JandexUtils(); + assertThat(utils.getIndexFile(), is(JandexUtils.DEFAULT_INDEX_FILE)); + assertThat(utils.hasIndex(), is(false)); + } + + @Test + public void testCustomIndexFile() { + System.setProperty(JandexUtils.PROP_INDEX_FILE, "my-index-file"); + JandexUtils utils = new JandexUtils(); + assertThat(utils.getIndexFile(), is("my-index-file")); + } + + @Test + public void testLoadingCustomIndexFile() throws IOException, ClassNotFoundException { + String indexFile = getTempIndexFile(); + try { + System.setProperty(JandexUtils.PROP_INDEX_FILE, indexFile); + createManualIndex(indexFile, + "java/lang/String.class", + "java/lang/Double.class", + "java/lang/Integer.class"); + JandexUtils utils = new JandexUtils(); + utils.loadIndex(); + + assertThat(utils.hasIndex(), is(true)); + Index index = utils.getIndex(); + Set.of(String.class, Double.class, Integer.class).forEach(c -> { + ClassInfo classByName = index.getClassByName(DotName.createSimple(c.getName())); + assertThat(classByName, is(notNullValue())); + assertThat(classByName.toString(), is(c.getName())); + }); + } finally { + if (indexFile != null) { + new File(indexFile).delete(); + } + } + } +} \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java new file mode 100644 index 00000000000..52588bc50a3 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.util; + +import java.util.Set; + +import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; +import io.helidon.microprofile.graphql.server.model.Enum; +import io.helidon.microprofile.graphql.server.model.Schema; +import io.helidon.microprofile.graphql.server.test.EnumTestNoEnumName; +import io.helidon.microprofile.graphql.server.test.EnumTestWithEnumName; +import io.helidon.microprofile.graphql.server.test.EnumTestWithNameAndNameAnnotation; +import io.helidon.microprofile.graphql.server.test.EnumTestWithNameAnnotation; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests for {@link SchemaUtilsTest}. + */ +public class SchemaUtilsTest extends AbstractGraphQLTest { + + @Test + public void testEnumGeneration() { + testEnum(EnumTestNoEnumName.class, EnumTestNoEnumName.class.getSimpleName()); + + testEnum(EnumTestWithEnumName.class, "TShirtSize"); + + testEnum(EnumTestWithNameAnnotation.class, "TShirtSize"); + + testEnum(EnumTestWithNameAndNameAnnotation.class, "ThisShouldWin"); + } + + private void testEnum(Class clazz, String expectedName) { + Schema schema = SchemaUtils.generateSchemaFromClasses(clazz); + assertThat(schema, is(notNullValue())); + + assertThat(schema.getEnums().size(), is(1)); + Enum enumResult = schema.getEnumByName(expectedName); + + assertThat(enumResult, is(notNullValue())); + assertThat(enumResult.getValues().size(), is(6)); + + Set.of("S","M","L","XL","XXL","XXXL").forEach(v -> assertThat(enumResult.getValues().contains(v), is(true))); + } + +} diff --git a/microprofile/graphql/server/src/test/resources/META-INF/beans.xml b/microprofile/graphql/server/src/test/resources/META-INF/beans.xml new file mode 100644 index 00000000000..70f5bd0fe91 --- /dev/null +++ b/microprofile/graphql/server/src/test/resources/META-INF/beans.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/microprofile/graphql/server/src/test/resources/test-results/enum-test-01.txt b/microprofile/graphql/server/src/test/resources/test-results/enum-test-01.txt new file mode 100644 index 00000000000..f28d8a80999 --- /dev/null +++ b/microprofile/graphql/server/src/test/resources/test-results/enum-test-01.txt @@ -0,0 +1,7 @@ +# T Shirt Size +enum ShirtSize { + Small + Medium + Large + XLarge +} diff --git a/microprofile/graphql/server/src/test/resources/test-results/enum-test-02.txt b/microprofile/graphql/server/src/test/resources/test-results/enum-test-02.txt new file mode 100644 index 00000000000..a024245cd18 --- /dev/null +++ b/microprofile/graphql/server/src/test/resources/test-results/enum-test-02.txt @@ -0,0 +1,6 @@ +enum ShirtSize { + Small + Medium + Large + XLarge +} diff --git a/microprofile/graphql/server/src/test/resources/test-results/schema-test-01.txt b/microprofile/graphql/server/src/test/resources/test-results/schema-test-01.txt new file mode 100644 index 00000000000..c30753203fe --- /dev/null +++ b/microprofile/graphql/server/src/test/resources/test-results/schema-test-01.txt @@ -0,0 +1,7 @@ +schema { + query: Query +} + +type Query { +} + diff --git a/microprofile/graphql/server/src/test/resources/test-results/schema-test-02.txt b/microprofile/graphql/server/src/test/resources/test-results/schema-test-02.txt new file mode 100644 index 00000000000..dedaf3c064b --- /dev/null +++ b/microprofile/graphql/server/src/test/resources/test-results/schema-test-02.txt @@ -0,0 +1,11 @@ +schema { + query: Query + mutation: Mutation +} + +type Query { +} + +type Mutation { +} + diff --git a/microprofile/graphql/server/src/test/resources/test-results/schema-test-03.txt b/microprofile/graphql/server/src/test/resources/test-results/schema-test-03.txt new file mode 100644 index 00000000000..0edbb3beb76 --- /dev/null +++ b/microprofile/graphql/server/src/test/resources/test-results/schema-test-03.txt @@ -0,0 +1,15 @@ +schema { + query: Query + mutation: Mutation + subscription: Subscription +} + +type Query { +} + +type Mutation { +} + +type Subscription { +} + diff --git a/microprofile/graphql/server/src/test/resources/test-results/schema-test-04.txt b/microprofile/graphql/server/src/test/resources/test-results/schema-test-04.txt new file mode 100644 index 00000000000..2f63117793b --- /dev/null +++ b/microprofile/graphql/server/src/test/resources/test-results/schema-test-04.txt @@ -0,0 +1,17 @@ +directive @format(dateFormat: String!) on FIELD_DEFINITION + +schema { + query: Query + mutation: Mutation + subscription: Subscription +} + +type Query { +} + +type Mutation { +} + +type Subscription { +} + diff --git a/microprofile/pom.xml b/microprofile/pom.xml index 2812197ce37..3c49527c334 100644 --- a/microprofile/pom.xml +++ b/microprofile/pom.xml @@ -53,5 +53,6 @@ reactive-streams messaging cors + graphql From 238f7fea6717bb5362b9eeefb70fea938605a04c Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 3 Mar 2020 17:00:28 +0800 Subject: [PATCH 002/178] checkstyle fixes --- .../model/AbstractDescriptiveElement.java | 4 +- .../graphql/server/model/Argument.java | 26 +++---- .../graphql/server/model/Enum.java | 31 +++++++-- .../graphql/server/model/FieldDefinition.java | 20 +++--- .../graphql/server/model/Scalar.java | 2 +- .../graphql/server/model/Schema.java | 2 +- .../graphql/server/model/SchemaGenerator.java | 67 +++++++++++++++++++ .../graphql/server/model/Type.java | 6 +- .../graphql/server/util/SchemaUtils.java | 8 +-- 9 files changed, 124 insertions(+), 42 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java index ced85144a02..5ebfed967c0 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java @@ -26,8 +26,8 @@ public class AbstractDescriptiveElement implements DescriptiveElement { /** * The description for an element. */ - protected String description; - + private String description; + @Override public void setDescription(String description) { this.description = description; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java index c1695b3c17f..d918f1250e3 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java @@ -49,6 +49,7 @@ public class Argument extends AbstractDescriptiveElement * @param argumentName name of the argument * @param argumentType type of the argument * @param isMandatory indicates if the argument is mandatory + * @param defaultValue default value for the argument */ public Argument(String argumentName, String argumentType, boolean isMandatory, Object defaultValue) { this.argumentName = argumentName; @@ -138,13 +139,12 @@ public Object getDefaultValue() { @Override public String toString() { - return "Argument{" + - "argumentName='" + argumentName + '\'' + - ", argumentType='" + argumentType + '\'' + - ", isMandatory=" + isMandatory + - ", defaultValue=" + defaultValue + - ", description='" + description + '\'' + - '}'; + return "Argument{" + + "argumentName='" + argumentName + '\'' + + ", argumentType='" + argumentType + '\'' + + ", isMandatory=" + isMandatory + + ", defaultValue=" + defaultValue + + ", description='" + getDescription() + '\'' + '}'; } @Override @@ -159,15 +159,15 @@ public boolean equals(Object o) { return false; } Argument argument = (Argument) o; - return isMandatory == argument.isMandatory && - Objects.equals(argumentName, argument.argumentName) && - Objects.equals(argumentType, argument.argumentType) && - Objects.equals(description, argument.description) && - Objects.equals(defaultValue, argument.defaultValue); + return isMandatory == argument.isMandatory + && Objects.equals(argumentName, argument.argumentName) + && Objects.equals(argumentType, argument.argumentType) + && Objects.equals(getDescription(), argument.getDescription()) + && Objects.equals(defaultValue, argument.defaultValue); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), argumentName, argumentType, isMandatory, defaultValue, description); + return Objects.hash(super.hashCode(), argumentName, argumentType, isMandatory, defaultValue, getDescription()); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java index 48a1c85c1c7..95e9bb89299 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java @@ -36,23 +36,43 @@ public class Enum extends AbstractDescriptiveElement */ private List values; + /** + * Construct an Enum. + * @param name name for the enum + */ public Enum(String name) { this.name = name; this.values = new ArrayList<>(); } + /** + * Return the name of the {@link Enum}. + * @return the name of the {@link Enum} + */ public String getName() { return name; } + /** + * Set the name of the {@link Enum}. + * @param name the name of the {@link Enum} + */ public void setName(String name) { this.name = name; } + /** + * Return the values for the {@link Enum}. + * @return the values for the {@link Enum} + */ public List getValues() { return values; } + /** + * Add a value to the {@link Enum}. + * @param value value to add + */ public void addValue(String value) { this.values.add(value); } @@ -66,7 +86,7 @@ public String getSchemaAsString() { .append(SPACER) .append(OPEN_CURLY) .append(NEWLINE); - + values.forEach(v -> sb.append(SPACER).append(v).append(NEWLINE)); return sb.append(CLOSE_CURLY).append(NEWLINE).toString(); @@ -74,10 +94,9 @@ public String getSchemaAsString() { @Override public String toString() { - return "Enum{" + - "name='" + name + '\'' + - ", values=" + values + - ", description='" + description + '\'' + - '}'; + return "Enum{" + + "name='" + name + '\'' + + ", values=" + values + + ", description='" + getDescription() + '\'' + '}'; } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java index 47222ef82bb..1dc19c3c06d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java @@ -93,8 +93,7 @@ public String getSchemaAsString() { if (isArrayReturnType()) { sb.append(SPACER).append(OPEN_SQUARE).append(getReturnType()).append(CLOSE_SQUARE); - } - else { + } else { sb.append(SPACER).append(getReturnType()); } @@ -181,15 +180,12 @@ public void addArgument(Argument argument) { @Override public String toString() { - return "FieldDefinition{" + - "name='" + name + '\'' + - ", returnType='" + returnType + '\'' + - ", isArrayReturnType=" + isArrayReturnType + - ", isReturnTypeMandatory=" + isReturnTypeMandatory + - ", listArguments=" + listArguments + - ", description='" + description + '\'' + - '}'; + return "FieldDefinition{" + + "name='" + name + '\'' + + ", returnType='" + returnType + '\'' + + ", isArrayReturnType=" + isArrayReturnType + + ", isReturnTypeMandatory=" + isReturnTypeMandatory + + ", listArguments=" + listArguments + + ", description='" + getDescription() + '\'' + '}'; } - - } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java index 19f86e73b22..623f825d1b6 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java @@ -79,7 +79,7 @@ public String getActualClass() { public GraphQLScalarType getGraphQLScalarType() { return graphQLScalarType; } - + @Override public String getSchemaAsString() { return new StringBuilder("scalar ") diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java index 2732b98328a..f65ab1d32c4 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java @@ -172,7 +172,7 @@ public String getSchemaAsString() { /** * Returns the {@link RuntimeWiring} for the given auto-generated schema. - * + * * @return the {@link RuntimeWiring} */ public RuntimeWiring getRuntimeWiring() { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java index 041719848d1..803b87f5d15 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java @@ -20,22 +20,89 @@ * An interface to represent a element that can generate a schema. */ public interface SchemaGenerator { + /** + * Empty string. + */ String NOTHING = ""; + + /** + * Comma and newline. + */ String COMMA_NEWLINE = ",\n"; + + /** + * Comma and space. + */ String COMMA_SPACE = ", "; + + /** + * Comma. + */ String COMMA = ","; + + /** + * A comment identifier. + */ String COMMENT = "#"; + + /** + * Spacer. + */ char SPACER = ' '; + + /** + * Newline. + */ char NEWLINE = '\n'; + + /** + * Colon. + */ char COLON = ':'; + + /** + * Equals. + */ char EQUALS = '='; + + /** + * Mandatory indicator. + */ char MANDATORY = '!'; + + /** + * Double quote. + */ char QUOTE = '"'; + + /** + * Open curly bracket. + */ char OPEN_CURLY = '{'; + + /** + * Close curly bracket. + */ char CLOSE_CURLY = '}'; + + /** + * Open square bracket. + */ char OPEN_SQUARE = '['; + + /** + * Close square bracket. + */ char CLOSE_SQUARE = ']'; + + /** + * Open parenthesis. + */ char OPEN_PARENTHESES = '('; + + /** + * Close parenthesis. + */ char CLOSE_PARENTHESES = ')'; /** diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java index 6e969a3ca1d..46edd9507f1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java @@ -215,7 +215,7 @@ public boolean equals(Object o) { && Objects.equals(valueClassName, type.valueClassName) && Objects.equals(implementingInterface, type.implementingInterface) && Objects.equals(listFieldDefinitions, type.listFieldDefinitions) - && Objects.equals(description, type.description); + && Objects.equals(getDescription(), type.getDescription()); } @Override @@ -225,7 +225,7 @@ public int hashCode() { valueClassName, isInterface, implementingInterface, - description, + getDescription(), listFieldDefinitions); return result; } @@ -246,7 +246,7 @@ protected String toStringInternal() { + ", keyClassName='" + keyClassName + '\'' + ", valueClassName='" + valueClassName + '\'' + ", isInterface='" + isInterface + '\'' - + ", description='" + description + '\'' + + ", description='" + getDescription() + '\'' + ", implementingInterface='" + implementingInterface + '\'' + ", listFieldDefinitions=" + listFieldDefinitions + '}'; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java index 1cdf518cd21..0c9fb0e8ac0 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -18,7 +18,6 @@ import java.math.BigDecimal; import java.math.BigInteger; - import java.time.LocalDate; import java.time.LocalTime; import java.time.OffsetDateTime; @@ -27,11 +26,12 @@ import java.util.HashMap; import java.util.Map; -import graphql.Scalars; -import graphql.scalars.ExtendedScalars; import io.helidon.microprofile.graphql.server.model.Enum; import io.helidon.microprofile.graphql.server.model.Scalar; import io.helidon.microprofile.graphql.server.model.Schema; + +import graphql.Scalars; +import graphql.scalars.ExtendedScalars; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Input; import org.eclipse.microprofile.graphql.Interface; @@ -125,7 +125,7 @@ public static Schema generateSchemaFromClasses(Class... clazzes) { if (typeAnnotation != null || interfaceAnnotation != null || inputAnnotation != null) { } - + // obtain top level query API's t if (clazz.isAnnotationPresent(GraphQLApi.class)) { // defines top level From 59f0a6ef8e9b1138ef0898cd60f9f77625fce58d Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 5 Mar 2020 17:16:48 +0800 Subject: [PATCH 003/178] progress commit --- microprofile/graphql/pom.xml | 16 + microprofile/graphql/server/pom.xml | 46 + .../server/model/DescriptiveElement.java | 10 +- .../graphql/server/model/Schema.java | 386 ++++---- .../{Argument.java => SchemaArgument.java} | 18 +- .../{Directive.java => SchemaDirective.java} | 48 +- .../model/{Enum.java => SchemaEnum.java} | 18 +- ...nition.java => SchemaFieldDefinition.java} | 48 +- .../graphql/server/model/SchemaGenerator.java | 2 +- .../{InputType.java => SchemaInputType.java} | 15 +- .../model/{Scalar.java => SchemaScalar.java} | 26 +- .../model/{Type.java => SchemaType.java} | 110 +-- .../graphql/server/util/JandexUtils.java | 46 + .../graphql/server/util/SchemaUtils.java | 935 ++++++++++++++++-- .../server/src/main/java/module-info.java | 2 + .../src/main/resources/META-INF/beans.xml | 2 +- .../graphql/server/AbstractGraphQLIT.java | 38 +- .../graphql/server/AbstractGraphQLTest.java | 86 +- .../graphql/server/GraphQLIT.java | 40 + .../graphql/server/SchemaUtilsIT.java | 149 +++ .../graphql/server/model/ArgumentTest.java | 76 -- .../graphql/server/model/DirectiveTest.java | 110 --- .../graphql/server/model/EnumTest.java | 67 -- .../server/model/FieldDefinitionTest.java | 172 ---- .../server/model/SchemaArgumentTest.java | 76 ++ .../server/model/SchemaDirectiveTest.java | 110 +++ .../graphql/server/model/SchemaEnumTest.java | 81 ++ .../model/SchemaFieldDefinitionTest.java | 173 ++++ ...{ScalarTest.java => SchemaScalarTest.java} | 18 +- .../graphql/server/model/SchemaTest.java | 66 +- .../graphql/server/model/SchemaTypeTest.java | 187 ++++ .../graphql/server/model/TypeTest.java | 191 ---- .../server/resource/GraphQLResourceTest.java | 16 + .../test/{ => enums}/EnumTestNoEnumName.java | 4 +- .../{ => enums}/EnumTestWithEnumName.java | 4 +- .../EnumTestWithNameAndNameAnnotation.java | 4 +- .../EnumTestWithNameAnnotation.java | 4 +- .../server/test/types/AbstractVehicle.java | 91 ++ .../graphql/server/test/types/Address.java | 97 ++ .../graphql/server/test/types/Car.java | 41 + .../graphql/server/test/types/InnerClass.java | 67 ++ .../test/types/InterfaceWithTypeValue.java | 28 + .../graphql/server/test/types/Level0.java | 50 + .../graphql/server/test/types/Level1.java | 47 + .../graphql/server/test/types/Level2.java | 57 ++ .../graphql/server/test/types/Motorbike.java | 42 + .../graphql/server/test/types/Person.java | 172 ++++ .../server/test/types/PersonWithName.java | 40 + .../test/types/PersonWithNameValue.java | 41 + .../server/test/types/TypeWithIdOnField.java | 48 + .../server/test/types/TypeWithIdOnMethod.java | 48 + .../types/TypeWithNameAndJsonbProperty.java | 80 ++ .../graphql/server/test/types/Vehicle.java | 36 + .../server/test/types/VehicleIncident.java | 53 + .../graphql/server/util/JandexUtilsTest.java | 2 +- .../graphql/server/util/SchemaUtilsTest.java | 212 +++- .../src/test/resources/META-INF/beans.xml | 2 +- .../resources/test-results/enum-test-01.txt | 2 +- .../resources/test-results/enum-test-03.txt | 7 + 59 files changed, 3552 insertions(+), 1111 deletions(-) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/{Argument.java => SchemaArgument.java} (86%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/{Directive.java => SchemaDirective.java} (69%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/{Enum.java => SchemaEnum.java} (83%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/{FieldDefinition.java => SchemaFieldDefinition.java} (74%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/{InputType.java => SchemaInputType.java} (73%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/{Scalar.java => SchemaScalar.java} (75%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/{Type.java => SchemaType.java} (58%) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java delete mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/ArgumentTest.java delete mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/DirectiveTest.java delete mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/EnumTest.java delete mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/FieldDefinitionTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaArgumentTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaDirectiveTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaEnumTest.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinitionTest.java rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/{ScalarTest.java => SchemaScalarTest.java} (55%) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTypeTest.java delete mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/TypeTest.java rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/{ => enums}/EnumTestNoEnumName.java (86%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/{ => enums}/EnumTestWithEnumName.java (86%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/{ => enums}/EnumTestWithNameAndNameAnnotation.java (88%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/{ => enums}/EnumTestWithNameAnnotation.java (87%) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/AbstractVehicle.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Address.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Car.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/InnerClass.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/InterfaceWithTypeValue.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level0.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level1.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level2.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Motorbike.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/PersonWithName.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/PersonWithNameValue.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithIdOnField.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithIdOnMethod.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithNameAndJsonbProperty.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Vehicle.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/VehicleIncident.java create mode 100644 microprofile/graphql/server/src/test/resources/test-results/enum-test-03.txt diff --git a/microprofile/graphql/pom.xml b/microprofile/graphql/pom.xml index ee02f4da23f..49c441c8064 100644 --- a/microprofile/graphql/pom.xml +++ b/microprofile/graphql/pom.xml @@ -1,4 +1,20 @@ + + diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 087d6e672ff..0149ce1b8a1 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -1,4 +1,20 @@ + + @@ -29,6 +45,10 @@ javax.enterprise cdi-api
+ + jakarta.json.bind + jakarta.json.bind-api + io.helidon.microprofile.server helidon-microprofile-server @@ -81,6 +101,32 @@ + + + org.apache.maven.plugins + maven-surefire-plugin + + + + org.apache.maven.plugins + maven-failsafe-plugin + + false + + **/*IT.java + + + + + verify + verify + + integration-test + verify + + + + \ No newline at end of file diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java index 35c0ade5d83..06f0229269b 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java @@ -16,10 +16,9 @@ package io.helidon.microprofile.graphql.server.model; -import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.COMMENT; import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.NEWLINE; import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.NOTHING; -import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.SPACER; +import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.QUOTE; /** * Describes an element that has a description. @@ -46,9 +45,10 @@ public interface DescriptiveElement { default String getSchemaElementDescription() { if (getDescription() != null) { - return new StringBuilder(COMMENT) - .append(SPACER) - .append(getDescription()) + return new StringBuilder() + .append(QUOTE) + .append(getDescription().replaceAll("\"", "\\\\\"")) + .append(QUOTE) .append(NEWLINE) .toString(); } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java index f65ab1d32c4..18710542158 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java @@ -17,17 +17,33 @@ package io.helidon.microprofile.graphql.server.model; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; - +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import graphql.GraphQLException; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import graphql.schema.TypeResolver; import graphql.schema.idl.RuntimeWiring; +import graphql.schema.idl.SchemaParser; +import graphql.schema.idl.TypeDefinitionRegistry; +import graphql.schema.idl.TypeRuntimeWiring; import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring; +import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring; +import static io.helidon.microprofile.graphql.server.util.SchemaUtils.getSafeClass; /** * The representation of a GraphQL Schema. */ public class Schema implements SchemaGenerator { + private static final Logger LOGGER = Logger.getLogger(Schema.class.getName()); + /** * Default query name. */ @@ -59,29 +75,29 @@ public class Schema implements SchemaGenerator { private String subscriptionName; /** - * List of {@link Type}s for this schema. This includes the standard schema types and other types. + * List of {@link SchemaType}s for this schema. This includes the standard schema types and other types. */ - private final List listTypes; + private final List listSchemaTypes; /** - * List of {@link Scalar}s that should be included in the schema. + * List of {@link SchemaScalar}s that should be included in the schema. */ - private final List listScalars; + private final List listSchemaScalars; /** - * List of {@link Directive}s that should be included in the schema. + * List of {@link SchemaDirective}s that should be included in the schema. */ - private final List listDirectives; + private final List listSchemaDirectives; /** - * List of {@link InputType}s that should be included in the schema. + * List of {@link SchemaInputType}s that should be included in the schema. */ - private final List listInputTypes; + private final List listInputTypes; /** - * List of {@link Enum}s that should be included in the schema. + * List of {@link SchemaEnum}s that should be included in the schema. */ - private final List listEnums; + private final List listSchemaEnums; /** * Construct the DiscoveredSchema using the defaults. @@ -93,41 +109,39 @@ public Schema() { /** * Construct the DiscoveredSchema using the the provided values. * - * @param queryName name for the query type - * @param mutationName name for the mutation type - * @param subscriptionName name for the subscription type + * @param queryName name for the query type + * @param mutationName name for the mutation type + * @param subscriptionName name for the subscription type */ public Schema(String queryName, String mutationName, String subscriptionName) { - this.queryName = queryName; - this.mutationName = mutationName; + this.queryName = queryName; + this.mutationName = mutationName; this.subscriptionName = subscriptionName; - this.listTypes = new ArrayList<>(); - this.listScalars = new ArrayList<>(); - this.listDirectives = new ArrayList<>(); - this.listInputTypes = new ArrayList<>(); - this.listEnums = new ArrayList<>(); + this.listSchemaTypes = new ArrayList<>(); + this.listSchemaScalars = new ArrayList<>(); + this.listSchemaDirectives = new ArrayList<>(); + this.listInputTypes = new ArrayList<>(); + this.listSchemaEnums = new ArrayList<>(); } -// /** -// * Generates a {@link GraphQLSchema} from the current discovered schema. -// * -// * @return {@link GraphQLSchema} -// */ -// public GraphQLSchema generateGraphQLSchema() { -// SchemaParser schemaParser = new SchemaParser(); -// TypeDefinitionRegistry typeDefinitionRegistry; -// -// try { -// typeDefinitionRegistry = schemaParser.parse(generateGraphQLSchemaAsString()); -// return new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, getRuntimeWiring()); -// } -// catch (Exception e) { -// LoggingUtilities.log("Unable to parse the following generated schema:\n" + generateGraphQLSchemaAsString(), -// CacheFactory.LOG_WARN); -// throw e; -// } -// } - + /** + * Generates a {@link GraphQLSchema} from the current discovered schema. + * + * @return {@link GraphQLSchema} + */ + public GraphQLSchema generateGraphQLSchema() { + SchemaParser schemaParser = new SchemaParser(); + TypeDefinitionRegistry typeDefinitionRegistry; + + try { + typeDefinitionRegistry = schemaParser.parse(getSchemaAsString()); + return new graphql.schema.idl.SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, getRuntimeWiring()); + } catch (Exception e) { + String message = "Unable to parse the generated schema"; + LOGGER.warning(message + "\n" + getSchemaAsString()); + throw new GraphQLException(message, e); + } + } /** * Return the GraphQL schema representation of the element. @@ -138,8 +152,8 @@ public Schema(String queryName, String mutationName, String subscriptionName) { public String getSchemaAsString() { StringBuilder sb = new StringBuilder(); - listDirectives.forEach(d -> sb.append(d.getSchemaAsString()).append('\n')); - if (listDirectives.size() > 0) { + listSchemaDirectives.forEach(d -> sb.append(d.getSchemaAsString()).append('\n')); + if (listSchemaDirectives.size() > 0) { sb.append('\n'); } @@ -158,14 +172,14 @@ public String getSchemaAsString() { sb.append(CLOSE_CURLY).append(NEWLINE).append(NEWLINE); - listTypes.stream() + listSchemaTypes.stream() .forEach(t -> sb.append(t.getSchemaAsString()).append("\n")); listInputTypes.forEach(s -> sb.append(s.getSchemaAsString()).append('\n')); - listEnums.forEach(e -> sb.append(e.getSchemaAsString()).append('\n')); + listSchemaEnums.forEach(e -> sb.append(e.getSchemaAsString()).append('\n')); - listScalars.forEach(s -> sb.append(s.getSchemaAsString()).append('\n')); + listSchemaScalars.forEach(s -> sb.append(s.getSchemaAsString()).append('\n')); return sb.toString(); } @@ -179,267 +193,245 @@ public RuntimeWiring getRuntimeWiring() { RuntimeWiring.Builder builder = newRuntimeWiring(); // Create the top level Query Runtime Wiring. - Type queryType = getTypeByName(getQueryName()); -// -// if (queryType == null) { -// throw new IllegalStateException("No type exists for query of name " + getQueryName()); -// } -// -// final TypeRuntimeWiring.Builder typeRuntimeBuilder = newTypeWiring(getQueryName()); + SchemaType querySchemaType = getTypeByName(getQueryName()); + + if (querySchemaType == null) { + throw new GraphQLException("No type exists for query of name " + getQueryName()); + } + + final TypeRuntimeWiring.Builder typeRuntimeBuilder = newTypeWiring(getQueryName()); // // queryType.getFieldDefinitions().forEach(fd -> { // String cacheMapping = fd.getCacheMapping(); // if (fd.isArrayReturnType()) { // typeRuntimeBuilder.dataFetcher(fd.getName(), -// DataFetcherUtils.newGenericFilterDataFetcher(cacheMapping)); -// } -// else { +// DataFetcherUtils.newGenericFilterDataFetcher(cacheMapping)); +// } else { // typeRuntimeBuilder.dataFetcher(fd.getName(), -// DataFetcherUtils.newGenericSingleKeyDataFetcher(cacheMapping, "key")); -// } -// }); -// -// // register a type resolver for any interfaces if we have at least one -// Set setInterfaces = getTypes().stream().filter(Type::isInterface).collect(Collectors.toSet()); -// if (setInterfaces.size() > 0) { -// final Map mapTypes = new HashMap<>(); -// -// getTypes().stream().filter(t -> !t.isInterface()).forEach(t -> mapTypes.put(t.getName(), t.getValueClassName())); -// -// // generate a TypeResolver for all types that are not interfaces -// TypeResolver typeResolver = env -> { -// Object o = env.getObject(); -// for (Map.Entry entry : mapTypes.entrySet()) { -// String valueClass = entry.getValue(); -// if (valueClass != null) { -// Class typeClass = getSafeClass(entry.getValue()); -// if (typeClass != null && typeClass.isAssignableFrom(o.getClass())) { -// return (GraphQLObjectType) env.getSchema().getType(entry.getKey()); -// } -// } -// } -// return null; -// }; -// -// // add the type resolver to all interfaces and the Query object -// setInterfaces.forEach(t -> { -// builder.type(t.getName(), tr -> tr.typeResolver(typeResolver)); -// }); -// builder.type(getQueryName(), tr -> tr.typeResolver(typeResolver)); -// } -// -// // register the scalars -// getScalars().forEach(s -> builder.scalar(s.getGraphQLScalarType())); -// -// // we should now have the query runtime binding -// builder.type(typeRuntimeBuilder); -// -// // search for any types that have field definitions with DataFetchers -// getTypes().forEach(t -> { -// boolean hasDataFetchers = t.getFieldDefinitions().stream().filter(fd -> fd.getDataFetcher() != null).count() > 0; -// if (hasDataFetchers) { -// final TypeRuntimeWiring.Builder runtimeBuilder = newTypeWiring(t.getName()); -// t.getFieldDefinitions().stream() -// .filter(fd -> fd.getDataFetcher() != null) -// .forEach(fd -> runtimeBuilder.dataFetcher(fd.getName(), fd.getDataFetcher())); -// builder.type(runtimeBuilder); +// DataFetcherUtils.newGenericSingleKeyDataFetcher(cacheMapping, "key")); // } // }); + // register a type resolver for any interfaces if we have at least one + Set setInterfaces = getTypes().stream().filter(SchemaType::isInterface).collect(Collectors.toSet()); + if (setInterfaces.size() > 0) { + final Map mapTypes = new HashMap<>(); + + getTypes().stream().filter(t -> !t.isInterface()).forEach(t -> mapTypes.put(t.getName(), t + .getValueClassName())); + + // generate a TypeResolver for all types that are not interfaces + TypeResolver typeResolver = env -> { + Object o = env.getObject(); + for (Map.Entry entry : mapTypes.entrySet()) { + String valueClass = entry.getValue(); + if (valueClass != null) { + Class typeClass = getSafeClass(entry.getValue()); + if (typeClass != null && typeClass.isAssignableFrom(o.getClass())) { + return (GraphQLObjectType) env.getSchema().getType(entry.getKey()); + } + } + } + return null; + }; + + // add the type resolver to all interfaces and the Query object + setInterfaces.forEach(t -> { + builder.type(t.getName(), tr -> tr.typeResolver(typeResolver)); + }); + builder.type(getQueryName(), tr -> tr.typeResolver(typeResolver)); + } + + // register the scalars + getScalars().forEach(s -> builder.scalar(s.getGraphQLScalarType())); + + // we should now have the query runtime binding + builder.type(typeRuntimeBuilder); + + // search for any types that have field definitions with DataFetchers + getTypes().forEach(t -> { + boolean hasDataFetchers = t.getFieldDefinitions().stream().filter(fd -> fd.getDataFetcher() != null) + .count() > 0; + if (hasDataFetchers) { + final TypeRuntimeWiring.Builder runtimeBuilder = newTypeWiring(t.getName()); + t.getFieldDefinitions().stream() + .filter(fd -> fd.getDataFetcher() != null) + .forEach(fd -> runtimeBuilder.dataFetcher(fd.getName(), fd.getDataFetcher())); + builder.type(runtimeBuilder); + } + }); + return builder.build(); } -// -// /** -// * Register the Coherence Cache {@link org.dataloader.DataLoader}s with the {@link CoherenceGraphQLContext}. -// * -// * @param context {@link CoherenceGraphQLContext} -// */ -// public void registerDataLoaders(CoherenceGraphQLContext context) { -// // Create the top level Query Runtime wiring -// Type queryType = getTypeByName(getQueryName()); -// -// if (queryType == null) { -// throw new IllegalStateException("No type exists for query of name " + getQueryName()); -// } -// -// CoherenceDataLoader coherenceDataLoader = new CoherenceDataLoader(context); -// -// queryType.getFieldDefinitions().forEach(fd -> { -// // retrieve the Type for the return type so we can get the mapped cache name -// Type returnType = getTypeByName(fd.getReturnType()); -// if (!fd.isArrayReturnType()) { -// // only register DataLoader for non-array return types -// String cacheMapping = fd.getCacheMapping(); -// -// String returnClassName = returnType == null -// ? null -// : returnType.getKeyClassName(); -// context.registerDataLoader(cacheMapping, coherenceDataLoader.newDataLoader(cacheMapping, returnClassName)); -// } -// }); -// } /** - * Return a {@link Type} that matches the type name. + * Return a {@link SchemaType} that matches the type name. * * @param typeName type name to match - * @return a {@link Type} that matches the type name or null if none found + * @return a {@link SchemaType} that matches the type name or null if none found */ - public Type getTypeByName(String typeName) { - for (Type type : listTypes) { - if (type.getName().equals(typeName)) { - return type; + public SchemaType getTypeByName(String typeName) { + for (SchemaType schemaType : listSchemaTypes) { + if (schemaType.getName().equals(typeName)) { + return schemaType; } } return null; } /** - * Return a {@link Enum} that matches the enum name. + * Return a {@link SchemaEnum} that matches the enum name. * * @param enumName type name to match - * @return a {@link Enum} that matches the enum name or null if none found + * @return a {@link SchemaEnum} that matches the enum name or null if none found */ - public Enum getEnumByName(String enumName) { - for (Enum enum1 : listEnums) { - if (enum1.getName().equals(enumName)) { - return enum1; + public SchemaEnum getEnumByName(String enumName) { + for (SchemaEnum schemaEnum1 : listSchemaEnums) { + if (schemaEnum1.getName().equals(enumName)) { + return schemaEnum1; } } return null; } /** - * Returns a {@link Scalar} which matches the provided class name. - * @param actualClazz the class name to match - * @return {@link Scalar} or null if none found + * Returns a {@link SchemaScalar} which matches the provided class name. + * + * @param actualClazz the class name to match + * @return {@link SchemaScalar} or null if none found */ - public Scalar getScalarByActualClass(String actualClazz) { - for (Scalar scalar : getScalars()) { - if (scalar.getActualClass().equals(actualClazz)) { - return scalar; + public SchemaScalar getScalarByActualClass(String actualClazz) { + for (SchemaScalar schemaScalar : getScalars()) { + if (schemaScalar.getActualClass().equals(actualClazz)) { + return schemaScalar; } } return null; } /** - * Returns true of the {@link Type} with the the given name is present for this {@link Schema}. + * Returns true of the {@link SchemaType} with the the given name is present for this {@link Schema}. * * @param type type name to search for * @return true if the type name is contained within the type list */ public boolean containsTypeWithName(String type) { - return listTypes.stream().filter(t -> t.getName().equals(type)).count() == 1; + return listSchemaTypes.stream().filter(t -> t.getName().equals(type)).count() == 1; } /** - * Returns true of the {@link Scalar} with the the given name is present for this {@link Schema}. + * Returns true of the {@link SchemaScalar} with the the given name is present for this {@link Schema}. * * @param scalar the scalar name to search for * @return true if the scalar name is contained within the scalar list */ public boolean containsScalarWithName(String scalar) { - return listScalars.stream().filter(s -> s.getName().equals(scalar)).count() == 1; + return listSchemaScalars.stream().filter(s -> s.getName().equals(scalar)).count() == 1; } /** - * Returns true of the {@link Enum} with the the given name is present for this {@link Schema}. + * Returns true of the {@link SchemaEnum} with the the given name is present for this {@link Schema}. * * @param enumName the enum name to search for * @return true if the enum name is contained within the enum list */ public boolean containsEnumWithName(String enumName) { - return listEnums.stream().filter(e -> e.getName().equals(enumName)).count() == 1; + return listSchemaEnums.stream().filter(e -> e.getName().equals(enumName)).count() == 1; } /** - * Add a new {@link Type}. + * Add a new {@link SchemaType}. * - * @param type new {@link Type} to add + * @param schemaType new {@link SchemaType} to add */ - public void addType(Type type) { - listTypes.add(type); + public void addType(SchemaType schemaType) { + listSchemaTypes.add(schemaType); } /** - * Add a new {@link Scalar}. + * Add a new {@link SchemaScalar}. * - * @param scalar new {@link Scalar} to add. + * @param schemaScalar new {@link SchemaScalar} to add. */ - public void addScalar(Scalar scalar) { - listScalars.add(scalar); + public void addScalar(SchemaScalar schemaScalar) { + listSchemaScalars.add(schemaScalar); } /** - * Add a new {@link Directive}. + * Add a new {@link SchemaDirective}. * - * @param directive new {@link Directive} to add + * @param schemaDirective new {@link SchemaDirective} to add */ - public void addDirective(Directive directive) { - listDirectives.add(directive); + public void addDirective(SchemaDirective schemaDirective) { + listSchemaDirectives.add(schemaDirective); } /** - * Add a new {@link InputType}. + * Add a new {@link SchemaInputType}. * - * @param inputType new {@link InputType} to add + * @param inputType new {@link SchemaInputType} to add */ - public void addInputType(InputType inputType) { + public void addInputType(SchemaInputType inputType) { listInputTypes.add(inputType); } /** - * Add a new {@link Enum}. + * Add a new {@link SchemaEnum}. * - * @param enumToAdd new {@link Enum} to add + * @param schemaEnumToAdd new {@link SchemaEnum} to add */ - public void addEnum(Enum enumToAdd) { - listEnums.add(enumToAdd); + public void addEnum(SchemaEnum schemaEnumToAdd) { + listSchemaEnums.add(schemaEnumToAdd); } /** - * Return the {@link List} of {@link Type}s. - * @return the {@link List} of {@link Type}s + * Return the {@link List} of {@link SchemaType}s. + * + * @return the {@link List} of {@link SchemaType}s */ - public List getTypes() { - return listTypes; + public List getTypes() { + return listSchemaTypes; } /** - * Return the {@link List} of {@link Scalar}s. - * @return the {@link List} of {@link Scalar}s + * Return the {@link List} of {@link SchemaScalar}s. + * + * @return the {@link List} of {@link SchemaScalar}s */ - public List getScalars() { - return listScalars; + public List getScalars() { + return listSchemaScalars; } /** - * Return the {@link List} of {@link Directive}s. - * @return the {@link List} of {@link Directive}s + * Return the {@link List} of {@link SchemaDirective}s. + * + * @return the {@link List} of {@link SchemaDirective}s */ - public List getDirectives() { - return listDirectives; + public List getDirectives() { + return listSchemaDirectives; } /** - * Return the {@link List} of {@link InputType}s. - * @return the {@link List} of {@link InputType}s + * Return the {@link List} of {@link SchemaInputType}s. + * + * @return the {@link List} of {@link SchemaInputType}s */ - public List getInputTypes() { + public List getInputTypes() { return listInputTypes; } /** - * Return the {@link List} of {@link Enum}s. - * @return the {@link List} of {@link Enum}s + * Return the {@link List} of {@link SchemaEnum}s. + * + * @return the {@link List} of {@link SchemaEnum}s */ - public List getEnums() { - return listEnums; + public List getEnums() { + return listSchemaEnums; } /** * Return the query name. + * * @return the query name */ public String getQueryName() { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java similarity index 86% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java index d918f1250e3..4c6aa22c81c 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Argument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java @@ -21,7 +21,7 @@ /** * The representation of a GraphQL Argument or Parameter. */ -public class Argument extends AbstractDescriptiveElement +public class SchemaArgument extends AbstractDescriptiveElement implements SchemaGenerator { /** * Argument name. @@ -44,14 +44,14 @@ public class Argument extends AbstractDescriptiveElement private final Object defaultValue; /** - * Construct a {@link Argument} instance. + * Construct a {@link SchemaArgument} instance. * * @param argumentName name of the argument * @param argumentType type of the argument * @param isMandatory indicates if the argument is mandatory * @param defaultValue default value for the argument */ - public Argument(String argumentName, String argumentType, boolean isMandatory, Object defaultValue) { + public SchemaArgument(String argumentName, String argumentType, boolean isMandatory, Object defaultValue) { this.argumentName = argumentName; this.argumentType = argumentType; this.isMandatory = isMandatory; @@ -158,12 +158,12 @@ public boolean equals(Object o) { if (!super.equals(o)) { return false; } - Argument argument = (Argument) o; - return isMandatory == argument.isMandatory - && Objects.equals(argumentName, argument.argumentName) - && Objects.equals(argumentType, argument.argumentType) - && Objects.equals(getDescription(), argument.getDescription()) - && Objects.equals(defaultValue, argument.defaultValue); + SchemaArgument schemaArgument = (SchemaArgument) o; + return isMandatory == schemaArgument.isMandatory + && Objects.equals(argumentName, schemaArgument.argumentName) + && Objects.equals(argumentType, schemaArgument.argumentType) + && Objects.equals(getDescription(), schemaArgument.getDescription()) + && Objects.equals(defaultValue, schemaArgument.defaultValue); } @Override diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Directive.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaDirective.java similarity index 69% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Directive.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaDirective.java index 60f5b087322..a20e9412617 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Directive.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaDirective.java @@ -27,7 +27,7 @@ /** * The representation of a GraphQL directive. */ -public class Directive implements SchemaGenerator { +public class SchemaDirective implements SchemaGenerator { /** * The name of the directive. @@ -37,7 +37,7 @@ public class Directive implements SchemaGenerator { /** * The list of arguments for the directive. */ - private final List listArguments; + private final List listSchemaArguments; /** * The locations the directive applies to. @@ -45,13 +45,13 @@ public class Directive implements SchemaGenerator { private final Set setLocations; /** - * Construct a {@link Directive}. + * Construct a {@link SchemaDirective}. * * @param name name of the directive */ - public Directive(String name) { + public SchemaDirective(String name) { this.name = name; - this.listArguments = new ArrayList<>(); + this.listSchemaArguments = new ArrayList<>(); this.setLocations = new LinkedHashSet<>(); } @@ -64,10 +64,10 @@ public Directive(String name) { public String getSchemaAsString() { StringBuilder sb = new StringBuilder("directive @" + getName()); - if (listArguments.size() > 0) { + if (listSchemaArguments.size() > 0) { sb.append("("); AtomicBoolean isFirst = new AtomicBoolean(true); - listArguments.forEach(a -> { + listSchemaArguments.forEach(a -> { String delim = isFirst.get() ? "" : ", "; isFirst.set(false); @@ -87,16 +87,16 @@ public String getSchemaAsString() { } /** - * Add an {@link Argument} to the {@link Directive}. + * Add an {@link SchemaArgument} to the {@link SchemaDirective}. * - * @param argument the {@link Argument} to add + * @param schemaArgument the {@link SchemaArgument} to add */ - public void addArgument(Argument argument) { - listArguments.add(argument); + public void addArgument(SchemaArgument schemaArgument) { + listSchemaArguments.add(schemaArgument); } /** - * Add a location to the {@link Directive}. + * Add a location to the {@link SchemaDirective}. * * @param location the location to add */ @@ -105,21 +105,21 @@ public void addLocation(String location) { } /** - * Return the name of the {@link Directive}. + * Return the name of the {@link SchemaDirective}. * - * @return the name of the {@link Directive} + * @return the name of the {@link SchemaDirective} */ public String getName() { return name; } /** - * Return the {@link List} of {@link Argument}s. + * Return the {@link List} of {@link SchemaArgument}s. * - * @return the {@link List} of {@link Argument}s + * @return the {@link List} of {@link SchemaArgument}s */ - public List getArguments() { - return listArguments; + public List getArguments() { + return listSchemaArguments; } /** @@ -139,22 +139,22 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - Directive directive = (Directive) o; - return Objects.equals(name, directive.name) - && Objects.equals(listArguments, directive.listArguments) - && Objects.equals(setLocations, directive.setLocations); + SchemaDirective schemaDirective = (SchemaDirective) o; + return Objects.equals(name, schemaDirective.name) + && Objects.equals(listSchemaArguments, schemaDirective.listSchemaArguments) + && Objects.equals(setLocations, schemaDirective.setLocations); } @Override public int hashCode() { - return Objects.hash(name, listArguments, setLocations); + return Objects.hash(name, listSchemaArguments, setLocations); } @Override public String toString() { return "Directive{" + "name='" + name + '\'' - + ", listArguments=" + listArguments + + ", listArguments=" + listSchemaArguments + ", setLocations=" + setLocations + '}'; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaEnum.java similarity index 83% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaEnum.java index 95e9bb89299..8c9d8f4b087 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Enum.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaEnum.java @@ -23,7 +23,7 @@ /** * The representation of a GraphQL Enum. */ -public class Enum extends AbstractDescriptiveElement +public class SchemaEnum extends AbstractDescriptiveElement implements SchemaGenerator { /** @@ -40,37 +40,37 @@ public class Enum extends AbstractDescriptiveElement * Construct an Enum. * @param name name for the enum */ - public Enum(String name) { + public SchemaEnum(String name) { this.name = name; this.values = new ArrayList<>(); } /** - * Return the name of the {@link Enum}. - * @return the name of the {@link Enum} + * Return the name of the {@link SchemaEnum}. + * @return the name of the {@link SchemaEnum} */ public String getName() { return name; } /** - * Set the name of the {@link Enum}. - * @param name the name of the {@link Enum} + * Set the name of the {@link SchemaEnum}. + * @param name the name of the {@link SchemaEnum} */ public void setName(String name) { this.name = name; } /** - * Return the values for the {@link Enum}. - * @return the values for the {@link Enum} + * Return the values for the {@link SchemaEnum}. + * @return the values for the {@link SchemaEnum} */ public List getValues() { return values; } /** - * Add a value to the {@link Enum}. + * Add a value to the {@link SchemaEnum}. * @param value value to add */ public void addValue(String value) { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinition.java similarity index 74% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinition.java index 1dc19c3c06d..091817d6d6a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/FieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinition.java @@ -25,7 +25,7 @@ /** * The representation of a GraphQL Field Definition. */ -public class FieldDefinition extends AbstractDescriptiveElement +public class SchemaFieldDefinition extends AbstractDescriptiveElement implements SchemaGenerator { /** * Name of the field definition. @@ -50,7 +50,7 @@ public class FieldDefinition extends AbstractDescriptiveElement /** * List of arguments. */ - private final List listArguments; + private final List listSchemaArguments; /** * {@link DataFetcher} to override default behaviour of field. @@ -58,17 +58,17 @@ public class FieldDefinition extends AbstractDescriptiveElement private DataFetcher dataFetcher; /** - * Construct a {@link FieldDefinition}. + * Construct a {@link SchemaFieldDefinition}. * * @param name field definition name * @param returnType return type * @param isArrayReturnType indicates if the return type is an array type such as a native array([]) or a List, Collection, etc * @param isReturnTypeMandatory indicates if the return type is mandatory. */ - public FieldDefinition(String name, String returnType, boolean isArrayReturnType, boolean isReturnTypeMandatory) { + public SchemaFieldDefinition(String name, String returnType, boolean isArrayReturnType, boolean isReturnTypeMandatory) { this.name = name; this.returnType = returnType; - this.listArguments = new ArrayList<>(); + this.listSchemaArguments = new ArrayList<>(); this.isArrayReturnType = isArrayReturnType; this.isReturnTypeMandatory = isReturnTypeMandatory; } @@ -78,15 +78,13 @@ public String getSchemaAsString() { StringBuilder sb = new StringBuilder(getSchemaElementDescription()) .append(getName()); - // set compact mode if no argument descriptions exist - boolean isCompact = listArguments.stream().filter(a -> a.getDescription() != null).count() == 0; - - if (listArguments.size() > 0) { - sb.append(OPEN_PARENTHESES).append(isCompact ? NOTHING : NEWLINE); - sb.append(listArguments.stream() - .map(Argument::getSchemaAsString) - .collect(Collectors.joining(isCompact ? COMMA_SPACE : COMMA_NEWLINE))); - sb.append(isCompact ? NOTHING : NEWLINE).append(CLOSE_PARENTHESES); + if (listSchemaArguments.size() > 0) { + sb.append(OPEN_PARENTHESES) + .append(NEWLINE) + .append(listSchemaArguments.stream() + .map(SchemaArgument::getSchemaAsString) + .collect(Collectors.joining(COMMA_SPACE + NEWLINE))); + sb.append(NEWLINE).append(CLOSE_PARENTHESES); } sb.append(COLON); @@ -114,11 +112,11 @@ public String getName() { } /** - * Return the {@link List} of {@link Argument}s. - * @return the {@link List} of {@link Argument}s + * Return the {@link List} of {@link SchemaArgument}s. + * @return the {@link List} of {@link SchemaArgument}s */ - public List getArguments() { - return listArguments; + public List getArguments() { + return listSchemaArguments; } /** @@ -146,8 +144,8 @@ public boolean isReturnTypeMandatory() { } /** - * Return the {@link DataFetcher} for this {@link FieldDefinition}. - * @return he {@link DataFetcher} for this {@link FieldDefinition} + * Return the {@link DataFetcher} for this {@link SchemaFieldDefinition}. + * @return he {@link DataFetcher} for this {@link SchemaFieldDefinition} */ public DataFetcher getDataFetcher() { return dataFetcher; @@ -170,12 +168,12 @@ public void setDataFetcher(DataFetcher dataFetcher) { } /** - * Add a {@link Argument} to this {@link FieldDefinition}. + * Add a {@link SchemaArgument} to this {@link SchemaFieldDefinition}. * - * @param argument {@link Argument} to add + * @param schemaArgument {@link SchemaArgument} to add */ - public void addArgument(Argument argument) { - listArguments.add(argument); + public void addArgument(SchemaArgument schemaArgument) { + listSchemaArguments.add(schemaArgument); } @Override @@ -185,7 +183,7 @@ public String toString() { + ", returnType='" + returnType + '\'' + ", isArrayReturnType=" + isArrayReturnType + ", isReturnTypeMandatory=" + isReturnTypeMandatory - + ", listArguments=" + listArguments + + ", listArguments=" + listSchemaArguments + ", description='" + getDescription() + '\'' + '}'; } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java index 803b87f5d15..16a047cbffd 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java @@ -48,7 +48,7 @@ public interface SchemaGenerator { /** * Spacer. */ - char SPACER = ' '; + String SPACER = " "; /** * Newline. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/InputType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java similarity index 73% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/InputType.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java index 011eb9b343d..d26c88bb797 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/InputType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java @@ -19,25 +19,24 @@ /** * The representation of a GraphQL Input Type. */ -public class InputType extends Type { +public class SchemaInputType extends SchemaType { /** - * Construct an {@link InputType}. + * Construct an {@link SchemaInputType}. * * @param name name for the input type - * @param keyClassName fully qualified key class name * @param valueClassName fully qualified value name */ - public InputType(String name, String keyClassName, String valueClassName) { - super(name, keyClassName, valueClassName); + public SchemaInputType(String name, String valueClassName) { + super(name, valueClassName); } @Override - public void addFieldDefinition(FieldDefinition fieldDefinition) { - if (fieldDefinition.getArguments().size() > 0) { + public void addFieldDefinition(SchemaFieldDefinition schemaFieldDefinition) { + if (schemaFieldDefinition.getArguments().size() > 0) { throw new IllegalArgumentException("input types cannot have fields with arguments"); } - super.addFieldDefinition(fieldDefinition); + super.addFieldDefinition(schemaFieldDefinition); } @Override diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaScalar.java similarity index 75% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaScalar.java index 623f825d1b6..b74873e5b10 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Scalar.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaScalar.java @@ -23,7 +23,7 @@ /** * The representation of a GraphQL Scalar. */ -public class Scalar implements SchemaGenerator { +public class SchemaScalar implements SchemaGenerator { /** * Name of the Scalar. @@ -36,36 +36,36 @@ public class Scalar implements SchemaGenerator { private String actualClass; /** - * {@link GraphQLScalarType} to convert this {@link Scalar}. + * {@link GraphQLScalarType} to convert this {@link SchemaScalar}. */ private GraphQLScalarType graphQLScalarType; /** - * Construct a {@link Scalar}. + * Construct a {@link SchemaScalar}. * * @param name name * @param actualClass actual class name - * @param graphQLScalarType {@link GraphQLScalarType} to convert this {@link Scalar}. + * @param graphQLScalarType {@link GraphQLScalarType} to convert this {@link SchemaScalar}. */ - public Scalar(String name, String actualClass, GraphQLScalarType graphQLScalarType) { + public SchemaScalar(String name, String actualClass, GraphQLScalarType graphQLScalarType) { this.name = name; this.actualClass = actualClass; this.graphQLScalarType = graphQLScalarType; } /** - * Return the name of the {@link Scalar}. + * Return the name of the {@link SchemaScalar}. * - * @return the name of the {@link Scalar} + * @return the name of the {@link SchemaScalar} */ public String getName() { return name; } /** - * Return the actual class name of the {@link Scalar}. + * Return the actual class name of the {@link SchemaScalar}. * - * @return the actual class name of the {@link Scalar} + * @return the actual class name of the {@link SchemaScalar} */ public String getActualClass() { return actualClass; @@ -95,10 +95,10 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - Scalar scalar = (Scalar) o; - return Objects.equals(name, scalar.name) - && Objects.equals(actualClass, scalar.actualClass) - && Objects.equals(graphQLScalarType, scalar.graphQLScalarType); + SchemaScalar schemaScalar = (SchemaScalar) o; + return Objects.equals(name, schemaScalar.name) + && Objects.equals(actualClass, schemaScalar.actualClass) + && Objects.equals(graphQLScalarType, schemaScalar.graphQLScalarType); } @Override diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaType.java similarity index 58% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaType.java index 46edd9507f1..d2520de21a2 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Type.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaType.java @@ -23,7 +23,7 @@ /** * The representation of a GraphQL Type. */ -public class Type extends AbstractDescriptiveElement +public class SchemaType extends AbstractDescriptiveElement implements SchemaGenerator { /** @@ -31,43 +31,36 @@ public class Type extends AbstractDescriptiveElement */ private final String name; - /** - * Key class name. - */ - private String keyClassName; - /** * Value class name. */ private final String valueClassName; /** - * Indicates if the {@link Type} is an interface. + * Indicates if the {@link SchemaType} is an interface. */ private boolean isInterface; /** - * The interface that this {@link Type} implements. + * The interface that this {@link SchemaType} implements. */ private String implementingInterface; /** - * {@link List} of {@link FieldDefinition}. + * {@link List} of {@link SchemaFieldDefinition}. */ - private final List listFieldDefinitions; + private final List listSchemaFieldDefinitions; /** * Construct a @{link Type} with the given arguments. * * @param name name of the Type - * @param keyClassName key class name * @param valueClassName value class name */ - public Type(String name, String keyClassName, String valueClassName) { + public SchemaType(String name, String valueClassName) { this.name = name; - this.keyClassName = keyClassName; this.valueClassName = valueClassName; - this.listFieldDefinitions = new ArrayList<>(); + this.listSchemaFieldDefinitions = new ArrayList<>(); } /** @@ -87,7 +80,7 @@ public String getSchemaAsString() { } sb.append(SPACER).append(OPEN_CURLY).append(NEWLINE); - listFieldDefinitions.forEach(fd -> sb.append(fd.getSchemaAsString()).append(NEWLINE)); + listSchemaFieldDefinitions.forEach(fd -> sb.append(fd.getSchemaAsString()).append(NEWLINE)); // for each sb.append(CLOSE_CURLY).append(NEWLINE); @@ -96,13 +89,13 @@ public String getSchemaAsString() { } /** - * Generates a {@link InputType} from the current {@link Type}. + * Generates a {@link SchemaInputType} from the current {@link SchemaType}. * * @param sSuffix the suffix to add to the type as it must be unique within Type and InputType - * @return an new {@link InputType} + * @return an new {@link SchemaInputType} */ - public InputType createInputType(String sSuffix) { - InputType inputType = new InputType(getName() + sSuffix, getKeyClassName(), getValueClassName()); + public SchemaInputType createInputType(String sSuffix) { + SchemaInputType inputType = new SchemaInputType(getName() + sSuffix, getValueClassName()); getFieldDefinitions().forEach(fd -> { fd.getArguments().clear(); inputType.addFieldDefinition(fd); @@ -111,41 +104,23 @@ public InputType createInputType(String sSuffix) { } /** - * Set the key class name for the @{link Type}. + * Set if the {@link SchemaType} is an interface. * - * @param keyClassName the key class name for the @{link Type} - */ - public void setKeyClassName(String keyClassName) { - this.keyClassName = keyClassName; - } - - /** - * Set if the {@link Type} is an interface. - * - * @param isInterface indicates if the {@link Type} is an interface; + * @param isInterface indicates if the {@link SchemaType} is an interface; */ public void setIsInterface(boolean isInterface) { this.isInterface = isInterface; } /** - * Return the name of the {@link Type}. + * Return the name of the {@link SchemaType}. * - * @return the name of the {@link Type} + * @return the name of the {@link SchemaType} */ public String getName() { return name; } - /** - * Return the key class name for the @{link Type}. - * - * @return the key class name for the @{link Type}. - */ - public String getKeyClassName() { - return keyClassName; - } - /** * Return the value class name for the @{link Type}. * @@ -156,48 +131,48 @@ public String getValueClassName() { } /** - * Return the {@link List} of {@link FieldDefinition}s. + * Return the {@link List} of {@link SchemaFieldDefinition}s. * - * @return the {@link List} of {@link FieldDefinition}s + * @return the {@link List} of {@link SchemaFieldDefinition}s */ - public List getFieldDefinitions() { - return listFieldDefinitions; + public List getFieldDefinitions() { + return listSchemaFieldDefinitions; } /** - * Indicates if the {@link Type} is an interface. + * Indicates if the {@link SchemaType} is an interface. * - * @return if the {@link Type} is an interface. + * @return if the {@link SchemaType} is an interface. */ public boolean isInterface() { return isInterface; } /** - * Return the interface that this {@link Type} implements. + * Return the interface that this {@link SchemaType} implements. * - * @return the interface that this {@link Type} implements + * @return the interface that this {@link SchemaType} implements */ public String getImplementingInterface() { return implementingInterface; } /** - * Set the interface that this {@link Type} implements. + * Set the interface that this {@link SchemaType} implements. * - * @param implementingInterface the interface that this {@link Type} implements + * @param implementingInterface the interface that this {@link SchemaType} implements */ public void setImplementingInterface(String implementingInterface) { this.implementingInterface = implementingInterface; } /** - * Add a {@link FieldDefinition} to the {@link Type}. + * Add a {@link SchemaFieldDefinition} to the {@link SchemaType}. * - * @param fieldDefinition {@link FieldDefinition} + * @param schemaFieldDefinition {@link SchemaFieldDefinition} */ - public void addFieldDefinition(FieldDefinition fieldDefinition) { - listFieldDefinitions.add(fieldDefinition); + public void addFieldDefinition(SchemaFieldDefinition schemaFieldDefinition) { + listSchemaFieldDefinitions.add(schemaFieldDefinition); } @Override @@ -208,25 +183,23 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - Type type = (Type) o; - return isInterface == type.isInterface - && Objects.equals(name, type.name) - && Objects.equals(keyClassName, type.keyClassName) - && Objects.equals(valueClassName, type.valueClassName) - && Objects.equals(implementingInterface, type.implementingInterface) - && Objects.equals(listFieldDefinitions, type.listFieldDefinitions) - && Objects.equals(getDescription(), type.getDescription()); + SchemaType schemaType = (SchemaType) o; + return isInterface == schemaType.isInterface + && Objects.equals(name, schemaType.name) + && Objects.equals(valueClassName, schemaType.valueClassName) + && Objects.equals(implementingInterface, schemaType.implementingInterface) + && Objects.equals(listSchemaFieldDefinitions, schemaType.listSchemaFieldDefinitions) + && Objects.equals(getDescription(), schemaType.getDescription()); } @Override public int hashCode() { int result = Objects.hash(name, - keyClassName, valueClassName, isInterface, implementingInterface, getDescription(), - listFieldDefinitions); + listSchemaFieldDefinitions); return result; } @@ -243,18 +216,17 @@ public String toString() { protected String toStringInternal() { return "{" + "name='" + name + '\'' - + ", keyClassName='" + keyClassName + '\'' + ", valueClassName='" + valueClassName + '\'' + ", isInterface='" + isInterface + '\'' + ", description='" + getDescription() + '\'' + ", implementingInterface='" + implementingInterface + '\'' - + ", listFieldDefinitions=" + listFieldDefinitions + '}'; + + ", listFieldDefinitions=" + listSchemaFieldDefinitions + '}'; } /** - * Return the GraphQL name for this {@link Type}. + * Return the GraphQL name for this {@link SchemaType}. * - * @return the GraphQL name for this {@link Type}. + * @return the GraphQL name for this {@link SchemaType}. */ protected String getGraphQLName() { return isInterface() ? "interface" : "type"; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java index 574116da7e1..5cae33a589a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java @@ -18,9 +18,15 @@ import java.io.File; import java.io.FileInputStream; +import java.lang.reflect.Modifier; import java.net.URL; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import java.util.logging.Logger; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; import org.jboss.jandex.Index; import org.jboss.jandex.IndexReader; @@ -82,6 +88,46 @@ public void loadIndex() { } } + /** + * Return a {@link Collection} of {@link Class}es which are implementors of a given class/interface. + * + * @param clazz {@link Class} to check for implementors + * @param includeAbstract indicates if abstract classes should be included + * @return a {@link Collection} of {@link Class}es + */ + public Collection> getKnownImplementors(String clazz, boolean includeAbstract) { + if (index == null) { + return null; + } + Set allKnownImplementors = index.getAllKnownImplementors(DotName.createSimple(clazz)); + Set> setResults = new HashSet<>(); + + for (ClassInfo classInfo : allKnownImplementors) { + Class clazzName = null; + try { + clazzName = Class.forName(classInfo.toString()); + } catch (ClassNotFoundException e) { + // ignore as class should exist + } + if (includeAbstract || !Modifier.isAbstract(clazzName.getModifiers())) { + setResults.add(clazzName); + } + } + return setResults; + } + + /** + * Return a {@link Collection} of {@link Class}es which are implementors of a given class/interface + * and are not abstract. + * + * @param clazz {@link Class} to check for implementors + * + * @return a {@link Collection} of {@link Class}es + */ + public Collection> getKnownImplementors(String clazz) { + return getKnownImplementors(clazz, false); + } + /** * Indicates if an index was found. * diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java index 0c9fb0e8ac0..1d18e5fe32a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -16,45 +16,163 @@ package io.helidon.microprofile.graphql.server.util; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import javax.json.bind.annotation.JsonbProperty; -import io.helidon.microprofile.graphql.server.model.Enum; -import io.helidon.microprofile.graphql.server.model.Scalar; import io.helidon.microprofile.graphql.server.model.Schema; +import io.helidon.microprofile.graphql.server.model.SchemaEnum; +import io.helidon.microprofile.graphql.server.model.SchemaFieldDefinition; +import io.helidon.microprofile.graphql.server.model.SchemaInputType; +import io.helidon.microprofile.graphql.server.model.SchemaScalar; +import io.helidon.microprofile.graphql.server.model.SchemaType; +import graphql.GraphQLException; import graphql.Scalars; import graphql.scalars.ExtendedScalars; +import graphql.schema.DataFetcher; +import org.eclipse.microprofile.graphql.Description; +import org.eclipse.microprofile.graphql.Enum; import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Id; import org.eclipse.microprofile.graphql.Input; import org.eclipse.microprofile.graphql.Interface; import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.Type; +import static io.helidon.microprofile.graphql.server.util.SchemaUtils.DiscoveredMethod.READ; + /** * Various utilities for generating {@link Schema}s from classes. */ public class SchemaUtils { + /** + * Logger. + */ + private static final Logger LOGGER = Logger.getLogger(SchemaUtils.class.getName()); + /** * List of supported scalars keyed by the full class name. */ - private static final Map SUPPORTED_SCALARS = new HashMap<>() {{ - put(OffsetTime.class.getName(), new Scalar("Time", OffsetTime.class.getName(), ExtendedScalars.Time)); - put(LocalTime.class.getName(), new Scalar("LocalTime", OffsetTime.class.getName(), ExtendedScalars.Time)); - put(Object.class.getName(), new Scalar("Object", Object.class.getName(), ExtendedScalars.Object)); - put(Long.class.getName(), new Scalar("Long", Long.class.getName(), Scalars.GraphQLLong)); - put(OffsetDateTime.class.getName(), new Scalar("DateTime", OffsetDateTime.class.getName(), ExtendedScalars.DateTime)); - put(LocalDate.class.getName(), new Scalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); - put(BigDecimal.class.getName(), new Scalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); - put(BigInteger.class.getName(), new Scalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + private static final Map SUPPORTED_SCALARS = new HashMap<>() {{ + put(OffsetTime.class.getName(), new SchemaScalar("Time", OffsetTime.class.getName(), ExtendedScalars.Time)); + put(LocalTime.class.getName(), new SchemaScalar("LocalTime", OffsetTime.class.getName(), ExtendedScalars.Time)); + put(Object.class.getName(), new SchemaScalar("Object", Object.class.getName(), ExtendedScalars.Object)); + put(Long.class.getName(), new SchemaScalar("Long", Long.class.getName(), Scalars.GraphQLLong)); + put(OffsetDateTime.class.getName(), + new SchemaScalar("DateTime", OffsetDateTime.class.getName(), ExtendedScalars.DateTime)); + put(LocalDate.class.getName(), new SchemaScalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); + put(BigDecimal.class.getName(), new SchemaScalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); + put(BigInteger.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + }}; + + /** + * List of types the should map to a GraphQL Float. + */ + private static final List FLOAT_LIST = new ArrayList<>() {{ + add("double"); + add("Double"); + add("java.lang.Double"); + add("float"); + add("Float"); + add("java.lang.Float"); + add("java.lang.Number"); + }}; + + private static final List LONG_LIST = new ArrayList<>() {{ + add("java.lang.Long"); + add("long"); + add("Long"); + }}; + + private static final List BOOLEAN_LIST = new ArrayList<>() {{ + add("boolean"); + add(Boolean.class.getName()); + }}; + + /** + * List of types that should map to a GraphQL Int. + */ + private static final List INTEGER_LIST = new ArrayList<>() {{ + add("short"); + add("int"); + add("Short"); + add("Integer"); + add("java.lang.Integer"); + add("java.lang.Short"); + add("java.lang.Byte"); + add("byte"); + }}; + + /** + * List of types that should map to a GraphQL String. + */ + private static final List STRING_LIST = new ArrayList<>() {{ + add("java.lang.String"); + add("java.lang.Character"); + add("char"); + }}; + + /** + * List of all Java primitives. + */ + private static final List JAVA_PRIMITIVE_TYPES = new ArrayList<>() {{ + add("byte"); + add("short"); + add("int"); + add("long"); + add("float"); + add("double"); + add("boolean"); + add("char"); + }}; + + /** + * List of all Java primitive objects. + */ + private static final List JAVA_PRIMITIVE_OBJECTS = new ArrayList<>() {{ + addAll(STRING_LIST); + addAll(FLOAT_LIST); + addAll(INTEGER_LIST); + addAll(LONG_LIST); + }}; + + /** + * List of array primitive types and their array mapping. See https://docs.oracle.com/javase/6/docs/api/java/lang/Class + * .html#getName%28%29 + */ + private static final Map PRIMITIVE_ARRAY_MAP = new HashMap<>() {{ + put("[Z", "boolean"); + put("[B", "byte"); + put("[C", "char"); + put("[D", "double"); + put("[F", "float"); + put("[I", "int"); + put("[J", "long"); + put("[S", "short"); }}; /** @@ -83,9 +201,38 @@ public class SchemaUtils { protected static final String BOOLEAN = "Boolean"; /** - * Private no-args constructor. + * {@link JandexUtils} instance to hold indexes. + */ + private JandexUtils jandexUtils; + + /** + * Construct a {@link SchemaUtils} instance. */ - private SchemaUtils() { + public SchemaUtils() { + jandexUtils = new JandexUtils(); + jandexUtils.loadIndex(); + if (!jandexUtils.hasIndex()) { + String message = "Unable to find or load jandex index file: " + + jandexUtils.getIndexFile() + ".\nEnsure you are using the " + + "jandex-maven-plugin when you are building your application"; + LOGGER.warning(message); + } + } + + /** + * Generate a {@link Schema} by scanning all discovered classes via the Jandex plugin. + * + * @return a {@link Schema} + */ + public Schema generateSchema() throws IntrospectionException, ClassNotFoundException { + + List> listClasses = jandexUtils.getIndex() + .getKnownClasses() + .stream() + .map(ci -> getSafeClass(ci.toString())) + .collect(Collectors.toList()); + + return generateSchemaFromClasses(listClasses.toArray(new Class[0])); } /** @@ -95,47 +242,223 @@ private SchemaUtils() { * @param clazzes array of classes to check * @return a {@link Schema} */ - public static Schema generateSchemaFromClasses(Class... clazzes) { + protected Schema generateSchemaFromClasses(Class... clazzes) throws IntrospectionException, ClassNotFoundException { Schema schema = new Schema(); + List listSchemaTypes = new ArrayList<>(); + List> listGraphQLApis = new ArrayList<>(); + Set setUnresolvedTypes = new HashSet<>(); for (Class clazz : clazzes) { - // Enum - if (clazz.isAnnotationPresent(org.eclipse.microprofile.graphql.Enum.class)) { - org.eclipse.microprofile.graphql.Enum annotation = clazz - .getAnnotation(org.eclipse.microprofile.graphql.Enum.class); - - String enumName = annotation.value(); - - // only check for Name annotation if the Enum didn't have a value - if (enumName.equals("")) { - // check to see if this class has @Name annotation - Name nameAnnotation = getNameAnnotation(clazz); - if (nameAnnotation != null) { - enumName = nameAnnotation.value(); + // only include interfaces and concrete classes/enums + if (clazz.isInterface() || (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()))) { + // Discover Enum via annotation + if (clazz.isAnnotationPresent(org.eclipse.microprofile.graphql.Enum.class)) { + schema.addEnum(generateEnum(clazz)); + continue; + } + + // + // Type, Interface, Input are all treated similarly + // + Type typeAnnotation = clazz.getAnnotation(Type.class); + Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); + Input inputAnnotation = clazz.getAnnotation(Input.class); + + if (typeAnnotation != null || interfaceAnnotation != null) { + // interface or type + if (interfaceAnnotation != null && !clazz.isInterface()) { + throw new GraphQLException("Class " + clazz.getName() + " has been annotated with" + + " @Interface but is not one"); + } + + // assuming value for annotation overrides @Name + String typeName = getTypeName(clazz); + SchemaType type = new SchemaType(typeName.isBlank() ? clazz.getSimpleName() : typeName, clazz.getName()); + type.setIsInterface(clazz.isInterface()); + Description descriptionAnnotation = clazz.getAnnotation(Description.class); + if (descriptionAnnotation != null) { + type.setDescription(descriptionAnnotation.value()); + } + + listSchemaTypes.add(type); + + if (type.isInterface()) { + // is an interface so check for any implementors and add them to + jandexUtils.getKnownImplementors(clazz.getName()).forEach(c -> setUnresolvedTypes.add(c.getName())); + } +// else { +// // Automatically create an input type for this type if it is not an interface +// listSchemaTypes.add(type.createInputType("Input")); +// +// // check if the class implements an interface and add to unresolved classes if it is annotated +// } + } else if (inputAnnotation != null) { + // InputType + String inputTypeName = inputAnnotation.value(); + listSchemaTypes.add(new SchemaInputType(inputTypeName.isBlank() ? clazz.getSimpleName() : inputTypeName, + clazz.getName())); + } + + // + // obtain top level query API's + // + if (clazz.isAnnotationPresent(GraphQLApi.class)) { + // defines top level + listGraphQLApis.add(clazz); } - schema.addEnum(generateEnum(clazz, enumName)); } + } - // Type, Interface, Input are all treated similarly - Type typeAnnotation = clazz.getAnnotation(Type.class); - Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); - Input inputAnnotation = clazz.getAnnotation(Input.class); + // process all the types + addTypesToSchema(schema, listSchemaTypes, setUnresolvedTypes); - if (typeAnnotation != null || interfaceAnnotation != null || inputAnnotation != null) { + // create any types that are yet unresolved. e.g. an Order that contains OrderLine objects + // we must also ensure if the unresolved type contains another unresolved type then we process it + while (setUnresolvedTypes.size() > 0) { + String returnType = setUnresolvedTypes.iterator().next(); - } + setUnresolvedTypes.remove(returnType); + try { + String simpleName = getSimpleName(returnType); + //String simpleName = getSimpleName(returnType); + + SchemaScalar scalar = getScalar(returnType); + if (scalar != null) { + if (!schema.containsScalarWithName(scalar.getName())) { + schema.addScalar(scalar); + // update the return type with the scalar + updateLongTypes(schema, returnType, scalar.getName()); + } + } else if (isEnumClass(returnType)) { + SchemaEnum newEnum = generateEnum(Class.forName(returnType)); + if (!schema.containsEnumWithName(simpleName)) { + schema.addEnum(newEnum); + } + updateLongTypes(schema, returnType, newEnum.getName()); + } else { + // we will either know this type already or need to add it + boolean fExists = schema.getTypes().stream() + .filter(t -> t.getName().equals(simpleName)).count() > 0; + if (!fExists) { + SchemaType newType = generateType(returnType, setUnresolvedTypes); - // obtain top level query API's t - if (clazz.isAnnotationPresent(GraphQLApi.class)) { - // defines top level + // update any return types to the discovered scalars + checkScalars(schema, newType); + schema.addType(newType); + // schema.addType(newType.createInputType("Input")); + } + // need to update any FieldDefinitions that contained the original "long" type of c + updateLongTypes(schema, returnType, simpleName); + } + } catch (Exception e) { + throw new RuntimeException("Cannot get GraphQL type for " + returnType, e); } } + // look though all of interface type and see if any of the known types implement + // the interface and if so, add the interface to the type + schema.getTypes().stream().filter(SchemaType::isInterface).forEach(it -> { + schema.getTypes().stream().filter(t -> !t.isInterface()).forEach(type -> { + Class interfaceClass = getSafeClass(it.getValueClassName()); + Class typeClass = getSafeClass(type.getValueClassName()); + if (interfaceClass != null + && typeClass != null + && interfaceClass.isAssignableFrom(typeClass)) { + type.setImplementingInterface(it.getName()); + } + }); + }); + + SchemaType rootQueryType = new SchemaType(schema.getQueryName(), null); + + // process the @GraphQLApi annotated classes + if (listGraphQLApis.size() == 0) { + LOGGER.warning("Unable to find any classes with @GraphQLApi annotation." + + "Unable to build schema"); + } else { + // add in "Query" object to for searching for all Objects and individual object + + } + schema.addType(rootQueryType); + return schema; } + /** + * Generate a {@link SchemaType} from a given class. + * + * @param realReturnType the class to generate type from + * @param setUnresolvedTypes the set of unresolved types to add to if we find one + * @return a {@link SchemaType} + * @throws IntrospectionException + * @throws ClassNotFoundException + */ + private SchemaType generateType(String realReturnType, Set setUnresolvedTypes) + throws IntrospectionException, ClassNotFoundException { + + String simpleName = getSimpleName(realReturnType); + SchemaType type = new SchemaType(simpleName, realReturnType); + Description descriptionAnnotation = Class.forName(realReturnType).getAnnotation(Description.class); + if (descriptionAnnotation != null && !descriptionAnnotation.value().isBlank()) { + type.setDescription(descriptionAnnotation.value()); + } + + for (Map.Entry entry : retrieveBeanMethods(Class.forName(realReturnType)).entrySet()) { + DiscoveredMethod discoveredMethod = entry.getValue(); + String valueTypeName = discoveredMethod.getReturnType(); + SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod); + type.addFieldDefinition(fd); + + if (setUnresolvedTypes != null && discoveredMethod.getReturnType().equals(fd.getReturnType())) { + // value class was unchanged meaning we need to resolve + setUnresolvedTypes.add(valueTypeName); + } + } + return type; + } + + /** + * Add all collected types to the {@link Schema}. + * + * @param schema the {@link Schema} to add to + * @param listTypes the {@link List} of {@link Type}s previously obtained. + * @param setUnresolvedTypes the {@link Set} of unresolved types to update + * @throws IntrospectionException + * @throws ClassNotFoundException + */ + private void addTypesToSchema(Schema schema, List listTypes, Set setUnresolvedTypes) + throws IntrospectionException, ClassNotFoundException { + + for (SchemaType t : listTypes) { + String valueClassName = t.getValueClassName(); + retrieveBeanMethods(Class.forName(valueClassName)).forEach((k, v) -> { + + SchemaFieldDefinition fd = newFieldDefinition(v); + t.addFieldDefinition(fd); + + checkScalars(schema, t); + + String returnType = v.getReturnType(); + // check to see if this is a known type + if (returnType.equals(fd.getReturnType()) && !setUnresolvedTypes.contains(returnType)) { + // value class was unchanged meaning we need to resolve + setUnresolvedTypes.add(returnType); + } + }); + + // check if this Type is an interface then obtain all concrete classes that implement the type + // and add them to the set of unresolved types + if (t.isInterface()) { + Collection> setConcreteClasses = jandexUtils.getKnownImplementors(valueClassName); + setConcreteClasses.forEach(c -> setUnresolvedTypes.add(c.getName())); + } + + schema.addType(t); + } + } + /** * Process a {@link Class} which has been annotated with {@link GraphQLApi}. * @@ -143,49 +466,551 @@ public static Schema generateSchemaFromClasses(Class... clazzes) { * @param clazz {@link Class} to introspect * @param */ - private static void processGraphQLApi(Schema schema, Class clazz) { + private void processGraphQLApi(Schema schema, Class clazz) { } /** - * Generate an {@link Enum} from a given {@link java.lang.Enum}. + * Generate an {@link SchemaEnum} from a given {@link java.lang.Enum}. * - * @param clazz the {@link java.lang.Enum} to introspect - * @param enumName the name of the enum, if "" then use the simple class name - * @return a new {@link Enum} or null if the class provided is not an {@link java.lang.Enum} + * @param clazz the {@link java.lang.Enum} to introspect + * @return a new {@link SchemaEnum} or null if the class provided is not an {@link java.lang.Enum} */ - private static Enum generateEnum(Class clazz, String enumName) { + private SchemaEnum generateEnum(Class clazz) { if (clazz.isEnum()) { - Enum newEnum = new Enum("".equals(enumName.strip()) ? clazz.getSimpleName() : enumName); + // check for the Enum annotation as this method may be called from different paths + org.eclipse.microprofile.graphql.Enum annotation = clazz + .getAnnotation(org.eclipse.microprofile.graphql.Enum.class); + String name = annotation == null ? "" : annotation.value(); + // only check for Name annotation if the Enum didn't have a value + if ("".equals(name)) { + // check to see if this class has @Name annotation + String nameValue = getNameAnnotationValue(clazz); + if (nameValue != null) { + name = nameValue; + } + + } + SchemaEnum newSchemaEnum = new SchemaEnum(getTypeName(clazz)); Arrays.stream(clazz.getEnumConstants()) .map(v -> v.toString()) - .forEach(newEnum::addValue); - return newEnum; + .forEach(newSchemaEnum::addValue); + return newSchemaEnum; + } + return null; + } + + /** + * Return a new {@link SchemaFieldDefinition} with the given field and class. + * + * @param method the {@link DiscoveredMethod} + * @return a {@link SchemaFieldDefinition} + */ + @SuppressWarnings("rawTypes") + private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method) { + String sValueClassName = method.getReturnType(); + DataFetcher dataFetcher = null; + boolean isArrayReturnType = method.isArrayReturnType || method.isCollectionType() || method.isMap(); + + if (isArrayReturnType) { + + if (method.isMap) { + // add DataFetcher that will just retrieve the values() from the map + // dataFetcher = DataFetcherUtils.newMapValuesDataFetcher(fieldName); + } + } + + SchemaFieldDefinition fd = new SchemaFieldDefinition(method.name, getGraphQLType(sValueClassName), isArrayReturnType, + false); + fd.setDataFetcher(dataFetcher); + return fd; + } + + /** + * Checks a {@link SchemaType} for {@link SchemaFieldDefinition}s which contain known scalars and replace them with the scalar + * name. + * + * @param schema {@link Schema} to check scalars for. + * @param type {@link SchemaType} to check + */ + private void checkScalars(Schema schema, SchemaType type) { + type.getFieldDefinitions().forEach(fd -> { + SchemaScalar scalar = getScalar(fd.getReturnType()); + if (scalar != null) { + fd.setReturnType(scalar.getName()); + if (!schema.containsScalarWithName(scalar.getName())) { + schema.addScalar(scalar); + } + } + }); + } + + /** + * Return the {@link Name} annotation value if it exists or null. + * + * @param clazz {@link Class} to check + * @return the {@link Name} annotation value if it exists or null + */ + protected String getNameAnnotationValue(Class clazz) { + Name nameAnnotation = clazz.getAnnotation(Name.class); + if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { + return nameAnnotation.value(); } return null; } /** - * Returns a {@link Scalar} if one matches the known list of scalars available from the {@link ExtendedScalars} helper. + * Return correct name for a Type or Enum based upon the value of the annotation or the {@link Name}. + * + * @param clazz {@link Class} to introspect. + * @return the correct name + */ + protected String getTypeName(Class clazz) { + Type typeAnnotation = clazz.getAnnotation(Type.class); + Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); + Input inputAnnotation = clazz.getAnnotation(Input.class); + Enum enumAnnotation = clazz.getAnnotation(Enum.class); + + String name = ""; + if (typeAnnotation != null) { + name = typeAnnotation.value(); + } else if (interfaceAnnotation != null) { + name = interfaceAnnotation.value(); + } else if (inputAnnotation != null) { + name = inputAnnotation.value(); + } else if (enumAnnotation != null) { + name = enumAnnotation.value(); + } + + if (name.isBlank()) { + name = getNameAnnotationValue(clazz); + } + + return name == null || name.isBlank() ? clazz.getSimpleName() : name; + } + + /** + * Return the simple name from a given class as a String. This takes into account any annotations that may be present. + * + * @param className class name + * @return the simple class name + * @throws ClassNotFoundException if invalid class name + */ + protected String getSimpleName(String className) + throws ClassNotFoundException { + if (INT.equals(className) + || FLOAT.equals(className) || ID.equals(className) + || STRING.equals(className) || BOOLEAN.equalsIgnoreCase(className)) { + return className; + } + // return the type name taking into account any annotations + Class clazz = Class.forName(className); + return getTypeName(clazz); + } + + /** + * Look in the given {@link Schema} for any field definitions, arguments and key value classes that contain the return type of + * the long return type and replace with short return type. + * + * @param schema schema to introspect + * @param longReturnType long return type + * @param shortReturnType short return type + */ + private void updateLongTypes(Schema schema, String longReturnType, String shortReturnType) { + schema.getTypes().forEach(t -> { + t.getFieldDefinitions().forEach(fd -> { + if (fd.getReturnType().equals(longReturnType)) { + fd.setReturnType(shortReturnType); + } + + // check arguments + fd.getArguments().forEach(a -> { + if (a.getArgumentType().equals(longReturnType)) { + a.setArgumentType(shortReturnType); + } + }); + }); + }); + } + + /** + * Return true if the fully qualified class is an enum. + * + * @param clazz class to check + * @return true if the fully qualified class is an enum. + */ + private boolean isEnumClass(String clazz) { + try { + return (Class.forName(clazz)).isEnum(); + } catch (ClassNotFoundException e) { + return false; + } + } + + /** + * Returns a {@link SchemaScalar} if one matches the known list of scalars available from the {@link ExtendedScalars} helper. * * @param clazzName class name to check for - * @return a {@link Scalar} if one matches the known list of scalars or null if none found + * @return a {@link SchemaScalar} if one matches the known list of scalars or null if none found */ - private static Scalar getScalar(String clazzName) { + private static SchemaScalar getScalar(String clazzName) { return SUPPORTED_SCALARS.get(clazzName); } /** - * Return the {@link Name} annotation if it exists on the given class. - * @param clazz {@link Class} to introspect - * @return the {@link Name} annotation or null if the class does not contain the annotation + * Return a Class from a class name and ignore any exceptions. + * + * @param clazzName the class name as a String + * @return a Class name */ - public static Name getNameAnnotation(Class clazz) { - if (clazz != null && clazz.isAnnotationPresent(Name.class)) { - return clazz.getAnnotation(Name.class); + public static Class getSafeClass(String clazzName) { + try { + return Class.forName(clazzName); + } catch (ClassNotFoundException e) { + return null; + } + } + + /** + * Return the GraphQL type for the given Java type. + * + * @param className fully qualified class name + * @return the GraphQL type + */ + protected static String getGraphQLType(String className) { + if (INTEGER_LIST.contains(className)) { + return INT; + } else if (LONG_LIST.contains(className)) { + return Long.class.getName(); + } else if (FLOAT_LIST.contains(className)) { + return FLOAT; + } else if (BOOLEAN_LIST.contains(className)) { + return BOOLEAN; + } else if (STRING_LIST.contains(className)) { + return STRING; + } else if (java.util.UUID.class.getName().equals(className)) { + return ID; + } + + return className; + } + + /** + * Return a {@link Map} of the discovered methods. Key is the short name and the value + *

+ * + * @param clazz Class to introspect + * @return a {@link Map} of the methods and return types + * @throws IntrospectionException if there were errors introspecting classes + */ + protected static Map retrieveBeanMethods(Class clazz) + throws IntrospectionException { + Map mapDiscoveredMethods = new HashMap<>(); + + Arrays.asList(Introspector.getBeanInfo(clazz).getPropertyDescriptors()) + .stream() + .filter(p -> p.getReadMethod() != null + && !p.getReadMethod().getName().equals("getClass")) + .forEach(pd -> { + Method readMethod = pd.getReadMethod(); + Method writeMethod = pd.getWriteMethod(); + DiscoveredMethod discoveredMethod = null; + + String name = readMethod.getName(); + String prefix = null; + String varName; + if (name.startsWith("is")) { + prefix = "is"; + } else if (name.startsWith("get")) { + prefix = "get"; + } else if (name.startsWith("has")) { + prefix = "has"; + } + + // remove the prefix and make first letter lowercase + varName = name.replaceAll(prefix, ""); + varName = varName.substring(0, 1).toLowerCase() + varName.substring(1); + + // check for either Name or JsonbProperty annotations on method or field + String annotatedName = getMethodName(readMethod); + if (annotatedName != null) { + varName = annotatedName; + } else { + // check the field + annotatedName = getFieldName(clazz, pd.getName()); + if (annotatedName != null) { + varName = annotatedName; + } + } + + Class returnClazz = readMethod.getReturnType(); + String returnClazzName = returnClazz.getName(); + + boolean fieldHasIdAnnotation = false; + // check for Id annotation on class or field associated with the read method + // and if present change the type to ID + try { + Field field = clazz.getDeclaredField(pd.getName()); + fieldHasIdAnnotation = field != null && field.getAnnotation(Id.class) != null; + } catch (NoSuchFieldException e) { + // ignore + } + + if (fieldHasIdAnnotation || readMethod.getAnnotation(Id.class) != null) { + returnClazzName = ID; + } + + // check various array types + if (Collection.class.isAssignableFrom(returnClazz)) { + java.lang.reflect.Type returnType = readMethod.getGenericReturnType(); + if (returnType instanceof ParameterizedType) { + ParameterizedType paramReturnType = (ParameterizedType) returnType; + discoveredMethod = new DiscoveredMethod(varName, + paramReturnType.getActualTypeArguments()[0].getTypeName(), + READ); + discoveredMethod.setCollectionType(returnClazzName); + } + } else if (Map.class.isAssignableFrom(returnClazz)) { + java.lang.reflect.Type returnType = readMethod.getGenericReturnType(); + if (returnType instanceof ParameterizedType) { + ParameterizedType paramReturnType = (ParameterizedType) returnType; + // we are only interested in the value type as for this implementation we return a collection of + //values() + discoveredMethod = new DiscoveredMethod(varName, + paramReturnType.getActualTypeArguments()[1].getTypeName(), + READ); + discoveredMethod.setMap(true); + } + } else if (!returnClazzName.isEmpty() && returnClazzName.startsWith("[")) { + // return type is array of either primitives or Objects/Interfaces. + String sPrimitiveType = PRIMITIVE_ARRAY_MAP.get(returnClazzName); + if (sPrimitiveType != null) { + // is an array of primitives + discoveredMethod = new DiscoveredMethod(varName, sPrimitiveType, READ); + discoveredMethod.setArrayReturnType(true); + } else { + // Array of Object/Interface + // We are only supporting single level arrays currently + int cArrayCount = 0; + for (int i = 0; i < returnClazzName.length(); i++) { + if (returnClazzName.charAt(i) == '[') { + cArrayCount++; + } + } + + if (cArrayCount > 1) { + LOGGER.warning("Multi-dimension arrays are not yet supported. Ignoring field " + name); + } else { + String sRealReturnClass = returnClazzName.substring(2, returnClazzName.length() - 1); + discoveredMethod = new DiscoveredMethod(varName, sRealReturnClass, READ); + discoveredMethod.setArrayReturnType(true); + } + } + } else { + discoveredMethod = new DiscoveredMethod(varName, returnClazzName, READ); + } + + if (discoveredMethod != null) { + mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); + } + }); + return mapDiscoveredMethods; + } + + /** + * Return the field name after checking both the {@link Name} and {@link JsonbProperty} annotations are present on the {@link + * Method}.

Name will take precedence if both are specified. + * + * @param method {@link Method} to check + * @return the field name or null if non exist + */ + protected static String getMethodName(Method method) { + Name nameAnnotation = method.getAnnotation(Name.class); + JsonbProperty jsonbPropertyAnnotation = method.getAnnotation(JsonbProperty.class); + if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { + // Name annotation is specified so use this and don't bother checking JsonbProperty + return nameAnnotation.value(); + } + if (jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank()) { + return jsonbPropertyAnnotation.value(); } return null; } + /** + * Return the field name after checking both the {@link Name} and {@link JsonbProperty} annotations are present on the field + * name..

Name will take precedence if both are specified. + * + * @param clazz {@link Class} to check + * @param fieldName field name to check + * @return the field name or null if none exist + */ + protected static String getFieldName(Class clazz, String fieldName) { + try { + Field field = clazz.getDeclaredField(fieldName); + if (field != null) { + Name nameAnnotation = field.getAnnotation(Name.class); + // Name annotation is specified so use this and don't bother checking JsonbProperty + if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { + return nameAnnotation.value(); + } + // check for JsonbProperty + JsonbProperty jsonbPropertyAnnotation = field.getAnnotation(JsonbProperty.class); + return jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank() + ? jsonbPropertyAnnotation.value() + : null; + } + return null; + } catch (NoSuchFieldException e) { + return null; + } + } + + /** + * Defines discovered methods for a class. + */ + public static class DiscoveredMethod { + + /** + * Indicates read method. + */ + public static final int READ = 0; + + /** + * Indicates write method. + */ + public static final int WRITE = 1; + + /** + * Name of the discovered method. + */ + private String name; + + /** + * Return type of the method. + */ + private String returnType; + + /** + * type of method. + */ + private int methodType; + + /** + * If the return type is a {@link Collection} then this is the type of {@link Collection} and the returnType will be + * return type for the collection. + */ + private String collectionType; + + /** + * Indicates if the return type is an array. + */ + private boolean isArrayReturnType; + + /** + * Indicates if the return type is a {@link Map}. Note: In the 1.0 mciroprofile spec the behaviour if {@link Map} is + * undefined. + */ + private boolean isMap; + + /** + * Constructor using name and return type. + * + * @param name name of the method + * @param returnType return type + * @param methodType type of the method + */ + public DiscoveredMethod(String name, String returnType, int methodType) { + this.name = name; + this.returnType = returnType; + this.methodType = methodType; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getReturnType() { + return returnType; + } + + public void setReturnType(String returnType) { + this.returnType = returnType; + } + + public String getCollectionType() { + return collectionType; + } + + public void setCollectionType(String collectionType) { + this.collectionType = collectionType; + } + + public boolean isMap() { + return isMap; + } + + public void setMap(boolean map) { + isMap = map; + } + + public int getMethodType() { + return methodType; + } + + public void setMethodType(int methodType) { + this.methodType = methodType; + } + + public boolean isArrayReturnType() { + return isArrayReturnType; + } + + public void setArrayReturnType(boolean arrayReturnType) { + isArrayReturnType = arrayReturnType; + } + + public boolean isCollectionType() { + return collectionType != null; + } + + @Override + public String toString() { + return "DiscoveredMethod{" + + "name='" + name + '\'' + + ", returnType='" + returnType + '\'' + + ", methodType=" + methodType + + ", collectionType='" + collectionType + '\'' + + ", isArrayReturnType=" + isArrayReturnType + + ", isMap=" + isMap + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DiscoveredMethod that = (DiscoveredMethod) o; + return methodType == that.methodType + && isArrayReturnType == that.isArrayReturnType + && isMap == that.isMap + && Objects.equals(name, that.name) + && Objects.equals(returnType, that.returnType) + && Objects.equals(collectionType, that.collectionType); + } + + @Override + public int hashCode() { + return Objects.hash(name, returnType, methodType, collectionType, isArrayReturnType, isMap); + } + } + } diff --git a/microprofile/graphql/server/src/main/java/module-info.java b/microprofile/graphql/server/src/main/java/module-info.java index bfdc794a629..e1cecdf6bf9 100644 --- a/microprofile/graphql/server/src/main/java/module-info.java +++ b/microprofile/graphql/server/src/main/java/module-info.java @@ -26,6 +26,8 @@ requires graphql.java; requires microprofile.graphql.api; requires graphql.java.extended.scalars; + requires java.desktop; + requires java.json.bind; exports io.helidon.microprofile.graphql.server.application; diff --git a/microprofile/graphql/server/src/main/resources/META-INF/beans.xml b/microprofile/graphql/server/src/main/resources/META-INF/beans.xml index 70f5bd0fe91..c5405c06e65 100644 --- a/microprofile/graphql/server/src/main/resources/META-INF/beans.xml +++ b/microprofile/graphql/server/src/main/resources/META-INF/beans.xml @@ -1,7 +1,7 @@ + integration-test + + + + ${project.build.directory}/classes + + + ${project.build.directory}/test-classes + + + @@ -115,6 +133,9 @@ **/*IT.java + + org.slf4j:slf4j-log4j12 + diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java new file mode 100644 index 00000000000..9f36985462b --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +import io.helidon.microprofile.graphql.server.model.Schema; +import io.helidon.microprofile.graphql.server.util.SchemaUtils; + +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; +import graphql.execution.SubscriptionExecutionStrategy; +import graphql.execution.instrumentation.Instrumentation; +import graphql.schema.GraphQLSchema; +import graphql.schema.idl.SchemaPrinter; + +import static graphql.ExecutionInput.newExecutionInput; + +/** + * Defines a content in which to execute GraphQL commands. + * + * @param the context that will be used when executing queries. + */ +public class ExecutionContext { + + private static final Logger LOGGER = Logger.getLogger(ExecutionContext.class.getName()); + + /** + * An empty map. + */ + protected static final Map EMPTY_MAP = new HashMap<>(); + + /** + * {@link GraphQL} instance to use for execution. + */ + private GraphQL graphQL; + + /** + * {@link GraphQLSchema} instance to use for execution. + */ + private GraphQLSchema graphQLSchema; + + /** + * {@link SchemaUtils} instance to use for {@link Schema} generation. + */ + private final SchemaUtils schemaUtils; + + /** + * {@link Schema} used. + */ + private Schema schema; + + /** + * A context to pass to GrapQL for execution. + */ + private C context; + /** + * Return the {@link GraphQLSchema} instance created. + * + * @return the {@link GraphQLSchema} instance + */ + public GraphQLSchema getGraphQLSchema() { + return graphQLSchema; + } + + /** + * Return the generated {@link Schema}. + * + * @return the generated {@link Schema} + */ + public Schema getSchema() { + return schema; + } + + /** + * Construct an execution context in which ti execute GraphQL queries. + * + * @param context context + */ + public ExecutionContext(C context) { + try { + this.schemaUtils = new SchemaUtils(); + this.schema = schemaUtils.generateSchema(); + this.graphQLSchema = schema.generateGraphQLSchema(); + this.context = context; + SchemaPrinter.Options options = SchemaPrinter.Options.defaultOptions().includeDirectives(false); + SchemaPrinter schemaPrinter = new SchemaPrinter(options); + + GraphQL.Builder builder = GraphQL.newGraphQL(this.graphQLSchema) + .subscriptionExecutionStrategy(new SubscriptionExecutionStrategy()); + + Instrumentation instrumentation = null; // getInstrumentation(); + + if (instrumentation != null) { + builder.instrumentation(instrumentation); + } + + graphQL = builder.build(); + + LOGGER.info("Generated schema:\n" + schemaPrinter.print(graphQLSchema)); + } catch (Exception e) { + String message = "Unable to build GraphQL Schema: " + e; + LOGGER.warning(message); + throw new RuntimeException(message, e); + } + } + + /** + * Execute the given query and return the the {@link ExecutionResult}. + * + * @param query query to execute + * @return the {@link ExecutionResult} + */ + public ExecutionResult execute(String query) { + return execute(query, null, EMPTY_MAP); + } + + /** + * Execute the given query and return the the {@link ExecutionResult} for the given operation name. + * + * @param query query to execute + * @param operationName the name of the operation + * @return the {@link ExecutionResult} + */ + public ExecutionResult execute(String query, String operationName) { + return execute(query, operationName, EMPTY_MAP); + } + + /** + * Execute the given query and return the the {@link ExecutionResult} for the given operation name. + * + * @param query query to execute + * @param operationName the name of the operation + * @param mapVariables the map of variables to pass through + * @return the {@link ExecutionResult} + */ + public ExecutionResult execute(String query, String operationName, Map mapVariables) { + ExecutionInput.Builder executionInput = newExecutionInput() + .query(query) + .operationName(operationName) + .context(context) + .variables(mapVariables == null ? EMPTY_MAP : mapVariables); + + return graphQL.execute(executionInput.build()); + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java index f60cb2d56c5..692afb99a14 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java @@ -16,6 +16,11 @@ package io.helidon.microprofile.graphql.server.application; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -26,9 +31,26 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import io.helidon.microprofile.graphql.server.ExecutionContext; +import io.helidon.microprofile.graphql.server.util.JsonUtils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import graphql.ExecutionResult; + +/** + * A resource for servicing GraphQL requests. + */ @Path("/") @RequestScoped public class GraphQLResource { + + private static final Logger LOGGER = Logger.getLogger(GraphQLResource.class.getName()); + + /** + * {@link ExecutionContext} for this resource. + */ + private ExecutionContext context; + /** * Process a GET request. * @@ -42,8 +64,12 @@ public class GraphQLResource { @Consumes({ MediaType.APPLICATION_JSON }) public Response processGraphQLQueryGET(@QueryParam("query") String query, @QueryParam("operationName") String operation, - @QueryParam("variables") String variables) { - return Response.ok("GET").build(); + @QueryParam("variables") String variables) throws JsonProcessingException { + Map mapVariables = null; + if (variables != null) { + mapVariables = JsonUtils.convertJSONtoMap(variables); + } + return processRequest(query, operation, mapVariables); } /** @@ -55,7 +81,57 @@ public Response processGraphQLQueryGET(@QueryParam("query") String query, @POST @Produces({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON }) - public Response processGraphQLQueryPOST(String body) { - return Response.ok("POST").build(); + public Response processGraphQLQueryPOST(String body) throws JsonProcessingException { + Map json = JsonUtils.convertJSONtoMap(body); + return processRequest( + (String) json.get("query"), + (String) json.get("operationName"), + getVariables(json.get("variables"))); + } + + /** + * Initialize the {@link ExecutionContext}. + */ + @PostConstruct + public void init() { + try { + context = new ExecutionContext<>("Dummy Context"); + } catch (Exception e) { + LOGGER.warning("Unable to build GraphQL Schema: " + e); + e.printStackTrace(); + throw new RuntimeException(e); + } } + + /** + * Process the GraphQL Query. + * + * @param query query to process + * @param operation operation name, may be null + * @param variables variables to apply to query, may be null + * @return a {@link Response} containing the results of the query + */ + private Response processRequest(String query, String operation, Map variables) + throws JsonProcessingException { + ExecutionResult executionResult = context.execute(query, operation, variables); + return Response.ok(JsonUtils.convertMapToJson(executionResult.toSpecification())).build(); + } + + /** + * Return a {@link Map} of variables from the given argument. + * + * @param variables Json string of variables + * @return a {@link Map} of variables + */ + private Map getVariables(Object variables) throws JsonProcessingException { + if (variables instanceof Map) { + Map inputVars = (Map) variables; + Map vars = new HashMap<>(); + + inputVars.forEach((k, v) -> vars.put(String.valueOf(k), v)); + return vars; + } + return JsonUtils.convertJSONtoMap(String.valueOf(variables)); + } + } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java new file mode 100644 index 00000000000..6603cdd5834 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java @@ -0,0 +1,47 @@ +package io.helidon.microprofile.graphql.server.util; + +import java.util.Collections; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Various Json utilities. + */ +public class JsonUtils { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + /** + * Private constructor for utilities class. + */ + private JsonUtils() { + } + + /** + * Convert a String that "should" contain JSON to a {@link Map}. + * + * @param json the Json to convert + * @return a {@link Map} containing the JSON. + */ + @SuppressWarnings("unchecked") + public static Map convertJSONtoMap(String json) throws JsonProcessingException { + if (json == null || json.trim().length() == 0) { + return Collections.emptyMap(); + } + return OBJECT_MAPPER.readValue(json, new TypeReference<>() { }); + } + + /** + * Convert an {@link Map} to a Json String representation. + * + * @param map {@link Map} to convert toJson + * @return a Json String representation + */ + public static String convertMapToJson(Map map) throws JsonProcessingException { + return OBJECT_MAPPER.writeValueAsString(map); + } +} + diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java index 1d18e5fe32a..8113365303a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -205,6 +205,11 @@ public class SchemaUtils { */ private JandexUtils jandexUtils; + /** + * Holds the {@link Set} of unresolved types while processing the annotations. + */ + private Set setUnresolvedTypes = new HashSet<>(); + /** * Construct a {@link SchemaUtils} instance. */ @@ -246,7 +251,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec Schema schema = new Schema(); List listSchemaTypes = new ArrayList<>(); List> listGraphQLApis = new ArrayList<>(); - Set setUnresolvedTypes = new HashSet<>(); + setUnresolvedTypes.clear(); for (Class clazz : clazzes) { // only include interfaces and concrete classes/enums @@ -287,12 +292,6 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec jandexUtils.getKnownImplementors(clazz.getName()).forEach(c -> setUnresolvedTypes.add(c.getName())); } -// else { -// // Automatically create an input type for this type if it is not an interface -// listSchemaTypes.add(type.createInputType("Input")); -// -// // check if the class implements an interface and add to unresolved classes if it is annotated -// } } else if (inputAnnotation != null) { // InputType String inputTypeName = inputAnnotation.value(); @@ -311,7 +310,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec } // process all the types - addTypesToSchema(schema, listSchemaTypes, setUnresolvedTypes); + addTypesToSchema(schema, listSchemaTypes); // create any types that are yet unresolved. e.g. an Order that contains OrderLine objects // we must also ensure if the unresolved type contains another unresolved type then we process it @@ -424,11 +423,10 @@ private SchemaType generateType(String realReturnType, Set setUnresolved * * @param schema the {@link Schema} to add to * @param listTypes the {@link List} of {@link Type}s previously obtained. - * @param setUnresolvedTypes the {@link Set} of unresolved types to update * @throws IntrospectionException * @throws ClassNotFoundException */ - private void addTypesToSchema(Schema schema, List listTypes, Set setUnresolvedTypes) + private void addTypesToSchema(Schema schema, List listTypes) throws IntrospectionException, ClassNotFoundException { for (SchemaType t : listTypes) { @@ -724,8 +722,6 @@ protected static Map retrieveBeanMethods(Class claz prefix = "is"; } else if (name.startsWith("get")) { prefix = "get"; - } else if (name.startsWith("has")) { - prefix = "has"; } // remove the prefix and make first letter lowercase diff --git a/microprofile/graphql/server/src/main/java/module-info.java b/microprofile/graphql/server/src/main/java/module-info.java index e1cecdf6bf9..24b231cf73a 100644 --- a/microprofile/graphql/server/src/main/java/module-info.java +++ b/microprofile/graphql/server/src/main/java/module-info.java @@ -18,16 +18,20 @@ * GraphQL microprofile server module. */ module helidon.microprofile.graphql.server { - requires io.helidon.microprofile.cdi; requires cdi.api; - requires java.ws.rs; + + requires com.fasterxml.jackson.databind; + requires io.helidon.microprofile.cdi; + requires jandex; + requires java.ws.rs; + requires java.desktop; + requires java.json.bind; + requires java.annotation; requires java.logging; requires graphql.java; - requires microprofile.graphql.api; requires graphql.java.extended.scalars; - requires java.desktop; - requires java.json.bind; + requires microprofile.graphql.api; exports io.helidon.microprofile.graphql.server.application; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java index cf9542a0f5a..84ba4ba4c70 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -16,44 +16,135 @@ package io.helidon.microprofile.graphql.server; +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.helidon.microprofile.cdi.Main; import io.helidon.microprofile.graphql.server.util.JandexUtils; +import io.helidon.microprofile.graphql.server.util.JsonUtils; import io.helidon.microprofile.server.Server; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.logging.LoggingFeature; import org.junit.jupiter.api.AfterAll; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + /** * Abstract functionality for integration tests. */ public abstract class AbstractGraphQLIT extends AbstractGraphQLTest { - private static Server server; + private static final Logger LOGGER = Logger.getLogger(AbstractGraphQLIT.class.getName()); + + /** + * Initial GraphiQL query from UI. + */ + protected static final String QUERY_INTROSPECT = "query {\n" + + " __schema {\n" + + " types {\n" + + " name\n" + + " }\n" + + " }\n" + + "}"; + + protected static final String QUERY = "query"; + protected static final String VARIABLES = "variables"; + protected static final String OPERATION = "operationName"; + protected static final String GRAPHQL = "graphql"; + protected static final String UI = "ui"; + private static String graphQLUrl; - private static String graphQLUIUrl; - public static int getPort() { - return server.port(); - } + private static Client client; - public static String getGraphQLUrl() { - return graphQLUrl; + public static Client getClient() { + return client; } - public static void _setupTest() { + /** + * Startup the test and create the Jandex index with the supplied {@link Class}es. + * + * @param clazzes {@link Class}es to add to index + */ + public static void _startupTest(Class... clazzes) throws IOException { + // setup the Jandex index with the required classes System.clearProperty(JandexUtils.PROP_INDEX_FILE); + String indexFileName = getTempIndexFile(); + setupIndex(indexFileName, clazzes); + System.setProperty(JandexUtils.PROP_INDEX_FILE, indexFileName); - server = Server.create().start(); - String baseURL = "http://127.0.0.1:" + getPort() + "/"; - graphQLUrl = baseURL + "graphql"; - graphQLUIUrl = baseURL+ "ui"; + Main.main(new String[0]); + // server = Server.create().start(); + graphQLUrl= "http://127.0.0.1:7001/"; + System.out.println("GraphQL URL: " + graphQLUrl); - System.out.println("GraphQL UI: " + graphQLUIUrl); + + client = ClientBuilder.newBuilder() + .register(new LoggingFeature(LOGGER, Level.WARNING, LoggingFeature.Verbosity.PAYLOAD_ANY, 32768)) + .property(ClientProperties.FOLLOW_REDIRECTS, true) + .build(); } @AfterAll public static void teardownTest() { - if (server != null) { - server.stop(); - } + Main.shutdown(); + } + + /** + * Return a {@link WebTarget} for the graphQL end point. + * + * @return a {@link WebTarget} for the graphQL end point + */ + protected static WebTarget getGraphQLWebTarget() { + Client client = getClient(); + return client.target(graphQLUrl); + } + + protected String encode(String param) throws UnsupportedEncodingException { + return param == null ? null : param.replaceAll("}", "%7D").replaceAll("\\{", "%7B"); + } + + /** + * Generate a Json Map with a request to send to graphql + * + * @param query the query to send + * @param operation optional operation + * @param variables optional variables + * @return a {@link java.util.Map} + */ + protected Map generateJsonRequest(String query, String operation, Map variables) { + Map map = new HashMap<>(); + map.put(QUERY, query); + map.put(OPERATION, operation); + map.put(VARIABLES, variables); + + return map; + } + + /** + * Return the response as Json. + * + * @param response {@link javax.ws.rs.core.Response} received from web server + * @return the response as Json + */ + protected Map getJsonResponse(Response response) throws JsonProcessingException { + String stringResponse = (response.readEntity(String.class)); + assertThat(stringResponse, is(notNullValue())); + return JsonUtils.convertJSONtoMap(stringResponse); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java index 208fe525941..c838422bdc6 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java @@ -46,24 +46,6 @@ public abstract class AbstractGraphQLTest { private SchemaPrinter schemaPrinter; - private String indexFileName = null; - private File indexFile = null; - - public void setIndexFileName(String indexFileName) { - this.indexFileName = indexFileName; - } - - public void setIndexFile(File indexFile) { - this.indexFile = indexFile; - } - - public String getIndexFileName() { - return indexFileName; - } - - public File getIndexFile() { - return indexFile; - } /** * Create a Jandex index using the given file name and classes. @@ -152,20 +134,18 @@ protected SchemaPrinter getSchemaPrinter() { return schemaPrinter; } - /** + /** * Setup an index file for the given {@link Class}es. * * @param clazzes classes to setup index for * @return a {@link JandexUtils} instance * @throws IOException */ - protected void setupIndex(Class... clazzes) throws IOException { - indexFileName = getTempIndexFile(); - - createManualIndex(indexFileName, Arrays.stream(clazzes).map(this::getIndexClassName).toArray(String[]::new)); + protected static void setupIndex(String indexFileName, Class... clazzes) throws IOException { + createManualIndex(indexFileName, Arrays.stream(clazzes).map(c -> getIndexClassName(c)).toArray(String[]::new)); System.setProperty(JandexUtils.PROP_INDEX_FILE, indexFileName); assertThat(indexFileName, CoreMatchers.is(notNullValue())); - indexFile = new File(indexFileName); + File indexFile = new File(indexFileName); assertThat(indexFile.exists(), CoreMatchers.is(true)); // do a load to check the classes are there @@ -175,7 +155,7 @@ protected void setupIndex(Class... clazzes) throws IOException { assertThat(utils.getIndex().getKnownClasses().size(), CoreMatchers.is(clazzes.length)); } - protected String getIndexClassName(Class clazz) { + protected static String getIndexClassName(Class clazz) { if (clazz == null) { throw new IllegalArgumentException("Must not be null"); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java index bfc5a61ee9e..ee1f6cb16ac 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java @@ -17,24 +17,59 @@ package io.helidon.microprofile.graphql.server; import java.io.IOException; +import java.util.Map; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.helidon.microprofile.graphql.server.test.types.Person; +import io.helidon.microprofile.graphql.server.util.JsonUtils; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + /** * Integration tests for microprofile-graphql implementation. */ public class GraphQLIT extends AbstractGraphQLIT { @BeforeAll - public static void setup() { - _setupTest(); + public static void setup() throws IOException { + _startupTest(Person.class); } @Test - @Disabled - public void basicTest() throws IOException, InterruptedException { - Thread.sleep(Long.MAX_VALUE); + public void basicEndpointTests() throws InterruptedException, JsonProcessingException { + // test /graphql endpoint + WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL); + System.err.println(webTarget.getUri()); + Map mapRequest = generateJsonRequest(QUERY_INTROSPECT, null, null); + Response response = webTarget.request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(JsonUtils.convertMapToJson(mapRequest))); + assertThat(response, is(notNullValue())); + assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); + + Map graphQLResults = getJsonResponse(response); + System.err.println(JsonUtils.convertMapToJson(graphQLResults)); + // assertThat(graphQLResults.size(), CoreMatchers.is(1)); + + } + + @Test + public void testUIEndpoint() throws InterruptedException { + // test /ui endpoint + WebTarget webTarget = getGraphQLWebTarget().path(UI).path("index.html"); + System.err.println(webTarget.getUri()); + + Response response = webTarget.request().get(); + assertThat(response, is(notNullValue())); + assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index 25c91bbac12..5fd8b0ebc43 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -42,21 +42,24 @@ import static org.hamcrest.MatcherAssert.assertThat; /** - * Integration ests for {@link SchemaUtilsTest}. + * Integration tests for {@link SchemaUtilsTest}. */ public class SchemaUtilsIT extends AbstractGraphQLTest { + private String indexFileName = null; + private File indexFile = null; + @BeforeEach - public void setupTest() { + public void setupTest() throws IOException { System.clearProperty(JandexUtils.PROP_INDEX_FILE); - setIndexFile(null); - setIndexFileName(null); + indexFileName = getTempIndexFile();; + indexFile = null; } @AfterEach public void teardownTest() { - if (getIndexFile() != null) { - getIndexFile().delete(); + if (indexFile != null) { + indexFile.delete(); } } @@ -65,7 +68,7 @@ public void teardownTest() { */ @Test public void testTypeGenerationWithNoName() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(Person.class); + setupIndex(indexFileName, Person.class); SchemaUtils schemaUtils = new SchemaUtils(); Schema schema = schemaUtils.generateSchema(); @@ -81,7 +84,7 @@ public void testTypeGenerationWithNoName() throws IOException, IntrospectionExce */ @Test public void testPersonWithName() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(PersonWithName.class); + setupIndex(indexFileName, PersonWithName.class); SchemaUtils schemaUtils = new SchemaUtils(); Schema schema = schemaUtils.generateSchema(); @@ -92,7 +95,7 @@ public void testPersonWithName() throws IOException, IntrospectionException, Cla @Test public void testMultipleLevels() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(Level0.class); + setupIndex(indexFileName, Level0.class); SchemaUtils schemaUtils = new SchemaUtils(); Schema schema = schemaUtils.generateSchema(); @@ -112,7 +115,7 @@ public void testMultipleLevels() throws IOException, IntrospectionException, Cla @Test public void testInterfaceDiscoveryWithImplementorsWithNoTypeAnnotation() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(Vehicle.class, Car.class, Motorbike.class, AbstractVehicle.class); + setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, AbstractVehicle.class); assertInterfaceResults(); } @@ -121,7 +124,7 @@ public void testInterfaceDiscoveryWithImplementorsWithNoTypeAnnotation() */ @Test public void testInterfaceDiscoveryWithUnresolvedType() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(Vehicle.class, Car.class, Motorbike.class, VehicleIncident.class, AbstractVehicle.class); + setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, VehicleIncident.class, AbstractVehicle.class); assertInterfaceResults(); } @@ -130,7 +133,7 @@ public void testInterfaceDiscoveryWithUnresolvedType() throws IOException, Intro */ @Test public void testInterfaceDiscoveryWithoutTypes() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(Vehicle.class, Car.class, Motorbike.class, AbstractVehicle.class); + setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, AbstractVehicle.class); assertInterfaceResults(); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java new file mode 100644 index 00000000000..d68fbb4b764 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.util; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests for {@link JandexUtils}. + */ +class JsonUtilsTest extends AbstractGraphQLTest { + + @Test + @SuppressWarnings("unchecked") + public void testValidJSON() throws JsonProcessingException { + Map jsonMap = JsonUtils.convertJSONtoMap("{\"name\": \"tim\" }"); + assertThat(jsonMap, CoreMatchers.is(CoreMatchers.notNullValue())); + assertThat(jsonMap.size(), CoreMatchers.is(1)); + assertThat(jsonMap.get("name"), CoreMatchers.is("tim")); + + jsonMap = JsonUtils.convertJSONtoMap("{\"name\": \"tim\", \"address\": { \"address1\": \"address line 1\", \"city\": \"Perth\" } }"); + assertThat(jsonMap, CoreMatchers.is(CoreMatchers.notNullValue())); + assertThat(jsonMap.size(), CoreMatchers.is(2)); + assertThat(jsonMap.get("name"), CoreMatchers.is("tim")); + + Map mapAddress = (Map) jsonMap.get("address"); + assertThat(mapAddress, CoreMatchers.is(CoreMatchers.notNullValue())); + assertThat(mapAddress.size(), CoreMatchers.is(2)); + + assertThat(mapAddress.get("address1"), CoreMatchers.is("address line 1")); + assertThat(mapAddress.get("city"), CoreMatchers.is("Perth")); + } + + @Test + public void testNullJson() throws JsonProcessingException { + Map jsonMap = JsonUtils.convertJSONtoMap(null); + assertThat(jsonMap.size(), CoreMatchers.is(0)); + } + + @Test + public void testEmptyJson() throws JsonProcessingException { + Map jsonMap = JsonUtils.convertJSONtoMap(" "); + assertThat(jsonMap.size(), CoreMatchers.is(0)); + } + + @Test + public void testConvertToJson() throws JsonProcessingException { + Map map = new HashMap<>(); + map.put("name", "tim"); + assertThat(JsonUtils.convertMapToJson(map), CoreMatchers.is("{\"name\":\"tim\"}")); + } + +} \ No newline at end of file From 91173bcc705550540e905fc15dfc2fdfc9686605 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 6 Mar 2020 14:32:16 +0800 Subject: [PATCH 005/178] initial GraphQLApi work --- microprofile/graphql/server/pom.xml | 4 + .../graphql/server/ExecutionContext.java | 6 +- .../server/application/GraphQLResource.java | 7 +- .../server/model/DescriptiveElement.java | 21 +- .../graphql/server/model/SchemaGenerator.java | 19 +- .../graphql/server/util/JsonUtils.java | 18 +- .../graphql/server/util/SchemaUtils.java | 339 ++++++++++-------- .../graphql/server/AbstractGraphQLIT.java | 2 +- .../graphql/server/AbstractGraphQLTest.java | 117 ++++-- .../graphql/server/DummyContext.java | 36 ++ .../graphql/server/SchemaUtilsIT.java | 17 +- .../test/queries/SimpleQueriesNoArgs.java | 47 +++ .../graphql/server/test/types/Car.java | 2 +- .../graphql/server/util/JsonUtilsTest.java | 9 +- .../graphql/server/util/SchemaUtilsTest.java | 2 +- .../resources/test-results/enum-test-03.txt | 4 +- 16 files changed, 426 insertions(+), 224 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DummyContext.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 9abe95582af..2b4cbffe58e 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -48,6 +48,10 @@ jakarta.json.bind jakarta.json.bind-api + + org.eclipse + yasson + io.helidon.microprofile.server helidon-microprofile-server diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index 9f36985462b..90018c0c5ae 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -71,6 +71,7 @@ public class ExecutionContext { * A context to pass to GrapQL for execution. */ private C context; + /** * Return the {@link GraphQLSchema} instance created. * @@ -100,7 +101,10 @@ public ExecutionContext(C context) { this.schema = schemaUtils.generateSchema(); this.graphQLSchema = schema.generateGraphQLSchema(); this.context = context; - SchemaPrinter.Options options = SchemaPrinter.Options.defaultOptions().includeDirectives(false); + SchemaPrinter.Options options = SchemaPrinter.Options + .defaultOptions().includeDirectives(false) + .includeScalarTypes(true) + .includeExtendedScalarTypes(true); SchemaPrinter schemaPrinter = new SchemaPrinter(options); GraphQL.Builder builder = GraphQL.newGraphQL(this.graphQLSchema) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java index 692afb99a14..b4e8dc875a3 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java @@ -64,7 +64,7 @@ public class GraphQLResource { @Consumes({ MediaType.APPLICATION_JSON }) public Response processGraphQLQueryGET(@QueryParam("query") String query, @QueryParam("operationName") String operation, - @QueryParam("variables") String variables) throws JsonProcessingException { + @QueryParam("variables") String variables) { Map mapVariables = null; if (variables != null) { mapVariables = JsonUtils.convertJSONtoMap(variables); @@ -111,8 +111,7 @@ public void init() { * @param variables variables to apply to query, may be null * @return a {@link Response} containing the results of the query */ - private Response processRequest(String query, String operation, Map variables) - throws JsonProcessingException { + private Response processRequest(String query, String operation, Map variables) { ExecutionResult executionResult = context.execute(query, operation, variables); return Response.ok(JsonUtils.convertMapToJson(executionResult.toSpecification())).build(); } @@ -123,7 +122,7 @@ private Response processRequest(String query, String operation, Map getVariables(Object variables) throws JsonProcessingException { + private Map getVariables(Object variables) { if (variables instanceof Map) { Map inputVars = (Map) variables; Map vars = new HashMap<>(); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java index 06f0229269b..1540c8d61d1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java @@ -19,6 +19,7 @@ import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.NEWLINE; import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.NOTHING; import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.QUOTE; +import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.TRIPLE_QUOTE; /** * Describes an element that has a description. @@ -27,28 +28,36 @@ public interface DescriptiveElement { /** * Set the description for this element. + * * @param description the description for this element */ void setDescription(String description); /** * Return the description for this element. + * * @return the description for this element */ String getDescription(); /** - * Return the description of the schema element. - * Only valid for Type, Field, Method, Parameter + * Return the description of the schema element. Only valid for Type, Field, Method, Parameter + * * @return the description of the schema element. */ default String getSchemaElementDescription() { - if (getDescription() != null) { + String description = getDescription(); + if (description != null) { + // if the description contains a quote or newline then use the triple quote option + boolean useNormalQuotes = description.indexOf('\n') == -1 && description.indexOf('"') == -1; + return new StringBuilder() - .append(QUOTE) - .append(getDescription().replaceAll("\"", "\\\\\"")) - .append(QUOTE) + .append(useNormalQuotes + ? QUOTE : (TRIPLE_QUOTE + NEWLINE)) + .append(description) + .append(useNormalQuotes + ? QUOTE : (NEWLINE + TRIPLE_QUOTE)) .append(NEWLINE) .toString(); } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java index 16a047cbffd..9778405f1e3 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java @@ -25,11 +25,6 @@ public interface SchemaGenerator { */ String NOTHING = ""; - /** - * Comma and newline. - */ - String COMMA_NEWLINE = ",\n"; - /** * Comma and space. */ @@ -41,9 +36,14 @@ public interface SchemaGenerator { String COMMA = ","; /** - * A comment identifier. + * Triple quote. + */ + String TRIPLE_QUOTE = "\"\"\""; + + /** + * Double quote. */ - String COMMENT = "#"; + String QUOTE = "\""; /** * Spacer. @@ -70,11 +70,6 @@ public interface SchemaGenerator { */ char MANDATORY = '!'; - /** - * Double quote. - */ - char QUOTE = '"'; - /** * Open curly bracket. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java index 6603cdd5834..eb4a02e09be 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java @@ -3,16 +3,18 @@ import java.util.Collections; import java.util.Map; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; /** * Various Json utilities. */ public class JsonUtils { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + /** + * JSONB instance. + */ + private static final Jsonb JSONB = JsonbBuilder.create(); /** * Private constructor for utilities class. @@ -27,11 +29,11 @@ private JsonUtils() { * @return a {@link Map} containing the JSON. */ @SuppressWarnings("unchecked") - public static Map convertJSONtoMap(String json) throws JsonProcessingException { + public static Map convertJSONtoMap(String json) { if (json == null || json.trim().length() == 0) { return Collections.emptyMap(); } - return OBJECT_MAPPER.readValue(json, new TypeReference<>() { }); + return JSONB.fromJson(json, Map.class); } /** @@ -40,8 +42,8 @@ public static Map convertJSONtoMap(String json) throws JsonProce * @param map {@link Map} to convert toJson * @return a Json String representation */ - public static String convertMapToJson(Map map) throws JsonProcessingException { - return OBJECT_MAPPER.writeValueAsString(map); + public static String convertMapToJson(Map map) { + return JSONB.toJson(map); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java index 8113365303a..85eea25897f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -18,6 +18,7 @@ import java.beans.IntrospectionException; import java.beans.Introspector; +import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -81,12 +82,12 @@ public class SchemaUtils { put(OffsetTime.class.getName(), new SchemaScalar("Time", OffsetTime.class.getName(), ExtendedScalars.Time)); put(LocalTime.class.getName(), new SchemaScalar("LocalTime", OffsetTime.class.getName(), ExtendedScalars.Time)); put(Object.class.getName(), new SchemaScalar("Object", Object.class.getName(), ExtendedScalars.Object)); - put(Long.class.getName(), new SchemaScalar("Long", Long.class.getName(), Scalars.GraphQLLong)); put(OffsetDateTime.class.getName(), new SchemaScalar("DateTime", OffsetDateTime.class.getName(), ExtendedScalars.DateTime)); put(LocalDate.class.getName(), new SchemaScalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); put(BigDecimal.class.getName(), new SchemaScalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); put(BigInteger.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + put(Long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); }}; /** @@ -102,12 +103,6 @@ public class SchemaUtils { add("java.lang.Number"); }}; - private static final List LONG_LIST = new ArrayList<>() {{ - add("java.lang.Long"); - add("long"); - add("Long"); - }}; - private static final List BOOLEAN_LIST = new ArrayList<>() {{ add("boolean"); add(Boolean.class.getName()); @@ -157,7 +152,6 @@ public class SchemaUtils { addAll(STRING_LIST); addAll(FLOAT_LIST); addAll(INTEGER_LIST); - addAll(LONG_LIST); }}; /** @@ -250,9 +244,10 @@ public Schema generateSchema() throws IntrospectionException, ClassNotFoundExcep protected Schema generateSchemaFromClasses(Class... clazzes) throws IntrospectionException, ClassNotFoundException { Schema schema = new Schema(); List listSchemaTypes = new ArrayList<>(); - List> listGraphQLApis = new ArrayList<>(); setUnresolvedTypes.clear(); + SchemaType rootQueryType = new SchemaType(schema.getQueryName(), null); + for (Class clazz : clazzes) { // only include interfaces and concrete classes/enums if (clazz.isInterface() || (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()))) { @@ -285,7 +280,8 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec type.setDescription(descriptionAnnotation.value()); } - listSchemaTypes.add(type); + // add the discovered type + addTypeToSchema(schema, type); if (type.isInterface()) { // is an interface so check for any implementors and add them to @@ -303,16 +299,12 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec // obtain top level query API's // if (clazz.isAnnotationPresent(GraphQLApi.class)) { - // defines top level - listGraphQLApis.add(clazz); + addRootQueriesToSchema(rootQueryType, schema, clazz); } } } - // process all the types - addTypesToSchema(schema, listSchemaTypes); - - // create any types that are yet unresolved. e.g. an Order that contains OrderLine objects + // create any types that are still unresolved. e.g. an Order that contains OrderLine objects // we must also ensure if the unresolved type contains another unresolved type then we process it while (setUnresolvedTypes.size() > 0) { String returnType = setUnresolvedTypes.iterator().next(); @@ -340,7 +332,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec boolean fExists = schema.getTypes().stream() .filter(t -> t.getName().equals(simpleName)).count() > 0; if (!fExists) { - SchemaType newType = generateType(returnType, setUnresolvedTypes); + SchemaType newType = generateType(returnType); // update any return types to the discovered scalars checkScalars(schema, newType); @@ -370,10 +362,8 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec }); }); - SchemaType rootQueryType = new SchemaType(schema.getQueryName(), null); - // process the @GraphQLApi annotated classes - if (listGraphQLApis.size() == 0) { + if (rootQueryType.getFieldDefinitions().size() == 0) { LOGGER.warning("Unable to find any classes with @GraphQLApi annotation." + "Unable to build schema"); } else { @@ -388,13 +378,12 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec /** * Generate a {@link SchemaType} from a given class. * - * @param realReturnType the class to generate type from - * @param setUnresolvedTypes the set of unresolved types to add to if we find one + * @param realReturnType the class to generate type from * @return a {@link SchemaType} * @throws IntrospectionException * @throws ClassNotFoundException */ - private SchemaType generateType(String realReturnType, Set setUnresolvedTypes) + private SchemaType generateType(String realReturnType) throws IntrospectionException, ClassNotFoundException { String simpleName = getSimpleName(realReturnType); @@ -419,53 +408,70 @@ private SchemaType generateType(String realReturnType, Set setUnresolved } /** - * Add all collected types to the {@link Schema}. + * Add all the queries in the annotated class to the root query defined by the {@link SchemaType}. * - * @param schema the {@link Schema} to add to - * @param listTypes the {@link List} of {@link Type}s previously obtained. + * @param schemaType the root query type + * @param clazz {@link Class} to introspect * @throws IntrospectionException - * @throws ClassNotFoundException */ - private void addTypesToSchema(Schema schema, List listTypes) - throws IntrospectionException, ClassNotFoundException { + private void addRootQueriesToSchema(SchemaType schemaType, Schema schema, Class clazz) throws IntrospectionException { + retrieveBeanMethods(clazz).forEach((k, v) -> { + String methodName = k; + DiscoveredMethod method = v; - for (SchemaType t : listTypes) { - String valueClassName = t.getValueClassName(); - retrieveBeanMethods(Class.forName(valueClassName)).forEach((k, v) -> { + DataFetcher dataFetcher = null; - SchemaFieldDefinition fd = newFieldDefinition(v); - t.addFieldDefinition(fd); + SchemaFieldDefinition fd = newFieldDefinition(v); + if (dataFetcher != null) { + fd.setDataFetcher(dataFetcher); + } + schemaType.addFieldDefinition(fd); - checkScalars(schema, t); + checkScalars(schema, schemaType); - String returnType = v.getReturnType(); - // check to see if this is a known type - if (returnType.equals(fd.getReturnType()) && !setUnresolvedTypes.contains(returnType)) { - // value class was unchanged meaning we need to resolve - setUnresolvedTypes.add(returnType); - } - }); - - // check if this Type is an interface then obtain all concrete classes that implement the type - // and add them to the set of unresolved types - if (t.isInterface()) { - Collection> setConcreteClasses = jandexUtils.getKnownImplementors(valueClassName); - setConcreteClasses.forEach(c -> setUnresolvedTypes.add(c.getName())); + String returnType = v.getReturnType(); + // check to see if this is a known type + if (returnType.equals(fd.getReturnType()) && !setUnresolvedTypes.contains(returnType)) { + // value class was unchanged meaning we need to resolve + setUnresolvedTypes.add(returnType); } - - schema.addType(t); - } + }); } /** - * Process a {@link Class} which has been annotated with {@link GraphQLApi}. + * Add the type to the {@link Schema}. * - * @param schema the {@link Schema} to add the discovered information to - * @param clazz {@link Class} to introspect - * @param + * @param schema the {@link Schema} to add to + * @throws IntrospectionException + * @throws ClassNotFoundException */ - private void processGraphQLApi(Schema schema, Class clazz) { + private void addTypeToSchema(Schema schema, SchemaType type) + throws IntrospectionException, ClassNotFoundException { + + String valueClassName = type.getValueClassName(); + retrieveBeanMethods(Class.forName(valueClassName)).forEach((k, v) -> { + + SchemaFieldDefinition fd = newFieldDefinition(v); + type.addFieldDefinition(fd); + + checkScalars(schema, type); + + String returnType = v.getReturnType(); + // check to see if this is a known type + if (returnType.equals(fd.getReturnType()) && !setUnresolvedTypes.contains(returnType)) { + // value class was unchanged meaning we need to resolve + setUnresolvedTypes.add(returnType); + } + }); + // check if this Type is an interface then obtain all concrete classes that implement the type + // and add them to the set of unresolved types + if (type.isInterface()) { + Collection> setConcreteClasses = jandexUtils.getKnownImplementors(valueClassName); + setConcreteClasses.forEach(c -> setUnresolvedTypes.add(c.getName())); + } + + schema.addType(type); } /** @@ -679,8 +685,6 @@ public static Class getSafeClass(String clazzName) { protected static String getGraphQLType(String className) { if (INTEGER_LIST.contains(className)) { return INT; - } else if (LONG_LIST.contains(className)) { - return Long.class.getName(); } else if (FLOAT_LIST.contains(className)) { return FLOAT; } else if (BOOLEAN_LIST.contains(className)) { @@ -713,99 +717,8 @@ protected static Map retrieveBeanMethods(Class claz .forEach(pd -> { Method readMethod = pd.getReadMethod(); Method writeMethod = pd.getWriteMethod(); - DiscoveredMethod discoveredMethod = null; - - String name = readMethod.getName(); - String prefix = null; - String varName; - if (name.startsWith("is")) { - prefix = "is"; - } else if (name.startsWith("get")) { - prefix = "get"; - } - // remove the prefix and make first letter lowercase - varName = name.replaceAll(prefix, ""); - varName = varName.substring(0, 1).toLowerCase() + varName.substring(1); - - // check for either Name or JsonbProperty annotations on method or field - String annotatedName = getMethodName(readMethod); - if (annotatedName != null) { - varName = annotatedName; - } else { - // check the field - annotatedName = getFieldName(clazz, pd.getName()); - if (annotatedName != null) { - varName = annotatedName; - } - } - - Class returnClazz = readMethod.getReturnType(); - String returnClazzName = returnClazz.getName(); - - boolean fieldHasIdAnnotation = false; - // check for Id annotation on class or field associated with the read method - // and if present change the type to ID - try { - Field field = clazz.getDeclaredField(pd.getName()); - fieldHasIdAnnotation = field != null && field.getAnnotation(Id.class) != null; - } catch (NoSuchFieldException e) { - // ignore - } - - if (fieldHasIdAnnotation || readMethod.getAnnotation(Id.class) != null) { - returnClazzName = ID; - } - - // check various array types - if (Collection.class.isAssignableFrom(returnClazz)) { - java.lang.reflect.Type returnType = readMethod.getGenericReturnType(); - if (returnType instanceof ParameterizedType) { - ParameterizedType paramReturnType = (ParameterizedType) returnType; - discoveredMethod = new DiscoveredMethod(varName, - paramReturnType.getActualTypeArguments()[0].getTypeName(), - READ); - discoveredMethod.setCollectionType(returnClazzName); - } - } else if (Map.class.isAssignableFrom(returnClazz)) { - java.lang.reflect.Type returnType = readMethod.getGenericReturnType(); - if (returnType instanceof ParameterizedType) { - ParameterizedType paramReturnType = (ParameterizedType) returnType; - // we are only interested in the value type as for this implementation we return a collection of - //values() - discoveredMethod = new DiscoveredMethod(varName, - paramReturnType.getActualTypeArguments()[1].getTypeName(), - READ); - discoveredMethod.setMap(true); - } - } else if (!returnClazzName.isEmpty() && returnClazzName.startsWith("[")) { - // return type is array of either primitives or Objects/Interfaces. - String sPrimitiveType = PRIMITIVE_ARRAY_MAP.get(returnClazzName); - if (sPrimitiveType != null) { - // is an array of primitives - discoveredMethod = new DiscoveredMethod(varName, sPrimitiveType, READ); - discoveredMethod.setArrayReturnType(true); - } else { - // Array of Object/Interface - // We are only supporting single level arrays currently - int cArrayCount = 0; - for (int i = 0; i < returnClazzName.length(); i++) { - if (returnClazzName.charAt(i) == '[') { - cArrayCount++; - } - } - - if (cArrayCount > 1) { - LOGGER.warning("Multi-dimension arrays are not yet supported. Ignoring field " + name); - } else { - String sRealReturnClass = returnClazzName.substring(2, returnClazzName.length() - 1); - discoveredMethod = new DiscoveredMethod(varName, sRealReturnClass, READ); - discoveredMethod.setArrayReturnType(true); - } - } - } else { - discoveredMethod = new DiscoveredMethod(varName, returnClazzName, READ); - } + DiscoveredMethod discoveredMethod = generateDiscoveredMethod(readMethod, clazz, pd); if (discoveredMethod != null) { mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); @@ -814,6 +727,111 @@ protected static Map retrieveBeanMethods(Class claz return mapDiscoveredMethods; } + /** + * Generate a {@link DiscoveredMethod} from the given arguments. + * + * @param method {@link Method} being introspected + * @param clazz {@link Class} being introspected + * @param pd {@link PropertyDescriptor} for the property being introspected + * @return a {@link DiscoveredMethod} + */ + private static DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, PropertyDescriptor pd) { + DiscoveredMethod discoveredMethod = null; + + String name = method.getName(); + String prefix = null; + String varName; + if (name.startsWith("is")) { + prefix = "is"; + } else if (name.startsWith("get")) { + prefix = "get"; + } + + // remove the prefix and make first letter lowercase + varName = name.replaceAll(prefix, ""); + varName = varName.substring(0, 1).toLowerCase() + varName.substring(1); + // check for either Name or JsonbProperty annotations on method or field + String annotatedName = getMethodName(method); + if (annotatedName != null) { + varName = annotatedName; + } else { + // check the field + annotatedName = getFieldName(clazz, pd.getName()); + if (annotatedName != null) { + varName = annotatedName; + } + } + + Class returnClazz = method.getReturnType(); + String returnClazzName = returnClazz.getName(); + + boolean fieldHasIdAnnotation = false; + // check for Id annotation on class or field associated with the read method + // and if present change the type to ID + try { + Field field = clazz.getDeclaredField(pd.getName()); + fieldHasIdAnnotation = field != null && field.getAnnotation(Id.class) != null; + } catch (NoSuchFieldException e) { + // ignore + } + + if (fieldHasIdAnnotation || method.getAnnotation(Id.class) != null) { + returnClazzName = ID; + } + + // check various array types + if (Collection.class.isAssignableFrom(returnClazz)) { + java.lang.reflect.Type returnType = method.getGenericReturnType(); + if (returnType instanceof ParameterizedType) { + ParameterizedType paramReturnType = (ParameterizedType) returnType; + discoveredMethod = new DiscoveredMethod(varName, + paramReturnType.getActualTypeArguments()[0].getTypeName(), + READ, method); + discoveredMethod.setCollectionType(returnClazzName); + } + } else if (Map.class.isAssignableFrom(returnClazz)) { + java.lang.reflect.Type returnType = method.getGenericReturnType(); + if (returnType instanceof ParameterizedType) { + ParameterizedType paramReturnType = (ParameterizedType) returnType; + // we are only interested in the value type as for this implementation we return a collection of + //values() + discoveredMethod = new DiscoveredMethod(varName, + paramReturnType.getActualTypeArguments()[1].getTypeName(), + READ, method); + discoveredMethod.setMap(true); + } + } else if (!returnClazzName.isEmpty() && returnClazzName.startsWith("[")) { + // return type is array of either primitives or Objects/Interfaces. + String sPrimitiveType = PRIMITIVE_ARRAY_MAP.get(returnClazzName); + if (sPrimitiveType != null) { + // is an array of primitives + discoveredMethod = new DiscoveredMethod(varName, sPrimitiveType, READ, method); + discoveredMethod.setArrayReturnType(true); + } else { + // Array of Object/Interface + // We are only supporting single level arrays currently + int cArrayCount = 0; + for (int i = 0; i < returnClazzName.length(); i++) { + if (returnClazzName.charAt(i) == '[') { + cArrayCount++; + } + } + + if (cArrayCount > 1) { + LOGGER.warning("Multi-dimension arrays are not yet supported. Ignoring field " + name); + } else { + String sRealReturnClass = returnClazzName.substring(2, returnClazzName.length() - 1); + discoveredMethod = new DiscoveredMethod(varName, sRealReturnClass, READ, method); + discoveredMethod.setArrayReturnType(true); + } + } + } else { + discoveredMethod = new DiscoveredMethod(varName, returnClazzName, READ, method); + } + + return discoveredMethod; + } + /** * Return the field name after checking both the {@link Name} and {@link JsonbProperty} annotations are present on the {@link * Method}.

Name will take precedence if both are specified. @@ -910,6 +928,11 @@ public static class DiscoveredMethod { */ private boolean isMap; + /** + * The actual method. + */ + private Method method; + /** * Constructor using name and return type. * @@ -917,10 +940,11 @@ public static class DiscoveredMethod { * @param returnType return type * @param methodType type of the method */ - public DiscoveredMethod(String name, String returnType, int methodType) { + public DiscoveredMethod(String name, String returnType, int methodType, Method method) { this.name = name; this.returnType = returnType; this.methodType = methodType; + this.method = method; } public String getName() { @@ -975,6 +999,14 @@ public boolean isCollectionType() { return collectionType != null; } + public Method getMethod() { + return method; + } + + public void setMethod(Method method) { + this.method = method; + } + @Override public String toString() { return "DiscoveredMethod{" @@ -983,6 +1015,7 @@ public String toString() { + ", methodType=" + methodType + ", collectionType='" + collectionType + '\'' + ", isArrayReturnType=" + isArrayReturnType + + ", method=" + method + ", isMap=" + isMap + '}'; } @@ -1000,12 +1033,14 @@ public boolean equals(Object o) { && isMap == that.isMap && Objects.equals(name, that.name) && Objects.equals(returnType, that.returnType) + && Objects.equals(method, that.method) && Objects.equals(collectionType, that.collectionType); } @Override public int hashCode() { - return Objects.hash(name, returnType, methodType, collectionType, isArrayReturnType, isMap); + return Objects.hash(name, returnType, methodType, method, + collectionType, isArrayReturnType, isMap); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java index 84ba4ba4c70..3675a5a59cf 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -141,7 +141,7 @@ protected Map generateJsonRequest(String query, String operation * @param response {@link javax.ws.rs.core.Response} received from web server * @return the response as Json */ - protected Map getJsonResponse(Response response) throws JsonProcessingException { + protected Map getJsonResponse(Response response) { String stringResponse = (response.readEntity(String.class)); assertThat(stringResponse, is(notNullValue())); return JsonUtils.convertJSONtoMap(stringResponse); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java index c838422bdc6..9765fdd3ecf 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java @@ -23,7 +23,9 @@ import java.net.URL; import java.nio.file.Files; import java.util.Arrays; +import java.util.Map; +import graphql.ExecutionResult; import graphql.schema.GraphQLSchema; import graphql.schema.idl.SchemaPrinter; import io.helidon.microprofile.graphql.server.model.Schema; @@ -37,6 +39,7 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.fail; /** * Functionality for use by unit and functional tests. @@ -50,33 +53,32 @@ public abstract class AbstractGraphQLTest { /** * Create a Jandex index using the given file name and classes. * - * @param fileName the file name to write the index to. The classes should be in the format of - * "java/lang/Thread.class" - * @param clazzes classes to index + * @param fileName the file name to write the index to. The classes should be in the format of "java/lang/Thread.class" + * @param clazzes classes to index */ public static void createManualIndex(String fileName, String... clazzes) throws IOException { - Indexer indexer = new Indexer(); - for (String clazz : clazzes) { - InputStream stream = AbstractGraphQLTest.class.getClassLoader().getResourceAsStream(clazz); - indexer.index(stream); - stream.close(); - } - Index index = indexer.complete(); - - FileOutputStream out = new FileOutputStream(fileName); - IndexWriter writer = new IndexWriter(out); - try { - writer.write(index); - } - finally { - out.close(); - } + Indexer indexer = new Indexer(); + for (String clazz : clazzes) { + InputStream stream = AbstractGraphQLTest.class.getClassLoader().getResourceAsStream(clazz); + indexer.index(stream); + stream.close(); + } + Index index = indexer.complete(); + + FileOutputStream out = new FileOutputStream(fileName); + IndexWriter writer = new IndexWriter(out); + try { + writer.write(index); + } finally { + out.close(); + } } - + /** * Return a temporary file which will be used to import the Jandex index to. + * * @return a new {@link File} - * @throws IOException if any IO related errors + * @throws IOException if any IO related errors */ public static String getTempIndexFile() throws IOException { return Files.createTempFile("index" + System.currentTimeMillis(), "idx").toFile().toString(); @@ -103,8 +105,7 @@ protected static void assertResultsMatch(String results, String fileName) { String sFromFile = new String(Files.readAllBytes(file.toPath())); assertThat("Results do not match expected", results, is(sFromFile)); - } - catch (Exception e) { + } catch (Exception e) { throw new RuntimeException("Exception in resultsMatch sResults=[" + results + "], sFileName=" + fileName, e); } } @@ -113,22 +114,28 @@ protected GraphQLSchema generateGraphQLSchema(Schema schema) { try { GraphQLSchema graphQLSchema = schema.generateGraphQLSchema(); - System.err.println("Schema:\n=======\n" - + getSchemaPrinter().print(graphQLSchema) + - "\n======="); + displaySchema(graphQLSchema); return graphQLSchema; - } - catch (Exception e) { + } catch (Exception e) { Assertions.fail("Schema generation failed. " + e.getMessage() + - "\ncause: " + e.getCause() + - "\nSchema: \n" + schema.getSchemaAsString()); + "\ncause: " + e.getCause() + + "\nSchema: \n" + schema.getSchemaAsString()); return null; } } + protected void displaySchema(GraphQLSchema graphQLSchema) { + System.err.println("Schema:\n=======\n" + + getSchemaPrinter().print(graphQLSchema) + + "\n======="); + } + protected SchemaPrinter getSchemaPrinter() { if (schemaPrinter == null) { - SchemaPrinter.Options options = SchemaPrinter.Options.defaultOptions().includeDirectives(false); + SchemaPrinter.Options options = SchemaPrinter.Options + .defaultOptions().includeDirectives(false) + .includeExtendedScalarTypes(true) + .includeScalarTypes(true); schemaPrinter = new SchemaPrinter(options); } return schemaPrinter; @@ -162,4 +169,52 @@ protected static String getIndexClassName(Class clazz) { return clazz.getName().replaceAll("\\.", "/") + ".class"; } + protected boolean hasErrors(ExecutionResult executionResult) { + return executionResult.getErrors().size() > 0; + } + + protected void displayErrors(ExecutionResult result) { + System.err.println(getError(result)); + } + + protected String getError(ExecutionResult result) { + assertThat(result, CoreMatchers.is(notNullValue())); + StringBuilder sb = new StringBuilder("Errors: "); + result.getErrors().forEach(e -> sb.append(e.getErrorType() + .toString()) + .append(" ") + .append(e.getMessage()) + .append(" ") + .append(e.getLocations() != null ? e.getLocations().toString() : "") + .append('\n')); + return sb.toString(); + } + + /** + * Assert an {@link ExecutionResult} is true and if not then display the error. + * + * @param result {@link ExecutionResult} data + */ + protected Map getAndAssertResult(ExecutionResult result) { + boolean failed = result.getErrors().size() > 0; + if (failed) { + String sError = getError(result); + fail(sError); + } + return result.getData(); + } + + /** + * Assert an {@link ExecutionResult} is true and if not then display the error. + * + * @param result {@link ExecutionResult} extensions + */ + protected Map getAndAssertExtensions(ExecutionResult result) { + boolean failed = result.getErrors().size() > 0; + if (failed) { + String sError = getError(result); + fail(sError); + } + return result.getExtensions(); + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DummyContext.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DummyContext.java new file mode 100644 index 00000000000..03fb1f85407 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DummyContext.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server; + +/** + * A dummt context to be used for IT. + */ +public class DummyContext { + private String dummyField; + + public DummyContext(String dummyField) { + this.dummyField = dummyField; + } + + public String getDummyField() { + return dummyField; + } + + public void setDummyField(String dummyField) { + this.dummyField = dummyField; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index 5fd8b0ebc43..0b6a3fabd53 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -19,9 +19,11 @@ import java.beans.IntrospectionException; import java.io.File; import java.io.IOException; -import java.util.Arrays; +import java.util.Map; +import graphql.ExecutionResult; import io.helidon.microprofile.graphql.server.model.Schema; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; import io.helidon.microprofile.graphql.server.test.types.Car; import io.helidon.microprofile.graphql.server.test.types.Level0; @@ -48,6 +50,7 @@ public class SchemaUtilsIT extends AbstractGraphQLTest { private String indexFileName = null; private File indexFile = null; + private DummyContext dummyContext = new DummyContext("Dummy"); @BeforeEach public void setupTest() throws IOException { @@ -137,6 +140,18 @@ public void testInterfaceDiscoveryWithoutTypes() throws IOException, Introspecti assertInterfaceResults(); } + @Test + public void testSimpleQueryGenerationNoArgs() throws IOException, IntrospectionException, ClassNotFoundException { + setupIndex(indexFileName, SimpleQueriesNoArgs.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + // displaySchema(executionContext.getGraphQLSchema()); + ExecutionResult result = executionContext.execute("query { hero }"); + + Map mapResults = getAndAssertResult(result); + + assertThat(mapResults.size(), is(1)); + } + private void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException { SchemaUtils schemaUtils = new SchemaUtils(); Schema schema = schemaUtils.generateSchema(); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java new file mode 100644 index 00000000000..a716b654a4d --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import javax.json.bind.annotation.JsonbProperty; + +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds simple query definitions with no-argument. + */ +@GraphQLApi +public class SimpleQueriesNoArgs { + + @Query + public String getHero() { + return "R2-D2"; + } + + @Query + @Name("episodeCount") + public int getNumberOfEpisodes() { + return 9; + } + + @Query + @JsonbProperty("numberOfStars") + public Long getTheNumberOfStars() { + return Long.MAX_VALUE; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Car.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Car.java index bfeb847719a..ec9e5fe0d75 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Car.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Car.java @@ -21,7 +21,7 @@ /** * Represents a Car. */ -@Description("A representation of a car") +@Description("A representation of a car.\nThis description should be surrounded by triple quotes.\nThe end.") public class Car extends AbstractVehicle { private int numberOfDoors; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java index d68fbb4b764..d6d7bcc481d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java @@ -19,7 +19,6 @@ import java.util.HashMap; import java.util.Map; -import com.fasterxml.jackson.core.JsonProcessingException; import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; @@ -33,7 +32,7 @@ class JsonUtilsTest extends AbstractGraphQLTest { @Test @SuppressWarnings("unchecked") - public void testValidJSON() throws JsonProcessingException { + public void testValidJSON() { Map jsonMap = JsonUtils.convertJSONtoMap("{\"name\": \"tim\" }"); assertThat(jsonMap, CoreMatchers.is(CoreMatchers.notNullValue())); assertThat(jsonMap.size(), CoreMatchers.is(1)); @@ -53,19 +52,19 @@ public void testValidJSON() throws JsonProcessingException { } @Test - public void testNullJson() throws JsonProcessingException { + public void testNullJson() { Map jsonMap = JsonUtils.convertJSONtoMap(null); assertThat(jsonMap.size(), CoreMatchers.is(0)); } @Test - public void testEmptyJson() throws JsonProcessingException { + public void testEmptyJson() { Map jsonMap = JsonUtils.convertJSONtoMap(" "); assertThat(jsonMap.size(), CoreMatchers.is(0)); } @Test - public void testConvertToJson() throws JsonProcessingException { + public void testConvertToJson() { Map map = new HashMap<>(); map.put("name", "tim"); assertThat(JsonUtils.convertMapToJson(map), CoreMatchers.is("{\"name\":\"tim\"}")); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java index f5751f44b66..ee130494975 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java @@ -207,7 +207,7 @@ public void testGetSimpleName() throws ClassNotFoundException { assertThat(schemaUtils.getSimpleName(SchemaUtils.FLOAT), is(SchemaUtils.FLOAT)); assertThat(schemaUtils.getSimpleName(SchemaUtils.STRING), is(SchemaUtils.STRING)); } - + private void assertDiscoveredMethod(SchemaUtils.DiscoveredMethod discoveredMethod, String name, String returnType, diff --git a/microprofile/graphql/server/src/test/resources/test-results/enum-test-03.txt b/microprofile/graphql/server/src/test/resources/test-results/enum-test-03.txt index d57ec932cc4..88c992829d7 100644 --- a/microprofile/graphql/server/src/test/resources/test-results/enum-test-03.txt +++ b/microprofile/graphql/server/src/test/resources/test-results/enum-test-03.txt @@ -1,4 +1,6 @@ -"Description\"" +""" +Description" +""" enum ShirtSize { Small Medium From 79fa7279bd560a359e8a6cebad9fc03bb8869e0f Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 6 Mar 2020 17:07:26 +0800 Subject: [PATCH 006/178] progress update --- .../graphql/server/model/SchemaGenerator.java | 2 +- .../graphql/server/util/DataFetcherUtils.java | 55 +++++++++++++++++++ .../graphql/server/util/SchemaUtils.java | 34 ++++++++---- .../graphql/server/SchemaUtilsIT.java | 11 ++++ .../test/queries/SimpleQueriesNoArgs.java | 5 ++ 5 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java index 9778405f1e3..0be2bc745a5 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java @@ -39,7 +39,7 @@ public interface SchemaGenerator { * Triple quote. */ String TRIPLE_QUOTE = "\"\"\""; - + /** * Double quote. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java new file mode 100644 index 00000000000..1600b984588 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import graphql.schema.DataFetcher; + +/** + * Utilities for working with {@link DataFetcher}s. + */ +public class DataFetcherUtils { + + /** + * Private constructor for utilities class. + */ + private DataFetcherUtils() { + } + + /** + * Create a new {@link DataFetcher} for a {@link Class} and {@link Method}. + * @param clazz {@link Class} + * @param method {@link Method} + * @param args Optional arguments + * @param value type + * @return a new {@link DataFetcher} + */ + @SuppressWarnings("unchecked") + public static DataFetcher newMethodDataFetcher(Class clazz, Method method, Object... args) { + return environment -> { + Constructor constructor = clazz.getConstructor(); + if (constructor == null) { + throw new IllegalArgumentException("Class " + clazz.getName() + + " must have a no-args constructor"); + } + + return (V) method.invoke(constructor.newInstance()); + }; + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java index 85eea25897f..99328778483 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -61,6 +61,7 @@ import org.eclipse.microprofile.graphql.Input; import org.eclipse.microprofile.graphql.Interface; import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; import org.eclipse.microprofile.graphql.Type; import static io.helidon.microprofile.graphql.server.util.SchemaUtils.DiscoveredMethod.READ; @@ -366,10 +367,8 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec if (rootQueryType.getFieldDefinitions().size() == 0) { LOGGER.warning("Unable to find any classes with @GraphQLApi annotation." + "Unable to build schema"); - } else { - // add in "Query" object to for searching for all Objects and individual object - } + schema.addType(rootQueryType); return schema; @@ -396,7 +395,7 @@ private SchemaType generateType(String realReturnType) for (Map.Entry entry : retrieveBeanMethods(Class.forName(realReturnType)).entrySet()) { DiscoveredMethod discoveredMethod = entry.getValue(); String valueTypeName = discoveredMethod.getReturnType(); - SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod); + SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod, null); type.addFieldDefinition(fd); if (setUnresolvedTypes != null && discoveredMethod.getReturnType().equals(fd.getReturnType())) { @@ -419,9 +418,10 @@ private void addRootQueriesToSchema(SchemaType schemaType, Schema schema, Class< String methodName = k; DiscoveredMethod method = v; - DataFetcher dataFetcher = null; + // assuming no-args at the moment + DataFetcher dataFetcher = DataFetcherUtils.newMethodDataFetcher(clazz, method.getMethod()); - SchemaFieldDefinition fd = newFieldDefinition(v); + SchemaFieldDefinition fd = newFieldDefinition(v, getMethodName(method.getMethod())); if (dataFetcher != null) { fd.setDataFetcher(dataFetcher); } @@ -451,7 +451,7 @@ private void addTypeToSchema(Schema schema, SchemaType type) String valueClassName = type.getValueClassName(); retrieveBeanMethods(Class.forName(valueClassName)).forEach((k, v) -> { - SchemaFieldDefinition fd = newFieldDefinition(v); + SchemaFieldDefinition fd = newFieldDefinition(v, null); type.addFieldDefinition(fd); checkScalars(schema, type); @@ -508,24 +508,28 @@ private SchemaEnum generateEnum(Class clazz) { /** * Return a new {@link SchemaFieldDefinition} with the given field and class. * - * @param method the {@link DiscoveredMethod} + * @param method the {@link DiscoveredMethod} + * @param optionalName optional name for the field definition * @return a {@link SchemaFieldDefinition} */ @SuppressWarnings("rawTypes") - private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method) { + private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String optionalName) { String sValueClassName = method.getReturnType(); DataFetcher dataFetcher = null; boolean isArrayReturnType = method.isArrayReturnType || method.isCollectionType() || method.isMap(); if (isArrayReturnType) { - if (method.isMap) { // add DataFetcher that will just retrieve the values() from the map // dataFetcher = DataFetcherUtils.newMapValuesDataFetcher(fieldName); } } - SchemaFieldDefinition fd = new SchemaFieldDefinition(method.name, getGraphQLType(sValueClassName), isArrayReturnType, + SchemaFieldDefinition fd = new SchemaFieldDefinition(optionalName != null + ? optionalName + : method.name, + getGraphQLType(sValueClassName), + isArrayReturnType, false); fd.setDataFetcher(dataFetcher); return fd; @@ -575,6 +579,7 @@ protected String getTypeName(Class clazz) { Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); Input inputAnnotation = clazz.getAnnotation(Input.class); Enum enumAnnotation = clazz.getAnnotation(Enum.class); + Query queryAnnotation = clazz.getAnnotation(Query.class); String name = ""; if (typeAnnotation != null) { @@ -585,6 +590,8 @@ protected String getTypeName(Class clazz) { name = inputAnnotation.value(); } else if (enumAnnotation != null) { name = enumAnnotation.value(); + } else if (queryAnnotation != null) { + name = queryAnnotation.value(); } if (name.isBlank()) { @@ -840,8 +847,12 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class * @return the field name or null if non exist */ protected static String getMethodName(Method method) { + Query queryAnnotation = method.getAnnotation(Query.class); Name nameAnnotation = method.getAnnotation(Name.class); JsonbProperty jsonbPropertyAnnotation = method.getAnnotation(JsonbProperty.class); + if (queryAnnotation != null && !queryAnnotation.value().isBlank()) { + return queryAnnotation.value(); + } if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { // Name annotation is specified so use this and don't bother checking JsonbProperty return nameAnnotation.value(); @@ -939,6 +950,7 @@ public static class DiscoveredMethod { * @param name name of the method * @param returnType return type * @param methodType type of the method + * @param method {@link Method} */ public DiscoveredMethod(String name, String returnType, int methodType, Method method) { this.name = name; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index 0b6a3fabd53..6a2781b5a29 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -150,6 +150,17 @@ public void testSimpleQueryGenerationNoArgs() throws IOException, IntrospectionE Map mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("hero"), is("R2-D2")); + + result = executionContext.execute("query { episodeCount }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("episodeCount"), is(9)); + + result = executionContext.execute("query { badGuy }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("badGuy"), is("Darth Vader")); } private void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index a716b654a4d..4bf9d69a4a8 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -44,4 +44,9 @@ public int getNumberOfEpisodes() { public Long getTheNumberOfStars() { return Long.MAX_VALUE; } + + @Query("badGuy") + public String getVillain() { + return "Darth Vader"; + } } From 167fc83378c6fe543e5e4b20996556f5dcf597a1 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 9 Mar 2020 17:04:05 +0800 Subject: [PATCH 007/178] progress --- microprofile/graphql/server/pom.xml | 4 + .../graphql/server/util/DataFetcherUtils.java | 13 +- .../graphql/server/util/SchemaUtils.java | 524 +++++++++++++----- .../graphql/server/GraphQLIT.java | 26 +- .../graphql/server/SchemaUtilsIT.java | 82 ++- .../graphql/server/test/db/TestDB.java | 132 +++++ .../test/queries/SimpleQueriesNoArgs.java | 30 +- .../test/queries/SimpleQueriesWithArgs.java | 96 ++++ .../graphql/server/util/SchemaUtilsTest.java | 31 +- 9 files changed, 793 insertions(+), 145 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 2b4cbffe58e..dc8d58c40f6 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -133,6 +133,10 @@ org.apache.maven.plugins maven-failsafe-plugin + + + --add-opens helidon.microprofile.graphql.server/io.helidon.microprofile.graphql.server.test.types=graphql.java + false **/*IT.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java index 1600b984588..d088a932ded 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java @@ -18,6 +18,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; import graphql.schema.DataFetcher; @@ -36,12 +38,12 @@ private DataFetcherUtils() { * Create a new {@link DataFetcher} for a {@link Class} and {@link Method}. * @param clazz {@link Class} * @param method {@link Method} - * @param args Optional arguments + * @param args Optional argument names * @param value type * @return a new {@link DataFetcher} */ @SuppressWarnings("unchecked") - public static DataFetcher newMethodDataFetcher(Class clazz, Method method, Object... args) { + public static DataFetcher newMethodDataFetcher(Class clazz, Method method, String... args) { return environment -> { Constructor constructor = clazz.getConstructor(); if (constructor == null) { @@ -49,7 +51,12 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met + " must have a no-args constructor"); } - return (V) method.invoke(constructor.newInstance()); + ArrayList listArgumentValues = new ArrayList<>(); + if (args.length > 0) { + Arrays.stream(args).forEach(a -> listArgumentValues.add(environment.getArgument(a))); + } + + return (V) method.invoke(constructor.newInstance(), listArgumentValues.toArray()); }; } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java index 99328778483..a54d95c0964 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -18,10 +18,12 @@ import java.beans.IntrospectionException; import java.beans.Introspector; +import java.beans.MethodDescriptor; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; import java.math.BigInteger; @@ -37,6 +39,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -44,6 +47,7 @@ import javax.json.bind.annotation.JsonbProperty; import io.helidon.microprofile.graphql.server.model.Schema; +import io.helidon.microprofile.graphql.server.model.SchemaArgument; import io.helidon.microprofile.graphql.server.model.SchemaEnum; import io.helidon.microprofile.graphql.server.model.SchemaFieldDefinition; import io.helidon.microprofile.graphql.server.model.SchemaInputType; @@ -54,6 +58,7 @@ import graphql.Scalars; import graphql.scalars.ExtendedScalars; import graphql.schema.DataFetcher; +import org.eclipse.microprofile.graphql.DefaultValue; import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.Enum; import org.eclipse.microprofile.graphql.GraphQLApi; @@ -88,6 +93,7 @@ public class SchemaUtils { put(LocalDate.class.getName(), new SchemaScalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); put(BigDecimal.class.getName(), new SchemaScalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); put(BigInteger.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + put("long", new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); put(Long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); }}; @@ -132,29 +138,6 @@ public class SchemaUtils { add("char"); }}; - /** - * List of all Java primitives. - */ - private static final List JAVA_PRIMITIVE_TYPES = new ArrayList<>() {{ - add("byte"); - add("short"); - add("int"); - add("long"); - add("float"); - add("double"); - add("boolean"); - add("char"); - }}; - - /** - * List of all Java primitive objects. - */ - private static final List JAVA_PRIMITIVE_OBJECTS = new ArrayList<>() {{ - addAll(STRING_LIST); - addAll(FLOAT_LIST); - addAll(INTEGER_LIST); - }}; - /** * List of array primitive types and their array mapping. See https://docs.oracle.com/javase/6/docs/api/java/lang/Class * .html#getName%28%29 @@ -258,9 +241,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec continue; } - // // Type, Interface, Input are all treated similarly - // Type typeAnnotation = clazz.getAnnotation(Type.class); Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); Input inputAnnotation = clazz.getAnnotation(Input.class); @@ -296,15 +277,15 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec clazz.getName())); } - // // obtain top level query API's - // if (clazz.isAnnotationPresent(GraphQLApi.class)) { addRootQueriesToSchema(rootQueryType, schema, clazz); } } } + schema.addType(rootQueryType); + // create any types that are still unresolved. e.g. an Order that contains OrderLine objects // we must also ensure if the unresolved type contains another unresolved type then we process it while (setUnresolvedTypes.size() > 0) { @@ -352,7 +333,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec // look though all of interface type and see if any of the known types implement // the interface and if so, add the interface to the type schema.getTypes().stream().filter(SchemaType::isInterface).forEach(it -> { - schema.getTypes().stream().filter(t -> !t.isInterface()).forEach(type -> { + schema.getTypes().stream().filter(t -> !t.isInterface() && t.getValueClassName() != null).forEach(type -> { Class interfaceClass = getSafeClass(it.getValueClassName()); Class typeClass = getSafeClass(type.getValueClassName()); if (interfaceClass != null @@ -369,8 +350,6 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec + "Unable to build schema"); } - schema.addType(rootQueryType); - return schema; } @@ -392,13 +371,13 @@ private SchemaType generateType(String realReturnType) type.setDescription(descriptionAnnotation.value()); } - for (Map.Entry entry : retrieveBeanMethods(Class.forName(realReturnType)).entrySet()) { + for (Map.Entry entry : retrieveGetterBeanMethods(Class.forName(realReturnType)).entrySet()) { DiscoveredMethod discoveredMethod = entry.getValue(); String valueTypeName = discoveredMethod.getReturnType(); SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod, null); type.addFieldDefinition(fd); - if (setUnresolvedTypes != null && discoveredMethod.getReturnType().equals(fd.getReturnType())) { + if (discoveredMethod.getReturnType().equals(fd.getReturnType())) { // value class was unchanged meaning we need to resolve setUnresolvedTypes.add(valueTypeName); } @@ -413,33 +392,50 @@ private SchemaType generateType(String realReturnType) * @param clazz {@link Class} to introspect * @throws IntrospectionException */ - private void addRootQueriesToSchema(SchemaType schemaType, Schema schema, Class clazz) throws IntrospectionException { - retrieveBeanMethods(clazz).forEach((k, v) -> { - String methodName = k; - DiscoveredMethod method = v; + @SuppressWarnings("rawtypes") + private void addRootQueriesToSchema(SchemaType schemaType, Schema schema, Class clazz) + throws IntrospectionException { + for (Map.Entry entry : retrieveAllAnnotatedBeanMethods(clazz).entrySet()) { + DiscoveredMethod discoveredMethod = entry.getValue(); + Method method = discoveredMethod.getMethod(); - // assuming no-args at the moment - DataFetcher dataFetcher = DataFetcherUtils.newMethodDataFetcher(clazz, method.getMethod()); + SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod, getMethodName(method)); - SchemaFieldDefinition fd = newFieldDefinition(v, getMethodName(method.getMethod())); - if (dataFetcher != null) { - fd.setDataFetcher(dataFetcher); + ArrayList argumentNames = new ArrayList<>(); + + // add all the arguments and check to see if they contain types that are not yet known + for (SchemaArgument a : discoveredMethod.getArguments()) { + String originalTypeName = a.getArgumentType(); + String typeName = getGraphQLType(originalTypeName); + a.setArgumentType(typeName); + fd.addArgument(a); + argumentNames.add(a.getArgumentName()); + + // type name has not changed, so must require adding of unresolved type + if (originalTypeName.equals(a.getArgumentType())) { + setUnresolvedTypes.add(typeName); + } } + + DataFetcher dataFetcher = DataFetcherUtils.newMethodDataFetcher(clazz, method, argumentNames.toArray(new String[0])); + fd.setDataFetcher(dataFetcher); + schemaType.addFieldDefinition(fd); + // check for scalar return type checkScalars(schema, schemaType); - String returnType = v.getReturnType(); + String returnType = discoveredMethod.getReturnType(); // check to see if this is a known type if (returnType.equals(fd.getReturnType()) && !setUnresolvedTypes.contains(returnType)) { // value class was unchanged meaning we need to resolve setUnresolvedTypes.add(returnType); } - }); + } } /** - * Add the type to the {@link Schema}. + * Add the given {@link SchemaType} to the {@link Schema}. * * @param schema the {@link Schema} to add to * @throws IntrospectionException @@ -449,7 +445,7 @@ private void addTypeToSchema(Schema schema, SchemaType type) throws IntrospectionException, ClassNotFoundException { String valueClassName = type.getValueClassName(); - retrieveBeanMethods(Class.forName(valueClassName)).forEach((k, v) -> { + retrieveGetterBeanMethods(Class.forName(valueClassName)).forEach((k, v) -> { SchemaFieldDefinition fd = newFieldDefinition(v, null); type.addFieldDefinition(fd); @@ -486,6 +482,7 @@ private SchemaEnum generateEnum(Class clazz) { org.eclipse.microprofile.graphql.Enum annotation = clazz .getAnnotation(org.eclipse.microprofile.graphql.Enum.class); String name = annotation == null ? "" : annotation.value(); + // only check for Name annotation if the Enum didn't have a value if ("".equals(name)) { // check to see if this class has @Name annotation @@ -493,12 +490,11 @@ private SchemaEnum generateEnum(Class clazz) { if (nameValue != null) { name = nameValue; } - } SchemaEnum newSchemaEnum = new SchemaEnum(getTypeName(clazz)); Arrays.stream(clazz.getEnumConstants()) - .map(v -> v.toString()) + .map(Object::toString) .forEach(newSchemaEnum::addValue); return newSchemaEnum; } @@ -522,6 +518,7 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String if (method.isMap) { // add DataFetcher that will just retrieve the values() from the map // dataFetcher = DataFetcherUtils.newMapValuesDataFetcher(fieldName); + // TODO } } @@ -706,114 +703,202 @@ protected static String getGraphQLType(String className) { } /** - * Return a {@link Map} of the discovered methods. Key is the short name and the value - *

+ * Return a {@link Map} of all the discovered methods which have the {@link Query} annotation. * * @param clazz Class to introspect * @return a {@link Map} of the methods and return types * @throws IntrospectionException if there were errors introspecting classes */ - protected static Map retrieveBeanMethods(Class clazz) + protected static Map retrieveAllAnnotatedBeanMethods(Class clazz) throws IntrospectionException { Map mapDiscoveredMethods = new HashMap<>(); + for (Method m : getAllMethods(clazz)) { + if (m.getAnnotation(Query.class) != null) { + DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null); + mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); + } + } + return mapDiscoveredMethods; + } - Arrays.asList(Introspector.getBeanInfo(clazz).getPropertyDescriptors()) - .stream() - .filter(p -> p.getReadMethod() != null - && !p.getReadMethod().getName().equals("getClass")) - .forEach(pd -> { - Method readMethod = pd.getReadMethod(); - Method writeMethod = pd.getWriteMethod(); + /** + * Retrieve only the getter methods for the {@link Class}. + * + * @param clazz the {@link Class} to introspect + * @return a {@link Map} of the methods and return types + * @throws IntrospectionException if there were errors introspecting classes + */ + protected static Map retrieveGetterBeanMethods(Class clazz) throws IntrospectionException { + Map mapDiscoveredMethods = new HashMap<>(); - DiscoveredMethod discoveredMethod = generateDiscoveredMethod(readMethod, clazz, pd); + for (Method m : getAllMethods(clazz)) { + if (m.getName().equals("getClass")) { + continue; + } + Optional optionalPd = Arrays.stream(Introspector.getBeanInfo(clazz).getPropertyDescriptors()) + .filter(p -> p.getReadMethod() != null && p.getReadMethod().getName().equals(m.getName())).findFirst(); + if (optionalPd.isPresent()) { + // this is a getter method, include it here + DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, optionalPd.get()); + mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); + } - if (discoveredMethod != null) { - mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); - } - }); + } return mapDiscoveredMethods; } + /** + * Return all {@link Method}s for a given {@link Class}. + * + * @param clazz the {@link Class} to introspect + * @return all {@link Method}s for a given {@link Class} + * @throws IntrospectionException + */ + protected static List getAllMethods(Class clazz) throws IntrospectionException { + return Arrays.asList(Introspector.getBeanInfo(clazz).getMethodDescriptors()) + .stream() + .map(MethodDescriptor::getMethod) + .collect(Collectors.toList()); + } + /** * Generate a {@link DiscoveredMethod} from the given arguments. * * @param method {@link Method} being introspected * @param clazz {@link Class} being introspected - * @param pd {@link PropertyDescriptor} for the property being introspected + * @param pd {@link PropertyDescriptor} for the property being introspected (may be null if retrieving all methods as in + * the case for a {@link Query} annotation) * @return a {@link DiscoveredMethod} */ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, PropertyDescriptor pd) { - DiscoveredMethod discoveredMethod = null; String name = method.getName(); - String prefix = null; String varName; - if (name.startsWith("is")) { - prefix = "is"; - } else if (name.startsWith("get")) { - prefix = "get"; + + if (pd != null) { + // this is a getter method + String prefix = null; + if (name.startsWith("is")) { + prefix = "is"; + } else if (name.startsWith("get")) { + prefix = "get"; + } + + // remove the prefix and make first letter lowercase + varName = name.replaceAll(prefix, ""); + varName = varName.substring(0, 1).toLowerCase() + varName.substring(1); + } else { + // may be any method, e.g. from GraphQLApi annotated class + varName = name; } - // remove the prefix and make first letter lowercase - varName = name.replaceAll(prefix, ""); - varName = varName.substring(0, 1).toLowerCase() + varName.substring(1); // check for either Name or JsonbProperty annotations on method or field String annotatedName = getMethodName(method); if (annotatedName != null) { varName = annotatedName; - } else { - // check the field + } else if (pd != null) { + // check the field only if this is a getter annotatedName = getFieldName(clazz, pd.getName()); if (annotatedName != null) { varName = annotatedName; } } - Class returnClazz = method.getReturnType(); + Class returnClazz = method.getReturnType(); String returnClazzName = returnClazz.getName(); - boolean fieldHasIdAnnotation = false; - // check for Id annotation on class or field associated with the read method - // and if present change the type to ID - try { - Field field = clazz.getDeclaredField(pd.getName()); - fieldHasIdAnnotation = field != null && field.getAnnotation(Id.class) != null; - } catch (NoSuchFieldException e) { - // ignore + if (pd != null) { + boolean fieldHasIdAnnotation = false; + // check for Id annotation on class or field associated with the method + // and if present change the type to ID + try { + Field field = clazz.getDeclaredField(pd.getName()); + fieldHasIdAnnotation = field != null && field.getAnnotation(Id.class) != null; + } catch (NoSuchFieldException e) { + // ignore + } + + if (fieldHasIdAnnotation || method.getAnnotation(Id.class) != null) { + returnClazzName = ID; + } } - if (fieldHasIdAnnotation || method.getAnnotation(Id.class) != null) { - returnClazzName = ID; + DiscoveredMethod discoveredMethod = new DiscoveredMethod(); + discoveredMethod.setName(varName); + discoveredMethod.setMethodType(READ); + discoveredMethod.setMethod(method); + + Parameter[] parameters = method.getParameters(); + if (parameters != null && parameters.length > 0) { + // process the parameters for the method + + java.lang.reflect.Type[] genericParameterTypes = method.getGenericParameterTypes(); + int i = 0; + for (Parameter parameter : parameters) { + Name paramNameAnnotation = parameter.getAnnotation(Name.class); + String parameterName = paramNameAnnotation != null + && !paramNameAnnotation.value().isBlank() + ? paramNameAnnotation.value() + : parameter.getName(); + DefaultValue defaultValueAnnotations = parameter.getAnnotation(DefaultValue.class); + // TODO: Add default value processing + Class paramType = parameter.getType(); + + ReturnType returnType = getReturnType(paramType, genericParameterTypes[i++]); + + discoveredMethod.addArgument(new SchemaArgument(parameterName, returnType.getReturnClass(), false, null)); + } } - // check various array types + // process the return type for the method + ReturnType realReturnType = getReturnType(returnClazz, method.getGenericReturnType()); + if (realReturnType.getReturnClass() != null && !ID.equals(returnClazzName)) { + discoveredMethod.setArrayReturnType(realReturnType.isArrayType()); + discoveredMethod.setCollectionType(realReturnType.getCollectionType()); + discoveredMethod.setMap(realReturnType.isMap()); + discoveredMethod.setReturnType(realReturnType.getReturnClass()); + } else { + discoveredMethod.setName(varName); + discoveredMethod.setReturnType(returnClazzName); + discoveredMethod.setMethod(method); + } + + return discoveredMethod; + } + + /** + * Return the {@link ReturnType} for this return class and method. + * + * @param returnClazz return type + * @param genericReturnType generic return {@link java.lang.reflect.Type} may be null + * @return a {@link ReturnType} + */ + private static ReturnType getReturnType(Class returnClazz, java.lang.reflect.Type genericReturnType) { + ReturnType actualReturnType = new ReturnType(); + String returnClazzName = returnClazz.getName(); if (Collection.class.isAssignableFrom(returnClazz)) { - java.lang.reflect.Type returnType = method.getGenericReturnType(); - if (returnType instanceof ParameterizedType) { - ParameterizedType paramReturnType = (ParameterizedType) returnType; - discoveredMethod = new DiscoveredMethod(varName, - paramReturnType.getActualTypeArguments()[0].getTypeName(), - READ, method); - discoveredMethod.setCollectionType(returnClazzName); + actualReturnType.setCollectionType(returnClazzName); + if (genericReturnType instanceof ParameterizedType) { + ParameterizedType paramReturnType = (ParameterizedType) genericReturnType; + actualReturnType.setReturnClass(paramReturnType.getActualTypeArguments()[0].getTypeName()); + } else { + actualReturnType.setReturnClass(Object.class.getName()); } } else if (Map.class.isAssignableFrom(returnClazz)) { - java.lang.reflect.Type returnType = method.getGenericReturnType(); - if (returnType instanceof ParameterizedType) { - ParameterizedType paramReturnType = (ParameterizedType) returnType; - // we are only interested in the value type as for this implementation we return a collection of - //values() - discoveredMethod = new DiscoveredMethod(varName, - paramReturnType.getActualTypeArguments()[1].getTypeName(), - READ, method); - discoveredMethod.setMap(true); + actualReturnType.setMap(true); + if (genericReturnType instanceof ParameterizedType) { + ParameterizedType paramReturnType = (ParameterizedType) genericReturnType; + // we are only interested in the value type as for this implementation we return a collection of values() + actualReturnType.setReturnClass(paramReturnType.getActualTypeArguments()[1].getTypeName()); + } else { + actualReturnType.setReturnClass(Object.class.getName()); } } else if (!returnClazzName.isEmpty() && returnClazzName.startsWith("[")) { // return type is array of either primitives or Objects/Interfaces. + actualReturnType.setArrayType(true); String sPrimitiveType = PRIMITIVE_ARRAY_MAP.get(returnClazzName); if (sPrimitiveType != null) { - // is an array of primitives - discoveredMethod = new DiscoveredMethod(varName, sPrimitiveType, READ, method); - discoveredMethod.setArrayReturnType(true); + actualReturnType.setReturnClass(sPrimitiveType); } else { // Array of Object/Interface // We are only supporting single level arrays currently @@ -825,18 +910,15 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class } if (cArrayCount > 1) { - LOGGER.warning("Multi-dimension arrays are not yet supported. Ignoring field " + name); + LOGGER.warning("Multi-dimension arrays are not yet supported. Ignoring class " + returnClazzName); } else { - String sRealReturnClass = returnClazzName.substring(2, returnClazzName.length() - 1); - discoveredMethod = new DiscoveredMethod(varName, sRealReturnClass, READ, method); - discoveredMethod.setArrayReturnType(true); + actualReturnType.setReturnClass(returnClazzName.substring(2, returnClazzName.length() - 1)); } } } else { - discoveredMethod = new DiscoveredMethod(varName, returnClazzName, READ, method); + actualReturnType.setReturnClass(returnClazzName); } - - return discoveredMethod; + return actualReturnType; } /** @@ -939,86 +1021,174 @@ public static class DiscoveredMethod { */ private boolean isMap; + /** + * The {@link List} of {@link SchemaArgument}s for this method. + */ + private List listArguments = new ArrayList<>(); + /** * The actual method. */ private Method method; /** - * Constructor using name and return type. - * - * @param name name of the method - * @param returnType return type - * @param methodType type of the method - * @param method {@link Method} + * Default constructor. */ - public DiscoveredMethod(String name, String returnType, int methodType, Method method) { - this.name = name; - this.returnType = returnType; - this.methodType = methodType; - this.method = method; + public DiscoveredMethod() { } + /** + * Return the name. + * + * @return the name + */ public String getName() { return name; } + /** + * Set the name. + * + * @param name the name + */ public void setName(String name) { this.name = name; } + /** + * Return the return type. + * + * @return the return type + */ public String getReturnType() { return returnType; } + /** + * Set the return type. + * + * @param returnType the return type + */ public void setReturnType(String returnType) { this.returnType = returnType; } + /** + * Return the collection type. + * + * @return the collection + */ public String getCollectionType() { return collectionType; } + /** + * Set the collection type. + * + * @param collectionType the collection type + */ public void setCollectionType(String collectionType) { this.collectionType = collectionType; } + /** + * Indicates if the method is a map return type. + * + * @return if the method is a map return type + */ public boolean isMap() { return isMap; } + /** + * Set if the method is a map return type. + * + * @param map if the method is a map return type + */ public void setMap(boolean map) { isMap = map; } + /** + * Return the method type. + * + * @return the method type + */ public int getMethodType() { return methodType; } + /** + * Set the method type either READ or WRITE. + * + * @param methodType the method type + */ public void setMethodType(int methodType) { this.methodType = methodType; } + /** + * Indicates if the method returns an array. + * + * @return if the method returns an array. + */ public boolean isArrayReturnType() { return isArrayReturnType; } + /** + * Indicates if the method returns an array. + * + * @param arrayReturnType if the method returns an array + */ public void setArrayReturnType(boolean arrayReturnType) { isArrayReturnType = arrayReturnType; } + /** + * INdicates if the method is a collection type. + * + * @return if the method is a collection type + */ public boolean isCollectionType() { return collectionType != null; } + /** + * Returns the {@link Method}. + * + * @return the {@link Method} + */ public Method getMethod() { return method; } + /** + * Sets the {@link Method}. + * + * @param method the {@link Method} + */ public void setMethod(Method method) { this.method = method; } + /** + * Return the {@link List} of {@link SchemaArgument}s. + * + * @return the {@link List} of {@link SchemaArgument} + */ + public List getArguments() { + return this.listArguments; + } + + /** + * Add a {@link SchemaArgument}. + * @param argument a {@link SchemaArgument} + */ + public void addArgument(SchemaArgument argument) { + listArguments.add(argument); + } + @Override public String toString() { return "DiscoveredMethod{" @@ -1027,8 +1197,9 @@ public String toString() { + ", methodType=" + methodType + ", collectionType='" + collectionType + '\'' + ", isArrayReturnType=" + isArrayReturnType - + ", method=" + method - + ", isMap=" + isMap + '}'; + + ", isMap=" + isMap + + ", listArguments=" + listArguments + + ", method=" + method + '}'; } @Override @@ -1056,4 +1227,107 @@ public int hashCode() { } } + /** + * Defines return types for methods or parameters. + */ + public static class ReturnType { + + /** + * Return class. + */ + private String returnClass; + + /** + * Indicates if this is an array type. + */ + private boolean isArrayType = false; + + /** + * Indicates if this is a {@link Map}. + */ + private boolean isMap = false; + + /** + * Return the type of collection. + */ + private String collectionType; + + /** + * Default constructor. + */ + public ReturnType() { + } + + /** + * Return the return class. + * + * @return the return class + */ + public String getReturnClass() { + return returnClass; + } + + /** + * Sets the return class. + * + * @param returnClass the return class + */ + public void setReturnClass(String returnClass) { + this.returnClass = returnClass; + } + + /** + * Indicates if this is an array type. + * + * @return if this is an array type + */ + public boolean isArrayType() { + return isArrayType; + } + + /** + * Set if this is an array type. + * + * @param arrayType if this is an array type + */ + public void setArrayType(boolean arrayType) { + isArrayType = arrayType; + } + + /** + * Indicates if this is a {@link Map}. + * + * @return if this is a {@link Map} + */ + public boolean isMap() { + return isMap; + } + + /** + * Set if this is a {@link Map}. + * + * @param map if this is a {@link Map} + */ + public void setMap(boolean map) { + isMap = map; + } + + /** + * Return the type of collection. + * + * @return the type of collection + */ + public String getCollectionType() { + return collectionType; + } + + /** + * Set the type of collection. + * + * @param collectionType the type of collection + */ + public void setCollectionType(String collectionType) { + this.collectionType = collectionType; + } + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java index ee1f6cb16ac..178c204f99b 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java @@ -17,6 +17,7 @@ package io.helidon.microprofile.graphql.server; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.util.Map; import javax.ws.rs.client.Entity; @@ -28,6 +29,7 @@ import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.util.JsonUtils; +import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -46,19 +48,33 @@ public static void setup() throws IOException { } @Test - public void basicEndpointTests() throws InterruptedException, JsonProcessingException { + public void basicEndpointTests() throws InterruptedException, JsonProcessingException, UnsupportedEncodingException { // test /graphql endpoint WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL); - System.err.println(webTarget.getUri()); Map mapRequest = generateJsonRequest(QUERY_INTROSPECT, null, null); - Response response = webTarget.request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(JsonUtils.convertMapToJson(mapRequest))); + + // test POST + Response response = webTarget.request(MediaType.APPLICATION_JSON_TYPE) + .post(Entity.json(JsonUtils.convertMapToJson(mapRequest))); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); - + Map graphQLResults = getJsonResponse(response); System.err.println(JsonUtils.convertMapToJson(graphQLResults)); - // assertThat(graphQLResults.size(), CoreMatchers.is(1)); + assertThat(graphQLResults.size(), CoreMatchers.is(1)); + + // test GET + webTarget = getGraphQLWebTarget().path(GRAPHQL) + .queryParam(QUERY, encode((String) mapRequest.get(QUERY))) + .queryParam(OPERATION, encode((String) mapRequest.get(OPERATION))) + .queryParam(VARIABLES, encode((String) mapRequest.get(VARIABLES))); + response = webTarget.request(MediaType.APPLICATION_JSON_TYPE).get(); + assertThat(response, is(notNullValue())); + assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); + graphQLResults = getJsonResponse(response); + System.err.println(JsonUtils.convertMapToJson(graphQLResults)); + assertThat(graphQLResults.size(), CoreMatchers.is(1)); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index 6a2781b5a29..5178b1fcc0a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -19,11 +19,19 @@ import java.beans.IntrospectionException; import java.io.File; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import graphql.ExecutionResult; + import io.helidon.microprofile.graphql.server.model.Schema; +import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; import io.helidon.microprofile.graphql.server.test.types.Car; import io.helidon.microprofile.graphql.server.test.types.Level0; @@ -37,6 +45,7 @@ import io.helidon.microprofile.graphql.server.util.SchemaUtilsTest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -55,7 +64,8 @@ public class SchemaUtilsIT extends AbstractGraphQLTest { @BeforeEach public void setupTest() throws IOException { System.clearProperty(JandexUtils.PROP_INDEX_FILE); - indexFileName = getTempIndexFile();; + indexFileName = getTempIndexFile(); + ; indexFile = null; } @@ -141,10 +151,10 @@ public void testInterfaceDiscoveryWithoutTypes() throws IOException, Introspecti } @Test - public void testSimpleQueryGenerationNoArgs() throws IOException, IntrospectionException, ClassNotFoundException { + @SuppressWarnings("unchecked") + public void testSimpleQueryGenerationNoArgs() throws IOException { setupIndex(indexFileName, SimpleQueriesNoArgs.class); ExecutionContext executionContext = new ExecutionContext<>(dummyContext); - // displaySchema(executionContext.getGraphQLSchema()); ExecutionResult result = executionContext.execute("query { hero }"); Map mapResults = getAndAssertResult(result); @@ -161,6 +171,72 @@ public void testSimpleQueryGenerationNoArgs() throws IOException, IntrospectionE mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("badGuy"), is("Darth Vader")); + + result = executionContext.execute("query { allPeople { personId } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + + ArrayList> arrayList = (ArrayList>) mapResults.get("allPeople"); + assertThat(arrayList.size(), is(TestDB.MAX_PEOPLE)); + + result = executionContext.execute("query { returnMediumSize }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnMediumSize"), is("M")); + + result = executionContext.execute("query { returnCurrentDate }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnCurrentDate"), is(notNullValue())); + } + + @Test + @SuppressWarnings("unchecked") + public void testSimpleQueryGenerationWithArgs() throws IOException { + setupIndex(indexFileName, SimpleQueriesWithArgs.class, Car.class, AbstractVehicle.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + + ExecutionResult result = executionContext.execute("query { hero(heroType: \"human\") }"); + Map mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("hero"), is("Luke")); + + result = executionContext.execute("query { hero(heroType: \"droid\") }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("hero"), is("R2-D2")); + + result = executionContext.execute("query { multiply(arg0: 10, arg1: 10) }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("multiply"), is(BigInteger.valueOf(100))); + + result = executionContext.execute("query { findAPerson(personId: 1) { personId creditLimit workAddress { city state zipCode } } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findAPerson"), is(notNullValue())); + + result = executionContext.execute("query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findPeopleFromState"), is(notNullValue())); + + ArrayList> arrayList = (ArrayList>) mapResults.get("findPeopleFromState"); + assertThat(arrayList, is(notNullValue())); + // since its random data we can't be sure if anyone was created in MA + assertThat(arrayList.size() >= 0, is(true)); + + result = executionContext.execute("query { findLocalDates(numberOfValues: 10) }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findLocalDates"), is(notNullValue())); + List listLocalDate = (List) mapResults.get("findLocalDates"); + assertThat(listLocalDate.size(), is(10)); +// +// result = executionContext.execute("query { findEnums(arg0: M) }"); +// mapResults = getAndAssertResult(result); +// assertThat(mapResults.size(), is(1)); +// assertThat(mapResults.get("findEnums"), is(notNullValue())); } private void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java new file mode 100644 index 00000000000..198e008296c --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.db; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import javax.enterprise.context.ApplicationScoped; + +import io.helidon.microprofile.graphql.server.test.types.Address; +import io.helidon.microprofile.graphql.server.test.types.Person; + +/** + * An injectable datasource for integration tests. + */ +@ApplicationScoped +public class TestDB { + + /** + * An empty map. + */ + protected static final Map EMPTY_MAP = new HashMap<>(); + + /** + * US Postal Service two letter postal codes. + */ + private static final String[] STATE_CODES = { + "AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "OF", "DC", + "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", + "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", + "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", + "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI", + "VA", "WA", "WV", "WI", "WY" + }; + + /** + * Used for generating random dates. + */ + private static Random RANDOM = new Random(); + + /** + * Maximum number people to create. + */ + public static final int MAX_PEOPLE = 100; + + private final Map allPeople = new HashMap<>(); + + public TestDB() { + for (int i = 1; i <= MAX_PEOPLE; i++) { + Address homeAddress = generateHomeAddress(); + Address workAddress = generateWorkAddress(); + Address prevAddress1 = generateWorkAddress(); + Address prevAddress2 = generateWorkAddress(); + Person person = new Person(i, "Person " + i, homeAddress, workAddress, new BigDecimal(RANDOM.nextFloat()), + List.of("BA", "BA Hon"), + List.of(prevAddress1, prevAddress2), new int[0], new String[0], EMPTY_MAP, + LocalDate.now()); + allPeople.put(person.getPersonId(), person); + } + } + + public Person getPerson(int personId) { + return allPeople.get(personId); + } + + public Collection getAllPeople() { + return allPeople.values(); + } + + /** + * Return a random Zip code. + * + * @return a random Zip code + */ + private static String getRandomZip() { + return String.valueOf(RANDOM.nextInt(99999)); + } + + /** + * Return a random state. + * + * @return a random state + */ + private static String getRandomState() { + return STATE_CODES[RANDOM.nextInt(STATE_CODES.length)]; + } + + /** + * Return a random name. + * + * @return a random name + */ + private static String getRandomName() { + int cCh = 4 + RANDOM.nextInt(7); + char[] ach = new char[cCh]; + + ach[0] = (char) ('A' + RANDOM.nextInt(26)); + for (int of = 1; of < cCh; ++of) { + ach[of] = (char) ('a' + RANDOM.nextInt(26)); + } + return new String(ach); + } + + public static Address generateHomeAddress() { + return new Address("1234 Railway Parade", null, getRandomName(), + getRandomState(), getRandomZip(), "US"); + } + + public static Address generateWorkAddress() { + return new Address("8 Yawkey Way", null, getRandomName(), + getRandomState(), getRandomZip(), "US"); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index 4bf9d69a4a8..afcda53b63b 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -16,8 +16,15 @@ package io.helidon.microprofile.graphql.server.test.queries; +import java.time.LocalDate; +import java.util.Collection; + +import javax.inject.Inject; import javax.json.bind.annotation.JsonbProperty; +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; +import io.helidon.microprofile.graphql.server.test.types.Person; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.Query; @@ -28,8 +35,11 @@ @GraphQLApi public class SimpleQueriesNoArgs { + @Inject + private TestDB testDB; + @Query - public String getHero() { + public String hero() { return "R2-D2"; } @@ -49,4 +59,22 @@ public Long getTheNumberOfStars() { public String getVillain() { return "Darth Vader"; } + + @Query("allPeople") + public Collection findAllPeople() { + return new TestDB().getAllPeople(); + // TODO: Need to figure out why CDI not working for + // return testDB.getAllPeople(); + } + + @Query + public LocalDate returnCurrentDate() { + return LocalDate.now(); + } + + @Query + @Name("returnMediumSize") + public EnumTestWithEnumName getEnumMedium() { + return EnumTestWithEnumName.M; + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java new file mode 100644 index 00000000000..d505fdc4071 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import javax.inject.Inject; +import javax.json.bind.annotation.JsonbProperty; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; +import io.helidon.microprofile.graphql.server.test.types.Car; +import io.helidon.microprofile.graphql.server.test.types.Person; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds simple query definitions with various numbers of arguments. + */ +@GraphQLApi +public class SimpleQueriesWithArgs { + + @Inject + private TestDB testDB; + + @Query + public String hero(@Name("heroType") String heroType) { + return "human".equalsIgnoreCase(heroType) + ? "Luke" + : "R2-D2"; + } + + @Query("multiply") + public long multiplyFunction(int number1, int number2) { + return number1 * number2; + } + + @Query + @Name("findAPerson") + public Person findPerson(@Name("personId") int personId) { + return new TestDB().getPerson(personId); + } + + // query with arguments as types, enums and scalars + // query with Collection, List and Map as return type + // query with ID as param + + @Query + public Collection findPeopleFromState(@Name("state") String state) { + return new TestDB().getAllPeople() + .stream().filter(p -> p.getHomeAddress().getState().equals(state)) + .collect(Collectors.toList()); + } + + @Query + public List findLocalDates(@Name("numberOfValues") int count) { + List localDates = new ArrayList<>(); + for (int i = 0; i < count; i++) { + localDates.add(LocalDate.now()); + } + return localDates; + } + +// @Query +// public boolean canFindCar(@Name("carToFind") Car car) { +// return false; +// } +// +// @Query +// @JsonbProperty("findEnums") +// public Collection findEnumValues(EnumTestWithNameAnnotation enum1) { +// return Arrays.stream(EnumTestWithNameAnnotation.values()).filter(e -> e.equals(enum1)).collect(Collectors.toList()); +// } + + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java index ee130494975..3dd0d6729a6 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java @@ -31,6 +31,7 @@ import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAndNameAnnotation; import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; import io.helidon.microprofile.graphql.server.test.types.Address; import io.helidon.microprofile.graphql.server.test.types.Car; import io.helidon.microprofile.graphql.server.test.types.InnerClass; @@ -76,7 +77,7 @@ public void testEnumGeneration() throws IntrospectionException, ClassNotFoundExc @Test public void testGettersPerson() throws IntrospectionException { - Map mapMethods = SchemaUtils.retrieveBeanMethods(Person.class); + Map mapMethods = SchemaUtils.retrieveGetterBeanMethods(Person.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(11)); @@ -96,7 +97,7 @@ public void testGettersPerson() throws IntrospectionException { @Test public void testMultipleLevels() throws IntrospectionException { - Map mapMethods = SchemaUtils.retrieveBeanMethods(Level0.class); + Map mapMethods = SchemaUtils.retrieveGetterBeanMethods(Level0.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); assertDiscoveredMethod(mapMethods.get("id"), "id", STRING, null, false, false, false); @@ -105,7 +106,7 @@ public void testMultipleLevels() throws IntrospectionException { @Test public void testTypeWithIdOnMethod() throws IntrospectionException { - Map mapMethods = SchemaUtils.retrieveBeanMethods(TypeWithIdOnMethod.class); + Map mapMethods = SchemaUtils.retrieveGetterBeanMethods(TypeWithIdOnMethod.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); assertDiscoveredMethod(mapMethods.get("name"), "name", STRING, null, false, false, false); @@ -114,7 +115,7 @@ public void testTypeWithIdOnMethod() throws IntrospectionException { @Test public void testTypeWithIdOnField() throws IntrospectionException { - Map mapMethods = SchemaUtils.retrieveBeanMethods(TypeWithIdOnField.class); + Map mapMethods = SchemaUtils.retrieveGetterBeanMethods(TypeWithIdOnField.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); assertDiscoveredMethod(mapMethods.get("name"), "name", STRING, null, false, false, false); @@ -124,7 +125,7 @@ public void testTypeWithIdOnField() throws IntrospectionException { @Test public void testTypeWithNameAndJsonbProperty() throws IntrospectionException { Map mapMethods = SchemaUtils - .retrieveBeanMethods(TypeWithNameAndJsonbProperty.class); + .retrieveGetterBeanMethods(TypeWithNameAndJsonbProperty.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(6)); assertDiscoveredMethod(mapMethods.get("newFieldName1"), "newFieldName1", STRING, null, false, false, false); @@ -137,7 +138,7 @@ public void testTypeWithNameAndJsonbProperty() throws IntrospectionException { @Test public void testInterfaceDiscovery() throws IntrospectionException { - Map mapMethods = SchemaUtils.retrieveBeanMethods(Vehicle.class); + Map mapMethods = SchemaUtils.retrieveGetterBeanMethods(Vehicle.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(6)); assertDiscoveredMethod(mapMethods.get("plate"), "plate", STRING, null, false, false, false); @@ -151,7 +152,7 @@ public void testInterfaceDiscovery() throws IntrospectionException { @Test public void testInterfaceImplementorDiscovery1() throws IntrospectionException { - Map mapMethods = SchemaUtils.retrieveBeanMethods(Car.class); + Map mapMethods = SchemaUtils.retrieveGetterBeanMethods(Car.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); assertDiscoveredMethod(mapMethods.get("plate"), "plate", STRING, null, false, false, false); @@ -166,7 +167,7 @@ public void testInterfaceImplementorDiscovery1() throws IntrospectionException { @Test public void testInterfaceImplementorDiscovery2() throws IntrospectionException { - Map mapMethods = SchemaUtils.retrieveBeanMethods(Motorbike.class); + Map mapMethods = SchemaUtils.retrieveGetterBeanMethods(Motorbike.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); assertDiscoveredMethod(mapMethods.get("plate"), "plate", STRING, null, false, false, false); @@ -207,6 +208,20 @@ public void testGetSimpleName() throws ClassNotFoundException { assertThat(schemaUtils.getSimpleName(SchemaUtils.FLOAT), is(SchemaUtils.FLOAT)); assertThat(schemaUtils.getSimpleName(SchemaUtils.STRING), is(SchemaUtils.STRING)); } + + @Test + public void testAllMethods() throws IntrospectionException { + Map mapMethods = SchemaUtils.retrieveAllAnnotatedBeanMethods(SimpleQueriesNoArgs.class); + assertThat(mapMethods, is(notNullValue())); + assertThat(mapMethods.size(), is(7)); + assertDiscoveredMethod(mapMethods.get("hero"), "hero", STRING, null, false, false, false); + assertDiscoveredMethod(mapMethods.get("episodeCount"), "episodeCount", "int", null, false, false, false); + assertDiscoveredMethod(mapMethods.get("numberOfStars"), "numberOfStars", Long.class.getName(), null, false, false, false); + assertDiscoveredMethod(mapMethods.get("badGuy"), "badGuy", STRING, null, false, false, false); + assertDiscoveredMethod(mapMethods.get("allPeople"), "allPeople", Person.class.getName(), COLLECTION, false, true, false); + assertDiscoveredMethod(mapMethods.get("returnCurrentDate"), "returnCurrentDate", LocalDate.class.getName(), null, false, false, false); + assertDiscoveredMethod(mapMethods.get("returnMediumSize"), "returnMediumSize", EnumTestWithEnumName.class.getName(), null, false, false, false); + } private void assertDiscoveredMethod(SchemaUtils.DiscoveredMethod discoveredMethod, String name, From 43e2512e56e96f3b5d0a24ee158dfac7918288e5 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 10 Mar 2020 13:03:16 +0800 Subject: [PATCH 008/178] progress --- microprofile/graphql/pom.xml | 38 ++++---- microprofile/graphql/server/pom.xml | 27 +----- .../graphql/server/model/SchemaArgument.java | 22 ++++- .../graphql/server/model/SchemaInputType.java | 2 + .../graphql/server/util/DataFetcherUtils.java | 35 +++++-- .../graphql/server/util/JsonUtils.java | 10 ++ .../graphql/server/util/SchemaUtils.java | 95 +++++++++++++++---- .../graphql/server/AbstractGraphQLIT.java | 1 - .../graphql/server/SchemaUtilsIT.java | 38 ++++++-- .../server/model/SchemaArgumentTest.java | 23 +++-- .../server/model/SchemaDirectiveTest.java | 17 ++-- .../model/SchemaFieldDefinitionTest.java | 35 +++---- .../graphql/server/model/SchemaTest.java | 4 +- .../graphql/server/model/SchemaTypeTest.java | 33 ++++--- .../test/queries/SimpleQueriesNoArgs.java | 6 +- .../test/queries/SimpleQueriesWithArgs.java | 80 ++++++++++++---- .../META-INF/microprofile-config.properties | 3 + 17 files changed, 319 insertions(+), 150 deletions(-) create mode 100644 microprofile/graphql/server/src/test/resources/META-INF/microprofile-config.properties diff --git a/microprofile/graphql/pom.xml b/microprofile/graphql/pom.xml index 49c441c8064..4fb0e367dd9 100644 --- a/microprofile/graphql/pom.xml +++ b/microprofile/graphql/pom.xml @@ -1,5 +1,4 @@ - - - - - io.helidon.microprofile - helidon-microprofile-project - 2.0.0-SNAPSHOT - - 4.0.0 - - io.helidon.microprofile.graphql - helidon-microprofile-graphql - Helidon Microprofile GraphQL - pom + + + io.helidon.microprofile + helidon-microprofile-project + 2.0.0-SNAPSHOT + + 4.0.0 + + io.helidon.microprofile.graphql + helidon-microprofile-graphql + Helidon Microprofile GraphQL + pom + + + server + - - server - - \ No newline at end of file diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index dc8d58c40f6..feb8b476120 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -40,10 +40,10 @@ com.graphql-java graphql-java-extended-scalars - - javax.enterprise - cdi-api - + + + + jakarta.json.bind jakarta.json.bind-api @@ -55,16 +55,6 @@ io.helidon.microprofile.server helidon-microprofile-server - - - org.jboss.spec.javax.el - jboss-el-api_3.0_spec - - - org.jboss.spec.javax.interceptor - jboss-interceptors-api_1.2_spec - - io.helidon.microprofile.config @@ -85,11 +75,6 @@ - - org.jboss.weld - weld-junit5 - test - io.helidon.microprofile.bundles internal-test-libs @@ -133,10 +118,6 @@ org.apache.maven.plugins maven-failsafe-plugin - - - --add-opens helidon.microprofile.graphql.server/io.helidon.microprofile.graphql.server.test.types=graphql.java - false **/*IT.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java index 4c6aa22c81c..f6ab4e25068 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java @@ -43,6 +43,11 @@ public class SchemaArgument extends AbstractDescriptiveElement */ private final Object defaultValue; + /** + * Original argument type before it was converted to a GraphQL representation. + */ + private final Class originalType; + /** * Construct a {@link SchemaArgument} instance. * @@ -50,12 +55,14 @@ public class SchemaArgument extends AbstractDescriptiveElement * @param argumentType type of the argument * @param isMandatory indicates if the argument is mandatory * @param defaultValue default value for the argument + * @param originalType original argument type before it was converted to a GraphQL representation. */ - public SchemaArgument(String argumentName, String argumentType, boolean isMandatory, Object defaultValue) { + public SchemaArgument(String argumentName, String argumentType, boolean isMandatory, Object defaultValue, Class originalType) { this.argumentName = argumentName; this.argumentType = argumentType; this.isMandatory = isMandatory; this.defaultValue = defaultValue; + this.originalType = originalType; } /** @@ -137,6 +144,15 @@ public Object getDefaultValue() { return defaultValue; } + /** + * Retrieve the original argument type. + * + * @return the original argument type + */ + public Class getOriginalType() { + return originalType; + } + @Override public String toString() { return "Argument{" @@ -144,6 +160,7 @@ public String toString() { + ", argumentType='" + argumentType + '\'' + ", isMandatory=" + isMandatory + ", defaultValue=" + defaultValue + + ", originalType=" + originalType + ", description='" + getDescription() + '\'' + '}'; } @@ -162,12 +179,13 @@ public boolean equals(Object o) { return isMandatory == schemaArgument.isMandatory && Objects.equals(argumentName, schemaArgument.argumentName) && Objects.equals(argumentType, schemaArgument.argumentType) + && Objects.equals(originalType, schemaArgument.originalType) && Objects.equals(getDescription(), schemaArgument.getDescription()) && Objects.equals(defaultValue, schemaArgument.defaultValue); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), argumentName, argumentType, isMandatory, defaultValue, getDescription()); + return Objects.hash(super.hashCode(), argumentName, argumentType, isMandatory, defaultValue, getDescription(), originalType); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java index d26c88bb797..bfdf8f77917 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java @@ -48,4 +48,6 @@ protected String getGraphQLName() { public String toString() { return "InputType" + toStringInternal(); } + + } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java index d088a932ded..3825c83a263 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java @@ -20,8 +20,10 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Map; import graphql.schema.DataFetcher; +import io.helidon.microprofile.graphql.server.model.SchemaArgument; /** * Utilities for working with {@link DataFetcher}s. @@ -36,24 +38,41 @@ private DataFetcherUtils() { /** * Create a new {@link DataFetcher} for a {@link Class} and {@link Method}. - * @param clazz {@link Class} - * @param method {@link Method} - * @param args Optional argument names - * @param value type + * + * @param clazz {@link Class} to call + * @param method {@link Method} to call + * @param args optional {@link SchemaArgument}s + * @param value type * @return a new {@link DataFetcher} */ - @SuppressWarnings("unchecked") - public static DataFetcher newMethodDataFetcher(Class clazz, Method method, String... args) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static DataFetcher newMethodDataFetcher(Class clazz, Method method, SchemaArgument... args) { return environment -> { Constructor constructor = clazz.getConstructor(); if (constructor == null) { throw new IllegalArgumentException("Class " + clazz.getName() - + " must have a no-args constructor"); + + " must have a no-args constructor"); } ArrayList listArgumentValues = new ArrayList<>(); if (args.length > 0) { - Arrays.stream(args).forEach(a -> listArgumentValues.add(environment.getArgument(a))); + for (SchemaArgument argument : args) { + Object key = environment.getArgument(argument.getArgumentName()); + if (key instanceof Map) { + // this means the type is an input type so convert it to the correct class instance + listArgumentValues.add(JsonUtils.convertFromJson(JsonUtils.convertMapToJson((Map) key), + argument.getOriginalType())); + } else { + // standard type or enum + Class originalType = argument.getOriginalType(); + if (originalType.isEnum()) { + Class enumClass = (Class) originalType; + listArgumentValues.add(Enum.valueOf(enumClass, key.toString())); + } else { + listArgumentValues.add(key); + } + } + } } return (V) method.invoke(constructor.newInstance(), listArgumentValues.toArray()); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java index eb4a02e09be..bde5560fa4a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java @@ -45,5 +45,15 @@ public static Map convertJSONtoMap(String json) { public static String convertMapToJson(Map map) { return JSONB.toJson(map); } + + /** + * Concert a Json object into the representative Java object + * @param json the json + * @param clazz {@link Class} to convert to + * @return a new {@link Class} instance + */ + public static Object convertFromJson(String json, Class clazz) { + return JSONB.fromJson(json, clazz); + } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java index a54d95c0964..4947d746e2e 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -43,6 +43,7 @@ import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.json.bind.annotation.JsonbProperty; @@ -153,6 +154,15 @@ public class SchemaUtils { put("[S", "short"); }}; + /** + * List of all Java primitive objects. + */ + private static final List JAVA_PRIMITIVE_OBJECTS = new ArrayList<>() {{ + addAll(STRING_LIST); + addAll(FLOAT_LIST); + addAll(INTEGER_LIST); + }}; + /** * GraphQL Int. */ @@ -294,15 +304,14 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec setUnresolvedTypes.remove(returnType); try { String simpleName = getSimpleName(returnType); - //String simpleName = getSimpleName(returnType); SchemaScalar scalar = getScalar(returnType); if (scalar != null) { if (!schema.containsScalarWithName(scalar.getName())) { schema.addScalar(scalar); - // update the return type with the scalar - updateLongTypes(schema, returnType, scalar.getName()); } + // update the return type with the scalar + updateLongTypes(schema, returnType, scalar.getName()); } else if (isEnumClass(returnType)) { SchemaEnum newEnum = generateEnum(Class.forName(returnType)); if (!schema.containsEnumWithName(simpleName)) { @@ -319,8 +328,6 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec // update any return types to the discovered scalars checkScalars(schema, newType); schema.addType(newType); - - // schema.addType(newType.createInputType("Input")); } // need to update any FieldDefinitions that contained the original "long" type of c updateLongTypes(schema, returnType, simpleName); @@ -394,30 +401,66 @@ private SchemaType generateType(String realReturnType) */ @SuppressWarnings("rawtypes") private void addRootQueriesToSchema(SchemaType schemaType, Schema schema, Class clazz) - throws IntrospectionException { + throws IntrospectionException, ClassNotFoundException { for (Map.Entry entry : retrieveAllAnnotatedBeanMethods(clazz).entrySet()) { DiscoveredMethod discoveredMethod = entry.getValue(); Method method = discoveredMethod.getMethod(); SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod, getMethodName(method)); - ArrayList argumentNames = new ArrayList<>(); - // add all the arguments and check to see if they contain types that are not yet known for (SchemaArgument a : discoveredMethod.getArguments()) { String originalTypeName = a.getArgumentType(); String typeName = getGraphQLType(originalTypeName); - a.setArgumentType(typeName); - fd.addArgument(a); - argumentNames.add(a.getArgumentName()); - // type name has not changed, so must require adding of unresolved type - if (originalTypeName.equals(a.getArgumentType())) { - setUnresolvedTypes.add(typeName); + a.setArgumentType(typeName); + String returnType = a.getArgumentType(); + + if (originalTypeName.equals(returnType)) { + // type name has not changed, so this must be either a Scalar, Enum or a Type + // Note: Interfaces are not currently supported as InputTypes in 1.0 of the Specification + // if is Scalar or enum then add to unresolved types and they will be dealt with + if (getScalar(returnType) != null || isEnumClass(returnType)) { + setUnresolvedTypes.add(returnType); + } else { + // create the input Type here + SchemaInputType inputType = generateType(returnType).createInputType("Input"); + schema.addInputType(inputType); + a.setArgumentType(inputType.getName()); + + // if this new Type contains any types, then they must also have + // InputTypes created for them if they are not enums or scalars + // Set setInputTypes = new HashSet<>(); + // setInputTypes.add(inputType); + // + // while (setInputTypes.size() > 0) { + // SchemaInputType type = setInputTypes.iterator().next(); + // setInputTypes.remove(type); + // // check each field definition to see if any return types are unknown + // InputTypes + // type.getFieldDefinitions().forEach(fdi -> { + // String fdReturnType = fdi.getReturnType(); + // String fdTypeName = getGraphQLType(fdReturnType); + // if (fdTypeName.equals(fdi.getReturnType())) { + // // has not changed so must be either an unknown input type or + // Scalar or enum + // if (getScalar(fdReturnType) != null || isEnumClass(fdReturnType)) { + // setUnresolvedTypes.add(fdReturnType); + // } + // else { + // SchemaInputType newInputType = generateType(fdReturnType) + // .createInputType("Input"); + // } + // } + // }); + // } + } } + fd.addArgument(a); } - DataFetcher dataFetcher = DataFetcherUtils.newMethodDataFetcher(clazz, method, argumentNames.toArray(new String[0])); + DataFetcher dataFetcher = DataFetcherUtils + .newMethodDataFetcher(clazz, method, fd.getArguments().toArray(new SchemaArgument[0])); fd.setDataFetcher(dataFetcher); schemaType.addFieldDefinition(fd); @@ -626,7 +669,10 @@ protected String getSimpleName(String className) * @param shortReturnType short return type */ private void updateLongTypes(Schema schema, String longReturnType, String shortReturnType) { - schema.getTypes().forEach(t -> { + // concatenate both the SchemaType and SchemaInputType + Stream streamInputTypes = schema.getInputTypes().stream().map(it -> (SchemaType) it); + Stream streamAll = Stream.concat(streamInputTypes, schema.getTypes().stream()); + streamAll.forEach(t -> { t.getFieldDefinitions().forEach(fd -> { if (fd.getReturnType().equals(longReturnType)) { fd.setReturnType(shortReturnType); @@ -841,12 +887,13 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class ? paramNameAnnotation.value() : parameter.getName(); DefaultValue defaultValueAnnotations = parameter.getAnnotation(DefaultValue.class); - // TODO: Add default value processing + // TODO: Add default value processing here Class paramType = parameter.getType(); ReturnType returnType = getReturnType(paramType, genericParameterTypes[i++]); - discoveredMethod.addArgument(new SchemaArgument(parameterName, returnType.getReturnClass(), false, null)); + discoveredMethod + .addArgument(new SchemaArgument(parameterName, returnType.getReturnClass(), false, null, paramType)); } } @@ -974,6 +1021,15 @@ protected static String getFieldName(Class clazz, String fieldName) { } } + /** + * Return the list of Java primitive types which are Objects. + * + * @return the list of Java primitive types + */ + public static List getJavaPrimitiveObjects() { + return JAVA_PRIMITIVE_OBJECTS; + } + /** * Defines discovered methods for a class. */ @@ -1146,7 +1202,7 @@ public void setArrayReturnType(boolean arrayReturnType) { } /** - * INdicates if the method is a collection type. + * Indicates if the method is a collection type. * * @return if the method is a collection type */ @@ -1183,6 +1239,7 @@ public List getArguments() { /** * Add a {@link SchemaArgument}. + * * @param argument a {@link SchemaArgument} */ public void addArgument(SchemaArgument argument) { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java index 3675a5a59cf..c6f10f34265 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -88,7 +88,6 @@ public static void _startupTest(Class... clazzes) throws IOException { Main.main(new String[0]); - // server = Server.create().start(); graphQLUrl= "http://127.0.0.1:7001/"; System.out.println("GraphQL URL: " + graphQLUrl); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index 5178b1fcc0a..47680ee169f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -19,17 +19,19 @@ import java.beans.IntrospectionException; import java.io.File; import java.io.IOException; -import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Map; +import javax.enterprise.inject.se.SeContainerInitializer; + import graphql.ExecutionResult; import io.helidon.microprofile.graphql.server.model.Schema; import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; @@ -44,8 +46,8 @@ import io.helidon.microprofile.graphql.server.util.SchemaUtils; import io.helidon.microprofile.graphql.server.util.SchemaUtilsTest; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -61,11 +63,15 @@ public class SchemaUtilsIT extends AbstractGraphQLTest { private File indexFile = null; private DummyContext dummyContext = new DummyContext("Dummy"); + @BeforeAll + public static void initialize() { + SeContainerInitializer seContainerInitializer = SeContainerInitializer.newInstance(); + } + @BeforeEach public void setupTest() throws IOException { System.clearProperty(JandexUtils.PROP_INDEX_FILE); indexFileName = getTempIndexFile(); - ; indexFile = null; } @@ -201,6 +207,11 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("hero"), is("Luke")); + result = executionContext.execute("query { canFindContact(contact: { id: \"10\" name: \"tim\" age: 52 }) }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("canFindContact"), is(false)); + result = executionContext.execute("query { hero(heroType: \"droid\") }"); mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); @@ -211,12 +222,14 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("multiply"), is(BigInteger.valueOf(100))); - result = executionContext.execute("query { findAPerson(personId: 1) { personId creditLimit workAddress { city state zipCode } } }"); + result = executionContext + .execute("query { findAPerson(personId: 1) { personId creditLimit workAddress { city state zipCode } } }"); mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("findAPerson"), is(notNullValue())); - result = executionContext.execute("query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }"); + result = executionContext.execute( + "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }"); mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("findPeopleFromState"), is(notNullValue())); @@ -232,11 +245,16 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { assertThat(mapResults.get("findLocalDates"), is(notNullValue())); List listLocalDate = (List) mapResults.get("findLocalDates"); assertThat(listLocalDate.size(), is(10)); -// -// result = executionContext.execute("query { findEnums(arg0: M) }"); -// mapResults = getAndAssertResult(result); -// assertThat(mapResults.size(), is(1)); -// assertThat(mapResults.get("findEnums"), is(notNullValue())); + + result = executionContext.execute("query { findEnums(arg0: S) }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findEnums"), is(notNullValue())); + + result = executionContext.execute("query { getMonthFromDate(date: \"2020-12-20\") }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("getMonthFromDate"), is("DECEMBER")); } private void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaArgumentTest.java index ccc2f59be33..231bb2cb13f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaArgumentTest.java @@ -27,19 +27,24 @@ */ class SchemaArgumentTest { + private static final Class STRING = String.class; + private static final Class INTEGER = Integer.class; + @Test public void testConstructors() { - SchemaArgument schemaArgument = new SchemaArgument("name", "Integer", true, null); + SchemaArgument schemaArgument = new SchemaArgument("name", "Integer", true, null, INTEGER); assertThat(schemaArgument.getArgumentName(), is("name")); assertThat(schemaArgument.getArgumentType(), is("Integer")); assertThat(schemaArgument.isMandatory(), is(true)); assertThat(schemaArgument.getDefaultValue(), is(nullValue())); + assertThat(schemaArgument.getOriginalType().getName(), is(Integer.class.getName())); - schemaArgument = new SchemaArgument("name2", "String", false, "Default"); + schemaArgument = new SchemaArgument("name2", "String", false, "Default", STRING); assertThat(schemaArgument.getArgumentName(), is("name2")); assertThat(schemaArgument.getArgumentType(), is("String")); assertThat(schemaArgument.isMandatory(), is(false)); assertThat(schemaArgument.getDefaultValue(), is("Default")); + assertThat(schemaArgument.getOriginalType().getName(), is(STRING.getName())); schemaArgument.setArgumentType("XYZ"); assertThat(schemaArgument.getArgumentType(), is("XYZ")); @@ -51,25 +56,25 @@ public void testConstructors() { @Test public void testSchemaGeneration() { - SchemaArgument schemaArgument = new SchemaArgument("name", "Integer", true, null); + SchemaArgument schemaArgument = new SchemaArgument("name", "Integer", true, null, INTEGER); assertThat(schemaArgument.getSchemaAsString(), is("name: Integer!")); - schemaArgument = new SchemaArgument("name", "Integer", true, 10); + schemaArgument = new SchemaArgument("name", "Integer", true, 10,INTEGER); assertThat(schemaArgument.getSchemaAsString(), is("name: Integer! = 10")); - schemaArgument = new SchemaArgument("name", "Integer", false, null); + schemaArgument = new SchemaArgument("name", "Integer", false, null,INTEGER); assertThat(schemaArgument.getSchemaAsString(), is("name: Integer")); - schemaArgument = new SchemaArgument("name", "Integer", false, 10); + schemaArgument = new SchemaArgument("name", "Integer", false, 10,INTEGER); assertThat(schemaArgument.getSchemaAsString(), is("name: Integer = 10")); - schemaArgument = new SchemaArgument("name", "String", false, "The Default Value"); + schemaArgument = new SchemaArgument("name", "String", false, "The Default Value", STRING); assertThat(schemaArgument.getSchemaAsString(), is("name: String = \"The Default Value\"")); - schemaArgument = new SchemaArgument("name", "String", true, "The Default Value"); + schemaArgument = new SchemaArgument("name", "String", true, "The Default Value", STRING); assertThat(schemaArgument.getSchemaAsString(), is("name: String! = \"The Default Value\"")); - schemaArgument = new SchemaArgument("name", "Integer", false, 10); + schemaArgument = new SchemaArgument("name", "Integer", false, 10, INTEGER); schemaArgument.setDescription("Description"); assertThat(schemaArgument.getSchemaAsString(), is("\"Description\"\nname: Integer = 10")); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaDirectiveTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaDirectiveTest.java index f46769f87c1..11739075abc 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaDirectiveTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaDirectiveTest.java @@ -29,7 +29,10 @@ * Tests for {@link SchemaScalar} class. */ class SchemaDirectiveTest { - + + private static final Class STRING = String.class; + private static final Class INTEGER = Integer.class; + @Test public void testConstructors() { SchemaDirective schemaDirective = new SchemaDirective("auth"); @@ -37,7 +40,7 @@ public void testConstructors() { assertThat(schemaDirective.getArguments().size(), is(0)); assertThat(schemaDirective.getLocations().size(), is(0)); - SchemaArgument arg = new SchemaArgument("name", "String", true, null); + SchemaArgument arg = new SchemaArgument("name", "String", true, null, STRING); schemaDirective.addArgument(arg); assertThat(schemaDirective.getArguments().contains(arg), is(true)); @@ -56,7 +59,7 @@ public void testDirectiveWith0Argument1Location() { @Test public void testDirectiveWith1Argument1Location() { - SchemaArgument arg = new SchemaArgument("name", "String", true, null); + SchemaArgument arg = new SchemaArgument("name", "String", true, null, STRING); SchemaDirective schemaDirective = new SchemaDirective("directiveName"); schemaDirective.addArgument(arg); schemaDirective.addLocation(FIELD_DEFINITION.name()); @@ -65,8 +68,8 @@ public void testDirectiveWith1Argument1Location() { @Test public void testDirectiveWith2Argument1Location() { - SchemaArgument arg1 = new SchemaArgument("name", "String", true, null); - SchemaArgument arg2 = new SchemaArgument("name1", "Int", false, null); + SchemaArgument arg1 = new SchemaArgument("name", "String", true, null, STRING); + SchemaArgument arg2 = new SchemaArgument("name1", "Int", false, null, INTEGER); SchemaDirective schemaDirective = new SchemaDirective("directiveName"); schemaDirective.addArgument(arg1); schemaDirective.addArgument(arg2); @@ -77,8 +80,8 @@ public void testDirectiveWith2Argument1Location() { @Test public void testDirectiveWith2Argument2Location() { - SchemaArgument arg1 = new SchemaArgument("name", "String", true, null); - SchemaArgument arg2 = new SchemaArgument("name1", "Int", false, null); + SchemaArgument arg1 = new SchemaArgument("name", "String", true, null, STRING); + SchemaArgument arg2 = new SchemaArgument("name1", "Int", false, null,INTEGER); SchemaDirective schemaDirective = new SchemaDirective("directiveName"); schemaDirective.addArgument(arg1); schemaDirective.addArgument(arg2); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinitionTest.java index b90cbb3c742..fa8843a24eb 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinitionTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinitionTest.java @@ -29,6 +29,9 @@ * Tests for {@link SchemaArgument} class. */ class SchemaFieldDefinitionTest { + + private static final Class STRING = String.class; + private static final Class INTEGER = Integer.class; @Test public void testConstructors() { @@ -38,12 +41,12 @@ public void testConstructors() { assertThat(schemaFieldDefinition.getArguments(), is(notNullValue())); assertThat(schemaFieldDefinition.isArrayReturnType(), is(true)); - SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null); + SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null, STRING); schemaFieldDefinition.addArgument(schemaArgument); assertThat(schemaFieldDefinition.getArguments().size(), is(1)); assertThat(schemaFieldDefinition.getArguments().get(0), is(schemaArgument)); - SchemaArgument schemaArgument2 = new SchemaArgument("filter2", "Integer", true, null); + SchemaArgument schemaArgument2 = new SchemaArgument("filter2", "Integer", true, null, INTEGER); schemaFieldDefinition.addArgument(schemaArgument2); assertThat(schemaFieldDefinition.getArguments().size(), is(2)); assertThat(schemaFieldDefinition.getArguments().contains(schemaArgument2), is(true)); @@ -80,14 +83,14 @@ public void testFieldDefinitionWithNoArgumentsAndArrayType() { @Test public void testFieldDefinitionWith1Argument() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): Person!")); } @Test public void testFieldDefinitionWith1ArgumentAndDescription() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null); + SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument.setDescription("Optional Filter"); schemaFieldDefinition.addArgument(schemaArgument); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\n\"Optional Filter\"\nfilter: String\n): Person!")); @@ -96,38 +99,38 @@ public void testFieldDefinitionWith1ArgumentAndDescription() { @Test public void testFieldDefinitionWith1ArgumentAndArrayType() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): [Person]!")); } @Test public void testFieldDefinitionWith1MandatoryArgument() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", true, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", true, null, STRING)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String!\n): Person!")); } @Test public void testFieldDefinitionWith1MandatoryArgumentAndArrayType() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): [Person]!")); } @Test public void testFieldDefinitionWithMultipleArguments() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String, \nage: Int!\n): Person!")); } @Test public void testFieldDefinitionWithMultipleArgumentsAndDescription() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null); + SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument1.setDescription("Optional filter"); - SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null); + SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); schemaArgument2.setDescription("Mandatory age"); schemaFieldDefinition.addArgument(schemaArgument1); schemaFieldDefinition.addArgument(schemaArgument2); @@ -139,9 +142,9 @@ public void testFieldDefinitionWithMultipleArgumentsAndDescription() { public void testFieldDefinitionWithMultipleArgumentsAndBothDescriptions() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); schemaFieldDefinition.setDescription("Description of field definition"); - SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null); + SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument1.setDescription("Optional filter"); - SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null); + SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); schemaArgument2.setDescription("Mandatory age"); schemaFieldDefinition.addArgument(schemaArgument1); schemaFieldDefinition.addArgument(schemaArgument2); @@ -153,9 +156,9 @@ public void testFieldDefinitionWithMultipleArgumentsAndBothDescriptions() { @Test public void testFieldDefinitionWithMultipleArgumentsWithArray() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, false); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null)); - schemaFieldDefinition.addArgument(new SchemaArgument("job", "String", false, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); + schemaFieldDefinition.addArgument(new SchemaArgument("job", "String", false, null, STRING)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String, \nage: Int!, \njob: String\n): [Person]")); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java index bbcd3dda662..49446d01138 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java @@ -36,6 +36,8 @@ */ class SchemaTest extends AbstractGraphQLTest { + private static final Class STRING = String.class; + @Test public void testEmptySchemaAsString() { Schema schema = new Schema(); @@ -56,7 +58,7 @@ public void testTopLevelSchemaAsString() { SchemaDirective schemaDirective = new SchemaDirective("format"); schemaDirective.addLocation(FIELD_DEFINITION.name()); - schemaDirective.addArgument(new SchemaArgument("dateFormat", "String", true, null)); + schemaDirective.addArgument(new SchemaArgument("dateFormat", "String", true, null, STRING)); schema.addDirective(schemaDirective); assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-04.txt"); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTypeTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTypeTest.java index 8ec290ed336..4668eb21f67 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTypeTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTypeTest.java @@ -27,6 +27,9 @@ * Tests for {@link SchemaType} and {@link SchemaInputType} classes. */ class SchemaTypeTest { + + private static final Class STRING = String.class; + private static final Class INTEGER = Integer.class; @Test public void testConstructors() { @@ -39,7 +42,7 @@ public void testConstructors() { schemaType.addFieldDefinition(new SchemaFieldDefinition("personId", "Integer", false, true)); schemaType.addFieldDefinition(new SchemaFieldDefinition("personId", "Integer", false, true)); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("orders", "Order", true, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); assertThat(schemaType.getFieldDefinitions().size(), is(4)); @@ -54,8 +57,8 @@ public void testConstructors() { public void testImplementingInterface() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); schemaType.setImplementingInterface("Contact"); @@ -68,7 +71,7 @@ public void testImplementingInterface() { public void testTypeSchemaOutput() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); assertThat(schemaType.getFieldDefinitions().get(0).equals(schemaFieldDefinition), is(true)); @@ -83,7 +86,7 @@ public void testTypeSchemaOutput() { public void testTypeSchemaOutputWithDescription() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); schemaType.setDescription("Type Description"); @@ -99,8 +102,8 @@ public void testTypeSchemaOutputWithDescription() { public void testTypeStringOutputWith2Arguments() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); assertThat(schemaType.getSchemaAsString(), is("type Person {\n" + @@ -112,11 +115,11 @@ public void testTypeStringOutputWith2Arguments() { public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null); + SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument1.setDescription("Argument1 Description"); schemaFieldDefinition.addArgument(schemaArgument1); - SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null); + SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); schemaArgument2.setDescription("Argument 2 Description"); schemaFieldDefinition.addArgument(schemaArgument2); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -130,8 +133,8 @@ public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { public void testTypeInterfaceStringOutputWith2Arguments() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, 30)); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, 30, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); schemaType.setIsInterface(true); assertThat(schemaType.getGraphQLName(), is("interface")); @@ -146,12 +149,12 @@ public void testTypeStringOutputWith2Fields() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); SchemaFieldDefinition schemaFieldDefinition2 = new SchemaFieldDefinition("people", "Person", true, false); - schemaFieldDefinition2.addArgument(new SchemaArgument("filter", "String", false, null)); - schemaFieldDefinition2.addArgument(new SchemaArgument("age", "Int", true, null)); + schemaFieldDefinition2.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition2.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition2); assertThat(schemaType.getSchemaAsString(), is("type Person {\n" + @@ -165,7 +168,7 @@ public void testCreatingInputTypeFromType() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); - schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null)); + schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); SchemaInputType inputType = schemaType.createInputType("Input"); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index afcda53b63b..8ce1a4c74fa 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -35,8 +35,8 @@ @GraphQLApi public class SimpleQueriesNoArgs { - @Inject - private TestDB testDB; + // @Inject + private TestDB testDB = new TestDB(); @Query public String hero() { @@ -62,7 +62,7 @@ public String getVillain() { @Query("allPeople") public Collection findAllPeople() { - return new TestDB().getAllPeople(); + return testDB.getAllPeople(); // TODO: Need to figure out why CDI not working for // return testDB.getAllPeople(); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java index d505fdc4071..b9261057687 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java @@ -28,7 +28,6 @@ import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; -import io.helidon.microprofile.graphql.server.test.types.Car; import io.helidon.microprofile.graphql.server.test.types.Person; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Name; @@ -40,8 +39,8 @@ @GraphQLApi public class SimpleQueriesWithArgs { - @Inject - private TestDB testDB; + // @Inject + private TestDB testDB = new TestDB(); @Query public String hero(@Name("heroType") String heroType) { @@ -58,16 +57,16 @@ public long multiplyFunction(int number1, int number2) { @Query @Name("findAPerson") public Person findPerson(@Name("personId") int personId) { - return new TestDB().getPerson(personId); + return testDB.getPerson(personId); } - // query with arguments as types, enums and scalars + // query with arguments a and scalars // query with Collection, List and Map as return type // query with ID as param @Query public Collection findPeopleFromState(@Name("state") String state) { - return new TestDB().getAllPeople() + return testDB.getAllPeople() .stream().filter(p -> p.getHomeAddress().getState().equals(state)) .collect(Collectors.toList()); } @@ -81,16 +80,65 @@ public List findLocalDates(@Name("numberOfValues") int count) { return localDates; } -// @Query -// public boolean canFindCar(@Name("carToFind") Car car) { -// return false; -// } -// -// @Query -// @JsonbProperty("findEnums") -// public Collection findEnumValues(EnumTestWithNameAnnotation enum1) { -// return Arrays.stream(EnumTestWithNameAnnotation.values()).filter(e -> e.equals(enum1)).collect(Collectors.toList()); -// } + @Query("getMonthFromDate") + public String returnDateAsLong(@Name("date") LocalDate localDate) { + return localDate.getMonth().toString(); + } + + @Query + public boolean canFindContact(@Name("contact") SimpleContact contact) { + return false; + } + + @Query + @JsonbProperty("findEnums") + public String findEnumValue(EnumTestWithNameAnnotation enum1) { + return enum1.name(); + } + + // Currently doesn't work as Car Input should also create Vehicle Input + // @Query + // public boolean canFindCar(@Name("carToFind") Car car) { + // return false; + // } + // + + public static class SimpleContact { + private String id; + private String name; + private int age; + + public SimpleContact(String id, String name, int age) { + this.id = id; + this.name = name; + this.age = age; + } + + public SimpleContact() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + } } diff --git a/microprofile/graphql/server/src/test/resources/META-INF/microprofile-config.properties b/microprofile/graphql/server/src/test/resources/META-INF/microprofile-config.properties new file mode 100644 index 00000000000..f3dbf2383a9 --- /dev/null +++ b/microprofile/graphql/server/src/test/resources/META-INF/microprofile-config.properties @@ -0,0 +1,3 @@ +# +mp.initializer.allow=true +mp.initializer.no-warn=true \ No newline at end of file From a0cccf77ae53e3212e616a8033ea9722e678b780 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 11 Mar 2020 08:24:06 +0800 Subject: [PATCH 009/178] add tck and runner modules --- dependencies/pom.xml | 6 + microprofile/graphql/pom.xml | 4 +- microprofile/graphql/runner/pom.xml | 113 ++++++++++++++++++ microprofile/graphql/server/pom.xml | 4 - .../application/GraphQLApplication.java | 3 +- .../graphql/server/model/SchemaArgument.java | 6 +- .../graphql/server/util/DataFetcherUtils.java | 41 +++++-- .../graphql/server/util/JandexUtils.java | 2 +- .../graphql/server/util/JsonUtils.java | 2 +- .../graphql/server/util/SchemaUtils.java | 58 +++++++-- .../server/src/main/resources/web/index.html | 2 +- .../graphql/server/GraphQLIT.java | 2 +- .../graphql/server/SchemaUtilsIT.java | 51 ++++++++ .../graphql/server/test/db/TestDB.java | 3 +- .../test/queries/SimpleQueriesNoArgs.java | 11 +- .../test/queries/SimpleQueriesWithArgs.java | 52 +++++++- .../graphql/server/test/types/Person.java | 13 +- .../graphql/server/util/JandexUtilsTest.java | 12 +- .../graphql/server/util/SchemaUtilsTest.java | 21 ++-- .../META-INF/microprofile-config.properties | 2 +- .../src/test/resources/logging.properties | 23 ++++ microprofile/graphql/tck/pom.xml | 83 +++++++++++++ 22 files changed, 459 insertions(+), 55 deletions(-) create mode 100644 microprofile/graphql/runner/pom.xml create mode 100644 microprofile/graphql/server/src/test/resources/logging.properties create mode 100644 microprofile/graphql/tck/pom.xml diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 4a6f2ba7a68..83c121105a1 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -87,6 +87,7 @@ 1.4 2.2 1.0 + 1.0 1.1.1 2.3.2 1.1.2 @@ -594,6 +595,11 @@ + + org.eclipse.microprofile.graphql + microprofile-graphql-tck + ${version.lib.microprofile-graphql-tck} + org.eclipse.microprofile.jwt microprofile-jwt-auth-api diff --git a/microprofile/graphql/pom.xml b/microprofile/graphql/pom.xml index 4fb0e367dd9..11e36cddfdb 100644 --- a/microprofile/graphql/pom.xml +++ b/microprofile/graphql/pom.xml @@ -30,6 +30,8 @@ server + tck + runner - \ No newline at end of file + diff --git a/microprofile/graphql/runner/pom.xml b/microprofile/graphql/runner/pom.xml new file mode 100644 index 00000000000..963d0df1f77 --- /dev/null +++ b/microprofile/graphql/runner/pom.xml @@ -0,0 +1,113 @@ + + + + + io.helidon.microprofile.graphql + helidon-microprofile-graphql + 2.0.0-SNAPSHOT + + 4.0.0 + + helidon-microprofile-graphql-runner + Helidon Microprofile GraphQL Runner + The microprofile GraphQL Runner + + + + io.helidon.microprofile.graphql + helidon-microprofile-graphql-server + 2.0.0-SNAPSHOT + + + org.eclipse.microprofile.graphql + microprofile-graphql-tck + + + + + ${project.artifactId} + + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + jandex + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + generate-sources + + unpack + + + + + org.eclipse.microprofile.graphql + microprofile-graphql-tck + jar + true + ${project.build.directory}/classes + + + **/tests/,**/dynamic/,**/*Test.class,**/beans.xml + + + + + + + + + + + runner + + + runner + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + java + + -classpath + + io.helidon.microprofile.cdi.Main + + + + + + + + diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index feb8b476120..2f182f7f4d3 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -40,10 +40,6 @@ com.graphql-java graphql-java-extended-scalars - - - - jakarta.json.bind jakarta.json.bind-api diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java index 2bfd04cf770..aee3615b088 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java @@ -16,6 +16,7 @@ package io.helidon.microprofile.graphql.server.application; +import java.util.Collections; import java.util.Set; import javax.enterprise.context.ApplicationScoped; @@ -30,7 +31,7 @@ public class GraphQLApplication extends Application { @Override public Set> getClasses() { - return Set.of( + return Collections.singleton( GraphQLResource.class ); } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java index f6ab4e25068..bb4b52b6a95 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java @@ -57,7 +57,8 @@ public class SchemaArgument extends AbstractDescriptiveElement * @param defaultValue default value for the argument * @param originalType original argument type before it was converted to a GraphQL representation. */ - public SchemaArgument(String argumentName, String argumentType, boolean isMandatory, Object defaultValue, Class originalType) { + public SchemaArgument(String argumentName, String argumentType, + boolean isMandatory, Object defaultValue, Class originalType) { this.argumentName = argumentName; this.argumentType = argumentType; this.isMandatory = isMandatory; @@ -186,6 +187,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), argumentName, argumentType, isMandatory, defaultValue, getDescription(), originalType); + return Objects.hash(super.hashCode(), argumentName, argumentType, + isMandatory, defaultValue, getDescription(), originalType); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java index 3825c83a263..ca54d45c6b1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java @@ -19,12 +19,17 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; import java.util.Map; +import java.util.UUID; + +import javax.enterprise.inject.spi.CDI; -import graphql.schema.DataFetcher; import io.helidon.microprofile.graphql.server.model.SchemaArgument; +import graphql.schema.DataFetcher; + +import static io.helidon.microprofile.graphql.server.util.SchemaUtils.ID; + /** * Utilities for working with {@link DataFetcher}s. */ @@ -47,12 +52,9 @@ private DataFetcherUtils() { */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static DataFetcher newMethodDataFetcher(Class clazz, Method method, SchemaArgument... args) { + Object instance = CDI.current().select(clazz).get(); + return environment -> { - Constructor constructor = clazz.getConstructor(); - if (constructor == null) { - throw new IllegalArgumentException("Class " + clazz.getName() - + " must have a no-args constructor"); - } ArrayList listArgumentValues = new ArrayList<>(); if (args.length > 0) { @@ -68,14 +70,35 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met if (originalType.isEnum()) { Class enumClass = (Class) originalType; listArgumentValues.add(Enum.valueOf(enumClass, key.toString())); + } else if (argument.getArgumentType().equals(ID)) { + // convert back to original data type + listArgumentValues.add(getOriginalValue(originalType, (String) key)); } else { listArgumentValues.add(key); } } } } - - return (V) method.invoke(constructor.newInstance(), listArgumentValues.toArray()); + + return (V) method.invoke(instance, listArgumentValues.toArray()); }; } + + /** + * Convert the ID type back to the original type for the method call. + * @param originalType original type + * @param key the key value passed in + * @return the value as the original type + */ + private static Object getOriginalValue(Class originalType, String key) { + if (originalType.equals(Long.class) || originalType.equals(long.class)) { + return Long.parseLong(key); + } else if (originalType.equals(Integer.class) || originalType.equals(int.class)) { + return Integer.parseInt(key); + } else if (originalType.equals(java.util.UUID.class)) { + return UUID.fromString(key); + } else { + return key; + } + } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java index 5cae33a589a..833be0b90cc 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java @@ -73,7 +73,7 @@ public void loadIndex() { if (file.isAbsolute()) { actualFile = indexFile; } else { - URL resource = JandexUtils.class.getResource(indexFile); + URL resource = JandexUtils.class.getClassLoader().getResource(indexFile); if (resource == null) { return; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java index bde5560fa4a..af277e84f9d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java @@ -47,7 +47,7 @@ public static String convertMapToJson(Map map) { } /** - * Concert a Json object into the representative Java object + * Concert a Json object into the representative Java object. * @param json the json * @param clazz {@link Class} to convert to * @return a new {@link Class} instance diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java index 4947d746e2e..11310397f16 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -55,7 +55,6 @@ import io.helidon.microprofile.graphql.server.model.SchemaScalar; import io.helidon.microprofile.graphql.server.model.SchemaType; -import graphql.GraphQLException; import graphql.Scalars; import graphql.scalars.ExtendedScalars; import graphql.schema.DataFetcher; @@ -94,7 +93,7 @@ public class SchemaUtils { put(LocalDate.class.getName(), new SchemaScalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); put(BigDecimal.class.getName(), new SchemaScalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); put(BigInteger.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); - put("long", new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + put(long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); put(Long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); }}; @@ -188,6 +187,16 @@ public class SchemaUtils { */ protected static final String BOOLEAN = "Boolean"; + /** + * Class for long primitive. + */ + protected static final String LONG_PRIMITIVE = long.class.getName(); + + /** + * Class for Long object. + */ + protected static final String LONG_OBJECT = Long.class.getName(); + /** * {@link JandexUtils} instance to hold indexes. */ @@ -218,6 +227,9 @@ public SchemaUtils() { * @return a {@link Schema} */ public Schema generateSchema() throws IntrospectionException, ClassNotFoundException { + if (!jandexUtils.hasIndex()) { + return generateSchemaFromClasses(); + } List> listClasses = jandexUtils.getIndex() .getKnownClasses() @@ -243,6 +255,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec SchemaType rootQueryType = new SchemaType(schema.getQueryName(), null); for (Class clazz : clazzes) { + LOGGER.info("processing class " + clazz.getName()); // only include interfaces and concrete classes/enums if (clazz.isInterface() || (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()))) { // Discover Enum via annotation @@ -259,7 +272,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec if (typeAnnotation != null || interfaceAnnotation != null) { // interface or type if (interfaceAnnotation != null && !clazz.isInterface()) { - throw new GraphQLException("Class " + clazz.getName() + " has been annotated with" + throw new RuntimeException("Class " + clazz.getName() + " has been annotated with" + " @Interface but is not one"); } @@ -303,6 +316,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec setUnresolvedTypes.remove(returnType); try { + LOGGER.info("Checking unresolved type " + returnType); String simpleName = getSimpleName(returnType); SchemaScalar scalar = getScalar(returnType); @@ -369,12 +383,12 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec * @throws ClassNotFoundException */ private SchemaType generateType(String realReturnType) - throws IntrospectionException, ClassNotFoundException { + throws IntrospectionException, ClassNotFoundException { String simpleName = getSimpleName(realReturnType); SchemaType type = new SchemaType(simpleName, realReturnType); Description descriptionAnnotation = Class.forName(realReturnType).getAnnotation(Description.class); - if (descriptionAnnotation != null && !descriptionAnnotation.value().isBlank()) { + if (descriptionAnnotation != null && !"".equals(descriptionAnnotation.value())) { type.setDescription(descriptionAnnotation.value()); } @@ -385,6 +399,8 @@ private SchemaType generateType(String realReturnType) type.addFieldDefinition(fd); if (discoveredMethod.getReturnType().equals(fd.getReturnType())) { + LOGGER.info("Adding unresolved type of " + valueTypeName + " for method " + + discoveredMethod.getName() + " on type " + realReturnType); // value class was unchanged meaning we need to resolve setUnresolvedTypes.add(valueTypeName); } @@ -416,7 +432,7 @@ private void addRootQueriesToSchema(SchemaType schemaType, Schema schema, Class< a.setArgumentType(typeName); String returnType = a.getArgumentType(); - if (originalTypeName.equals(returnType)) { + if (originalTypeName.equals(returnType) && !ID.equals(returnType)) { // type name has not changed, so this must be either a Scalar, Enum or a Type // Note: Interfaces are not currently supported as InputTypes in 1.0 of the Specification // if is Scalar or enum then add to unresolved types and they will be dealt with @@ -602,7 +618,7 @@ private void checkScalars(Schema schema, SchemaType type) { */ protected String getNameAnnotationValue(Class clazz) { Name nameAnnotation = clazz.getAnnotation(Name.class); - if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { + if (nameAnnotation != null && !"".equals(nameAnnotation.value())) { return nameAnnotation.value(); } return null; @@ -634,7 +650,7 @@ protected String getTypeName(Class clazz) { name = queryAnnotation.value(); } - if (name.isBlank()) { + if ("".equals(name)) { name = getNameAnnotationValue(clazz); } @@ -652,7 +668,8 @@ protected String getSimpleName(String className) throws ClassNotFoundException { if (INT.equals(className) || FLOAT.equals(className) || ID.equals(className) - || STRING.equals(className) || BOOLEAN.equalsIgnoreCase(className)) { + || STRING.equals(className) || BOOLEAN.equalsIgnoreCase(className) + || LONG_OBJECT.equals(className) || LONG_PRIMITIVE.equals(className)) { return className; } // return the type name taking into account any annotations @@ -668,6 +685,7 @@ protected String getSimpleName(String className) * @param longReturnType long return type * @param shortReturnType short return type */ + @SuppressWarnings("unchecked") private void updateLongTypes(Schema schema, String longReturnType, String shortReturnType) { // concatenate both the SchemaType and SchemaInputType Stream streamInputTypes = schema.getInputTypes().stream().map(it -> (SchemaType) it); @@ -865,6 +883,9 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class } if (fieldHasIdAnnotation || method.getAnnotation(Id.class) != null) { + if (!isValidIDType(returnClazz)) { + throw new RuntimeException("A class of type " + returnClazz + " is not allowed to be an @Id"); + } returnClazzName = ID; } } @@ -888,9 +909,16 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class : parameter.getName(); DefaultValue defaultValueAnnotations = parameter.getAnnotation(DefaultValue.class); // TODO: Add default value processing here + Class paramType = parameter.getType(); ReturnType returnType = getReturnType(paramType, genericParameterTypes[i++]); + if (parameter.getAnnotation(Id.class) != null) { + if (!isValidIDType(paramType)) { + throw new RuntimeException("A class of type " + paramType + " is not allowed to be an @Id"); + } + returnType.setReturnClass(ID); + } discoveredMethod .addArgument(new SchemaArgument(parameterName, returnType.getReturnClass(), false, null, paramType)); @@ -913,6 +941,18 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class return discoveredMethod; } + /** + * Return true if the {@link Class} is a valid {@link Class} to apply the {@link Id} annotation. + * + * @param clazz {@link Class} to check + * @return true if the {@link Class} is a valid {@link Class} to apply the {@link Id} annotation + */ + private static boolean isValidIDType(Class clazz) { + return clazz.equals(Long.class) || clazz.equals(Integer.class) + || clazz.equals(java.util.UUID.class) || clazz.equals(int.class) + || clazz.equals(String.class) || clazz.equals(long.class); + } + /** * Return the {@link ReturnType} for this return class and method. * diff --git a/microprofile/graphql/server/src/main/resources/web/index.html b/microprofile/graphql/server/src/main/resources/web/index.html index dc15c5eb181..e389f4c8892 100644 --- a/microprofile/graphql/server/src/main/resources/web/index.html +++ b/microprofile/graphql/server/src/main/resources/web/index.html @@ -20,7 +20,7 @@ - Coherence GraphiQL Interface + Helidon GraphiQL Interface diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java index 178c204f99b..e8add8ec3af 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java @@ -48,7 +48,7 @@ public static void setup() throws IOException { } @Test - public void basicEndpointTests() throws InterruptedException, JsonProcessingException, UnsupportedEncodingException { + public void basicEndpointTests() throws UnsupportedEncodingException { // test /graphql endpoint WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL); Map mapRequest = generateJsonRequest(QUERY_INTROSPECT, null, null); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index 47680ee169f..ac2861fa563 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -22,9 +22,12 @@ import java.math.BigInteger; import java.time.LocalDate; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.UUID; +import javax.enterprise.inject.se.SeContainer; import javax.enterprise.inject.se.SeContainerInitializer; import graphql.ExecutionResult; @@ -45,6 +48,7 @@ import io.helidon.microprofile.graphql.server.util.JandexUtils; import io.helidon.microprofile.graphql.server.util.SchemaUtils; import io.helidon.microprofile.graphql.server.util.SchemaUtilsTest; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -63,9 +67,17 @@ public class SchemaUtilsIT extends AbstractGraphQLTest { private File indexFile = null; private DummyContext dummyContext = new DummyContext("Dummy"); + private static SeContainer container; + @BeforeAll public static void initialize() { SeContainerInitializer seContainerInitializer = SeContainerInitializer.newInstance(); + container = seContainerInitializer.initialize(); + } + + @AfterAll + public static void teardown() { + container.close(); } @BeforeEach @@ -255,6 +267,45 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("getMonthFromDate"), is("DECEMBER")); + + result = executionContext.execute("query { findOneEnum(enum: XL) }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + Collection listEnums = (Collection) mapResults.get("findOneEnum"); + assertThat(listEnums.size(), is(1)); + assertThat(listEnums.iterator().next(), is(EnumTestWithNameAnnotation.XL.toString())); + + result = executionContext.execute("query { returnIntegerAsId(param1: 123) }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnIntegerAsId"), is(123)); + + result = executionContext.execute("query { returnIntAsId(param1: 124) }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnIntAsId"), is(124)); + + result = executionContext.execute("query { returnStringAsId(param1: \"StringValue\") }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnStringAsId"), is("StringValue")); + + result = executionContext.execute("query { returnLongAsId(param1: " + Long.MAX_VALUE + ") }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnLongAsId"), is(BigInteger.valueOf(Long.MAX_VALUE))); + + result = executionContext.execute("query { returnLongPrimitiveAsId(param1: " + Long.MAX_VALUE + ") }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnLongPrimitiveAsId"), is(BigInteger.valueOf(Long.MAX_VALUE))); + + UUID uuid = UUID.randomUUID(); + result = executionContext.execute("query { returnUUIDAsId(param1: \"" + uuid.toString() + "\") }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnUUIDAsId"), is(uuid.toString())); + } private void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java index 198e008296c..5f56ebc355d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java @@ -33,6 +33,7 @@ * An injectable datasource for integration tests. */ @ApplicationScoped +@SuppressWarnings("unchecked") public class TestDB { /** @@ -73,7 +74,7 @@ public TestDB() { Person person = new Person(i, "Person " + i, homeAddress, workAddress, new BigDecimal(RANDOM.nextFloat()), List.of("BA", "BA Hon"), List.of(prevAddress1, prevAddress2), new int[0], new String[0], EMPTY_MAP, - LocalDate.now()); + LocalDate.now(), System.nanoTime()); allPeople.put(person.getPersonId(), person); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index 8ce1a4c74fa..b5e32c6cdfa 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -19,6 +19,7 @@ import java.time.LocalDate; import java.util.Collection; +import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.json.bind.annotation.JsonbProperty; @@ -33,10 +34,14 @@ * Class that holds simple query definitions with no-argument. */ @GraphQLApi +@ApplicationScoped public class SimpleQueriesNoArgs { - // @Inject - private TestDB testDB = new TestDB(); + public SimpleQueriesNoArgs() { + } + + @Inject + private TestDB testDB; @Query public String hero() { @@ -69,7 +74,7 @@ public Collection findAllPeople() { @Query public LocalDate returnCurrentDate() { - return LocalDate.now(); + return LocalDate.now(); } @Query diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java index b9261057687..b1aa6b7cd38 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java @@ -20,9 +20,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; +import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.json.bind.annotation.JsonbProperty; @@ -30,6 +33,7 @@ import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; import io.helidon.microprofile.graphql.server.test.types.Person; import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Id; import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.Query; @@ -37,10 +41,45 @@ * Class that holds simple query definitions with various numbers of arguments. */ @GraphQLApi +@ApplicationScoped public class SimpleQueriesWithArgs { - // @Inject - private TestDB testDB = new TestDB(); + public SimpleQueriesWithArgs() { + } + + @Inject + private TestDB testDB; + + // tests for ID + @Query + public Integer returnIntegerAsId(@Name("param1") @Id Integer value) { + return value; + } + + @Query + public Integer returnIntAsId(@Name("param1") @Id int value) { + return value; + } + + @Query + public String returnStringAsId(@Name("param1") @Id String value) { + return value; + } + + @Query + public Long returnLongAsId(@Name("param1") @Id Long value) { + return value; + } + + @Query + public long returnLongPrimitiveAsId(@Name("param1") @Id long value) { + return value; + } + + @Query + public UUID returnUUIDAsId(@Name("param1") @Id UUID value) { + return value; + } @Query public String hero(@Name("heroType") String heroType) { @@ -60,10 +99,6 @@ public Person findPerson(@Name("personId") int personId) { return testDB.getPerson(personId); } - // query with arguments a and scalars - // query with Collection, List and Map as return type - // query with ID as param - @Query public Collection findPeopleFromState(@Name("state") String state) { return testDB.getAllPeople() @@ -85,6 +120,11 @@ public String returnDateAsLong(@Name("date") LocalDate localDate) { return localDate.getMonth().toString(); } + @Query + public Collection findOneEnum(@Name("enum") EnumTestWithNameAnnotation enum1) { + return Collections.singleton(enum1); + } + @Query public boolean canFindContact(@Name("contact") SimpleContact contact) { return false; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java index 64aae8410a9..a457ecc608b 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java @@ -41,6 +41,7 @@ public class Person { private String[] stringArray; private Map addressMap; private LocalDate localDate; + private long longValue; public Person(int personId, String name, @@ -51,7 +52,7 @@ public Person(int personId, List
previousAddresses, int[] intArray, String[] stringArray, - Map addressMap, LocalDate localDate) { + Map addressMap, LocalDate localDate, long longValue) { this.personId = personId; this.name = name; this.homeAddress = homeAddress; @@ -63,6 +64,15 @@ public Person(int personId, this.stringArray = stringArray; this.addressMap = addressMap; this.localDate = localDate; + this.longValue = longValue; + } + + public long getLongValue() { + return longValue; + } + + public void setLongValue(long longValue) { + this.longValue = longValue; } public int getPersonId() { @@ -166,6 +176,7 @@ public String toString() { ", intArray=" + Arrays.toString(intArray) + ", stringArray=" + Arrays.toString(stringArray) + ", addressMap=" + addressMap + + ", longValue=" + longValue + ", localDate=" + localDate + '}'; } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java index 85bfb409c4f..8c603393a4f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java @@ -18,6 +18,7 @@ import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Set; import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; @@ -69,11 +70,12 @@ public void testLoadingCustomIndexFile() throws IOException, ClassNotFoundExcept assertThat(utils.hasIndex(), is(true)); Index index = utils.getIndex(); - Set.of(String.class, Double.class, Integer.class).forEach(c -> { - ClassInfo classByName = index.getClassByName(DotName.createSimple(c.getName())); - assertThat(classByName, is(notNullValue())); - assertThat(classByName.toString(), is(c.getName())); - }); + Arrays.stream(new Class[] { String.class, Double.class, Integer.class }) + .forEach(c -> { + ClassInfo classByName = index.getClassByName(DotName.createSimple(c.getName())); + assertThat(classByName, is(notNullValue())); + assertThat(classByName.toString(), is(c.getName())); + }); } finally { if (indexFile != null) { new File(indexFile).delete(); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java index 3dd0d6729a6..ded2ffcf036 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java @@ -19,10 +19,10 @@ import java.beans.IntrospectionException; import java.math.BigDecimal; import java.time.LocalDate; +import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.Map; import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; import io.helidon.microprofile.graphql.server.model.SchemaEnum; @@ -79,7 +79,7 @@ public void testEnumGeneration() throws IntrospectionException, ClassNotFoundExc public void testGettersPerson() throws IntrospectionException { Map mapMethods = SchemaUtils.retrieveGetterBeanMethods(Person.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(11)); + assertThat(mapMethods.size(), is(12)); assertDiscoveredMethod(mapMethods.get("personId"), "personId", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("name"), "name", STRING, null, false, false, false); @@ -93,6 +93,7 @@ public void testGettersPerson() throws IntrospectionException { assertDiscoveredMethod(mapMethods.get("stringArray"), "stringArray", STRING, null, true, false, false); assertDiscoveredMethod(mapMethods.get("addressMap"), "addressMap", ADDRESS, null, false, false, true); assertDiscoveredMethod(mapMethods.get("localDate"), "localDate", LOCALDATE, null, false, false, false); + assertDiscoveredMethod(mapMethods.get("longValue"), "longValue", long.class.getName(), null, false, false, false); } @Test @@ -211,7 +212,8 @@ public void testGetSimpleName() throws ClassNotFoundException { @Test public void testAllMethods() throws IntrospectionException { - Map mapMethods = SchemaUtils.retrieveAllAnnotatedBeanMethods(SimpleQueriesNoArgs.class); + Map mapMethods = SchemaUtils + .retrieveAllAnnotatedBeanMethods(SimpleQueriesNoArgs.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); assertDiscoveredMethod(mapMethods.get("hero"), "hero", STRING, null, false, false, false); @@ -219,10 +221,12 @@ public void testAllMethods() throws IntrospectionException { assertDiscoveredMethod(mapMethods.get("numberOfStars"), "numberOfStars", Long.class.getName(), null, false, false, false); assertDiscoveredMethod(mapMethods.get("badGuy"), "badGuy", STRING, null, false, false, false); assertDiscoveredMethod(mapMethods.get("allPeople"), "allPeople", Person.class.getName(), COLLECTION, false, true, false); - assertDiscoveredMethod(mapMethods.get("returnCurrentDate"), "returnCurrentDate", LocalDate.class.getName(), null, false, false, false); - assertDiscoveredMethod(mapMethods.get("returnMediumSize"), "returnMediumSize", EnumTestWithEnumName.class.getName(), null, false, false, false); + assertDiscoveredMethod(mapMethods.get("returnCurrentDate"), "returnCurrentDate", LocalDate.class.getName(), null, false, + false, false); + assertDiscoveredMethod(mapMethods.get("returnMediumSize"), "returnMediumSize", EnumTestWithEnumName.class.getName(), null, + false, false, false); } - + private void assertDiscoveredMethod(SchemaUtils.DiscoveredMethod discoveredMethod, String name, String returnType, @@ -250,7 +254,8 @@ private void testEnum(Class clazz, String expectedName) throws IntrospectionE assertThat(schemaEnumResult, is(notNullValue())); assertThat(schemaEnumResult.getValues().size(), is(6)); - Set.of("S", "M", "L", "XL", "XXL", "XXXL").forEach(v -> assertThat(schemaEnumResult.getValues().contains(v), is(true))); + Arrays.stream(new String[] { "S", "M", "L", "XL", "XXL", "XXXL" }) + .forEach(v -> assertThat(schemaEnumResult.getValues().contains(v), is(true))); } } diff --git a/microprofile/graphql/server/src/test/resources/META-INF/microprofile-config.properties b/microprofile/graphql/server/src/test/resources/META-INF/microprofile-config.properties index f3dbf2383a9..0e70e50c2bd 100644 --- a/microprofile/graphql/server/src/test/resources/META-INF/microprofile-config.properties +++ b/microprofile/graphql/server/src/test/resources/META-INF/microprofile-config.properties @@ -1,3 +1,3 @@ # mp.initializer.allow=true -mp.initializer.no-warn=true \ No newline at end of file +mp.initializer.no-warn=true diff --git a/microprofile/graphql/server/src/test/resources/logging.properties b/microprofile/graphql/server/src/test/resources/logging.properties new file mode 100644 index 00000000000..81796123bfb --- /dev/null +++ b/microprofile/graphql/server/src/test/resources/logging.properties @@ -0,0 +1,23 @@ +# +# Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. +# +# 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. +# + +.level=INFO + +handlers = java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n diff --git a/microprofile/graphql/tck/pom.xml b/microprofile/graphql/tck/pom.xml new file mode 100644 index 00000000000..76ffc5543c5 --- /dev/null +++ b/microprofile/graphql/tck/pom.xml @@ -0,0 +1,83 @@ + + + + + io.helidon.microprofile.graphql + helidon-microprofile-graphql + 2.0.0-SNAPSHOT + + 4.0.0 + + helidon-microprofile-graphql-tck + Helidon Microprofile GraphQL TCK + The microprofile GraphQL TCK implementation + + + + io.helidon.microprofile.graphql + helidon-microprofile-graphql-server + 2.0.0-SNAPSHOT + + + org.eclipse.microprofile.graphql + microprofile-graphql-tck + test + + + + + io.helidon.microprofile.bundles + internal-test-libs + test + + + + + + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + jandex + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.eclipse.microprofile.graphql:microprofile-graphql-tck + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + + true + + + + + From 85718d094b90ff9b5cb2928c83458cc4dc6675dd Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 11 Mar 2020 11:52:07 +0800 Subject: [PATCH 010/178] fixup issues with ID fields --- .../graphql/server/util/SchemaUtils.java | 13 ++- .../graphql/server/SchemaUtilsIT.java | 55 ++++++++- .../graphql/server/test/db/TestDB.java | 2 +- .../test/queries/SimpleQueriesNoArgs.java | 22 +++- .../graphql/server/test/types/Level0.java | 3 + .../graphql/server/test/types/Level1.java | 16 ++- .../graphql/server/test/types/Level2.java | 3 + .../test/types/MultiLevelListsAndArrays.java | 68 +++++++++++ .../graphql/server/test/types/Person.java | 13 ++- .../server/test/types/TypeWithIDs.java | 107 ++++++++++++++++++ .../graphql/server/util/SchemaUtilsTest.java | 24 +++- 11 files changed, 313 insertions(+), 13 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithIDs.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java index 11310397f16..13e54751e48 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java @@ -398,7 +398,7 @@ private SchemaType generateType(String realReturnType) SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod, null); type.addFieldDefinition(fd); - if (discoveredMethod.getReturnType().equals(fd.getReturnType())) { + if (!ID.equals(valueTypeName) && valueTypeName.equals(fd.getReturnType())) { LOGGER.info("Adding unresolved type of " + valueTypeName + " for method " + discoveredMethod.getName() + " on type " + realReturnType); // value class was unchanged meaning we need to resolve @@ -513,7 +513,7 @@ private void addTypeToSchema(Schema schema, SchemaType type) String returnType = v.getReturnType(); // check to see if this is a known type - if (returnType.equals(fd.getReturnType()) && !setUnresolvedTypes.contains(returnType)) { + if (!ID.equals(returnType) && returnType.equals(fd.getReturnType()) && !setUnresolvedTypes.contains(returnType)) { // value class was unchanged meaning we need to resolve setUnresolvedTypes.add(returnType); } @@ -967,7 +967,14 @@ private static ReturnType getReturnType(Class returnClazz, java.lang.reflect. actualReturnType.setCollectionType(returnClazzName); if (genericReturnType instanceof ParameterizedType) { ParameterizedType paramReturnType = (ParameterizedType) genericReturnType; - actualReturnType.setReturnClass(paramReturnType.getActualTypeArguments()[0].getTypeName()); + // loop until we get the actual return type in the case we have List> + java.lang.reflect.Type actualTypeArgument = paramReturnType.getActualTypeArguments()[0]; + while (actualTypeArgument instanceof ParameterizedType) { + ParameterizedType parameterizedType2 = (ParameterizedType) actualTypeArgument; + actualTypeArgument = parameterizedType2.getActualTypeArguments()[0]; + } + + actualReturnType.setReturnClass(actualTypeArgument.getTypeName()); } else { actualReturnType.setReturnClass(Object.class.getName()); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index ac2861fa563..e4b5c620c45 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -41,11 +41,14 @@ import io.helidon.microprofile.graphql.server.test.types.Car; import io.helidon.microprofile.graphql.server.test.types.Level0; import io.helidon.microprofile.graphql.server.test.types.Motorbike; +import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.PersonWithName; +import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; import io.helidon.microprofile.graphql.server.test.types.Vehicle; import io.helidon.microprofile.graphql.server.test.types.VehicleIncident; import io.helidon.microprofile.graphql.server.util.JandexUtils; +import io.helidon.microprofile.graphql.server.util.JsonUtils; import io.helidon.microprofile.graphql.server.util.SchemaUtils; import io.helidon.microprofile.graphql.server.util.SchemaUtilsTest; import org.junit.jupiter.api.AfterAll; @@ -105,8 +108,30 @@ public void testTypeGenerationWithNoName() throws IOException, IntrospectionExce Schema schema = schemaUtils.generateSchema(); assertThat(schema.getTypeByName("Person"), is(notNullValue())); assertThat(schema.getTypeByName("Address"), is(notNullValue())); - assertThat(schema.getScalars().contains("Date"), is(notNullValue())); - assertThat(schema.getScalars().contains("BigDecimal"), is(notNullValue())); + assertThat(schema.containsScalarWithName("Date"), is(notNullValue())); + assertThat(schema.containsScalarWithName("BigDecimal"), is(notNullValue())); + generateGraphQLSchema(schema); + } + + @Test + public void testLevel0() throws IOException, IntrospectionException, ClassNotFoundException { + setupIndex(indexFileName, Level0.class); + SchemaUtils schemaUtils = new SchemaUtils(); + Schema schema = schemaUtils.generateSchema(); + assertThat(schema.containsTypeWithName("Level0"), is(true)); + assertThat(schema.containsTypeWithName("Level1"), is(true)); + assertThat(schema.containsTypeWithName("Level2"), is(true)); + generateGraphQLSchema(schema); + } + + @Test + public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassNotFoundException, IOException { + setupIndex(indexFileName, MultiLevelListsAndArrays.class); + SchemaUtils schemaUtils = new SchemaUtils(); + Schema schema = schemaUtils.generateSchema(); + assertThat(schema.containsTypeWithName("MultiLevelListsAndArrays"), is(true)); + assertThat(schema.containsTypeWithName("Person"), is(true)); + assertThat(schema.containsScalarWithName("BigDecimal"), is(true)); generateGraphQLSchema(schema); } @@ -168,6 +193,13 @@ public void testInterfaceDiscoveryWithoutTypes() throws IOException, Introspecti assertInterfaceResults(); } + @Test + public void testIds() throws IOException { + setupIndex(indexFileName, TypeWithIDs.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + ExecutionResult result = executionContext.execute("query { hero }"); + } + @Test @SuppressWarnings("unchecked") public void testSimpleQueryGenerationNoArgs() throws IOException { @@ -206,6 +238,25 @@ public void testSimpleQueryGenerationNoArgs() throws IOException { mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("returnCurrentDate"), is(notNullValue())); + + result = executionContext + .execute("query { returnTypeWithIDs { intId integerId longId longPrimitiveId stringId uuidId } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + Map results = (Map) mapResults.get("returnTypeWithIDs"); + TypeWithIDs value = (TypeWithIDs) JsonUtils.convertFromJson(JsonUtils.convertMapToJson(results), TypeWithIDs.class); + assertThat(value, is(notNullValue())); + assertThat(value.getIntegerId(), is(2)); + assertThat(value.getIntId(), is(1)); + assertThat(value.getLongId(), is(10L)); + assertThat(value.getLongPrimitiveId(), is(10L)); + assertThat(value.getStringId(), is("string")); + assertThat(value.getUuidId(), is(notNullValue())); + +// result = executionContext.execute("query { getMultiLevelList { listOfListOfBigDecimal } }"); +// mapResults = getAndAssertResult(result); +// assertThat(mapResults.size(), is(1)); +// assertThat(mapResults.get("getMultiLevelList"), is(notNullValue())); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java index 5f56ebc355d..497790020e2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java @@ -74,7 +74,7 @@ public TestDB() { Person person = new Person(i, "Person " + i, homeAddress, workAddress, new BigDecimal(RANDOM.nextFloat()), List.of("BA", "BA Hon"), List.of(prevAddress1, prevAddress2), new int[0], new String[0], EMPTY_MAP, - LocalDate.now(), System.nanoTime()); + LocalDate.now(), System.nanoTime(), BigDecimal.valueOf(10)); allPeople.put(person.getPersonId(), person); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index b5e32c6cdfa..a474626f0b3 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -16,8 +16,13 @@ package io.helidon.microprofile.graphql.server.test.queries; +import java.math.BigDecimal; import java.time.LocalDate; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; @@ -25,7 +30,9 @@ import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; +import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; import io.helidon.microprofile.graphql.server.test.types.Person; +import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.Query; @@ -68,8 +75,6 @@ public String getVillain() { @Query("allPeople") public Collection findAllPeople() { return testDB.getAllPeople(); - // TODO: Need to figure out why CDI not working for - // return testDB.getAllPeople(); } @Query @@ -82,4 +87,17 @@ public LocalDate returnCurrentDate() { public EnumTestWithEnumName getEnumMedium() { return EnumTestWithEnumName.M; } + + @Query + public TypeWithIDs returnTypeWithIDs() { + return new TypeWithIDs(1, 2, "string", 10L, 10L, UUID.randomUUID()); + } + + @Query + @Name("getMultiLevelList") + public MultiLevelListsAndArrays returnLists() { + List> listListBigDecimal = new ArrayList<>(); + listListBigDecimal.add(Collections.singletonList(new BigDecimal(100))); + return new MultiLevelListsAndArrays(listListBigDecimal, null, null); + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level0.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level0.java index b2d101f8c17..c54141ca19c 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level0.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level0.java @@ -32,6 +32,9 @@ public Level0(String id, Level1 level1) { this.level1 = level1; } + public Level0() { + } + public String getId() { return id; } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level1.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level1.java index 84e2d2cc799..95eed8f3c2e 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level1.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level1.java @@ -16,16 +16,23 @@ package io.helidon.microprofile.graphql.server.test.types; +import java.math.BigDecimal; + /** * POJO to test multiple levels of object graph. */ public class Level1 { private String code; private Level2 level2; + private BigDecimal bigDecimal; - public Level1(String code, Level2 level2) { + public Level1(String code, Level2 level2, BigDecimal bigDecimal) { this.code = code; this.level2 = level2; + this.bigDecimal = bigDecimal; + } + + public Level1() { } public String getCode() { @@ -44,4 +51,11 @@ public void setLevel2(Level2 level2) { this.level2 = level2; } + public BigDecimal getBigDecimal() { + return bigDecimal; + } + + public void setBigDecimal(BigDecimal bigDecimal) { + this.bigDecimal = bigDecimal; + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level2.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level2.java index 1a4f7a24932..debbeb2fc79 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level2.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Level2.java @@ -30,6 +30,9 @@ public Level2(String code, String description, Address address) { this.address = address; } + public Level2() { + } + public String getCode() { return code; } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java new file mode 100644 index 00000000000..63984684ef0 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import java.math.BigDecimal; +import java.util.List; + +import org.eclipse.microprofile.graphql.Type; + +/** + * POJO to test multiple levels of lists and arrays. + */ +@Type +public class MultiLevelListsAndArrays { + + private List> listOfListOfBigDecimal; + private List> listOfListOfPerson; + private List> listOfListOfInteger; + + public MultiLevelListsAndArrays(List> listOfListOfBigDecimal, + List> listOfListOfPerson, + List> listOfListOfInteger) { + this.listOfListOfBigDecimal = listOfListOfBigDecimal; + this.listOfListOfPerson = listOfListOfPerson; + this.listOfListOfInteger = listOfListOfInteger; + } + + public MultiLevelListsAndArrays() { + } + + public List> getListOfListOfBigDecimal() { + return listOfListOfBigDecimal; + } + + public void setListOfListOfBigDecimal(List> listOfListOfBigDecimaal) { + this.listOfListOfBigDecimal = listOfListOfBigDecimaal; + } + + public List> getListOfListOfPerson() { + return listOfListOfPerson; + } + + public void setListOfListOfPerson(List> listOfListOfPerson) { + this.listOfListOfPerson = listOfListOfPerson; + } + + public List> getListOfListOfInteger() { + return listOfListOfInteger; + } + + public void setListOfListOfInteger(List> listOfListOfInteger) { + this.listOfListOfInteger = listOfListOfInteger; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java index a457ecc608b..70cacfe4f0c 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java @@ -42,6 +42,7 @@ public class Person { private Map addressMap; private LocalDate localDate; private long longValue; + private BigDecimal bigDecimal; public Person(int personId, String name, @@ -52,7 +53,7 @@ public Person(int personId, List
previousAddresses, int[] intArray, String[] stringArray, - Map addressMap, LocalDate localDate, long longValue) { + Map addressMap, LocalDate localDate, long longValue, BigDecimal bigDecimal) { this.personId = personId; this.name = name; this.homeAddress = homeAddress; @@ -65,6 +66,7 @@ public Person(int personId, this.addressMap = addressMap; this.localDate = localDate; this.longValue = longValue; + this.bigDecimal = bigDecimal; } public long getLongValue() { @@ -163,6 +165,14 @@ public void setLocalDate(LocalDate localDate) { this.localDate = localDate; } + public BigDecimal getBigDecimal() { + return bigDecimal; + } + + public void setBigDecimal(BigDecimal bigDecimal) { + this.bigDecimal = bigDecimal; + } + @Override public String toString() { return "Person{" + @@ -178,6 +188,7 @@ public String toString() { ", addressMap=" + addressMap + ", longValue=" + longValue + ", localDate=" + localDate + + ", bigDecimal=" + bigDecimal + '}'; } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithIDs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithIDs.java new file mode 100644 index 00000000000..f96b7c3c0e6 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithIDs.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import java.util.UUID; + +import org.eclipse.microprofile.graphql.Id; +import org.eclipse.microprofile.graphql.Type; + +/** + * Class to test multiple ID fields. + */ +@Type +public class TypeWithIDs { + + @Id + private int intId; + + @Id + private Integer integerId; + + @Id + private String stringId; + + @Id + private Long longId; + + @Id + private long longPrimitiveId; + + @Id + private UUID uuidId; + + public TypeWithIDs() { + } + + public TypeWithIDs(int intId, Integer integerId, String stringId, Long longId, long longPrimitiveId, UUID uuidId) { + this.intId = intId; + this.integerId = integerId; + this.stringId = stringId; + this.longId = longId; + this.longPrimitiveId = longPrimitiveId; + this.uuidId = uuidId; + } + + public int getIntId() { + return intId; + } + + public void setIntId(int intId) { + this.intId = intId; + } + + public Integer getIntegerId() { + return integerId; + } + + public void setIntegerId(Integer integerId) { + this.integerId = integerId; + } + + public String getStringId() { + return stringId; + } + + public void setStringId(String stringId) { + this.stringId = stringId; + } + + public Long getLongId() { + return longId; + } + + public void setLongId(Long longId) { + this.longId = longId; + } + + public long getLongPrimitiveId() { + return longPrimitiveId; + } + + public void setLongPrimitiveId(long longPrimitiveId) { + this.longPrimitiveId = longPrimitiveId; + } + + public UUID getUuidId() { + return uuidId; + } + + public void setUuidId(UUID uuidId) { + this.uuidId = uuidId; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java index ded2ffcf036..37a32681f70 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java @@ -22,7 +22,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Map; +import java.util.Map; import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; import io.helidon.microprofile.graphql.server.model.SchemaEnum; @@ -39,9 +39,11 @@ import io.helidon.microprofile.graphql.server.test.types.Level0; import io.helidon.microprofile.graphql.server.test.types.Level1; import io.helidon.microprofile.graphql.server.test.types.Motorbike; +import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.PersonWithName; import io.helidon.microprofile.graphql.server.test.types.PersonWithNameValue; +import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; import io.helidon.microprofile.graphql.server.test.types.TypeWithIdOnField; import io.helidon.microprofile.graphql.server.test.types.TypeWithIdOnMethod; import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty; @@ -79,7 +81,7 @@ public void testEnumGeneration() throws IntrospectionException, ClassNotFoundExc public void testGettersPerson() throws IntrospectionException { Map mapMethods = SchemaUtils.retrieveGetterBeanMethods(Person.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(12)); + assertThat(mapMethods.size(), is(13)); assertDiscoveredMethod(mapMethods.get("personId"), "personId", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("name"), "name", STRING, null, false, false, false); @@ -94,6 +96,7 @@ public void testGettersPerson() throws IntrospectionException { assertDiscoveredMethod(mapMethods.get("addressMap"), "addressMap", ADDRESS, null, false, false, true); assertDiscoveredMethod(mapMethods.get("localDate"), "localDate", LOCALDATE, null, false, false, false); assertDiscoveredMethod(mapMethods.get("longValue"), "longValue", long.class.getName(), null, false, false, false); + assertDiscoveredMethod(mapMethods.get("bigDecimal"), "bigDecimal", BigDecimal.class.getName(), null, false, false, false); } @Test @@ -215,7 +218,7 @@ public void testAllMethods() throws IntrospectionException { Map mapMethods = SchemaUtils .retrieveAllAnnotatedBeanMethods(SimpleQueriesNoArgs.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(7)); + assertThat(mapMethods.size(), is(9)); assertDiscoveredMethod(mapMethods.get("hero"), "hero", STRING, null, false, false, false); assertDiscoveredMethod(mapMethods.get("episodeCount"), "episodeCount", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("numberOfStars"), "numberOfStars", Long.class.getName(), null, false, false, false); @@ -225,6 +228,10 @@ public void testAllMethods() throws IntrospectionException { false, false); assertDiscoveredMethod(mapMethods.get("returnMediumSize"), "returnMediumSize", EnumTestWithEnumName.class.getName(), null, false, false, false); + assertDiscoveredMethod(mapMethods.get("returnTypeWithIDs"), "returnTypeWithIDs", TypeWithIDs.class.getName(), null, + false, false, false); + assertDiscoveredMethod(mapMethods.get("getMultiLevelList"), "getMultiLevelList", MultiLevelListsAndArrays.class.getName(), null, + false, false, false); } private void assertDiscoveredMethod(SchemaUtils.DiscoveredMethod discoveredMethod, @@ -243,6 +250,17 @@ private void assertDiscoveredMethod(SchemaUtils.DiscoveredMethod discoveredMetho assertThat(discoveredMethod.getCollectionType(), is(collectionType)); } + @Test + public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassNotFoundException { + SchemaUtils schemaUtils = new SchemaUtils(); + Map mapMethods = SchemaUtils + .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class); + assertThat(mapMethods, is(notNullValue())); + assertThat(mapMethods.size(), is(3)); + Schema schema = schemaUtils.generateSchemaFromClasses(MultiLevelListsAndArrays.class); + generateGraphQLSchema(schema); + } + private void testEnum(Class clazz, String expectedName) throws IntrospectionException, ClassNotFoundException { SchemaUtils schemaUtils = new SchemaUtils(); Schema schema = schemaUtils.generateSchemaFromClasses(clazz); From 048c7630bbccd9521339c60774afe963f9a87f99 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 11 Mar 2020 13:21:32 +0800 Subject: [PATCH 011/178] flatten the package structure --- .../AbstractDescriptiveElement.java | 2 +- .../server/{util => }/DataFetcherUtils.java | 7 +- .../{model => }/DescriptiveElement.java | 10 +- .../graphql/server/ExecutionContext.java | 3 - .../{application => }/GraphQLApplication.java | 2 +- .../{application => }/GraphQLResource.java | 5 +- .../server/{util => }/JandexUtils.java | 2 +- .../graphql/server/{util => }/JsonUtils.java | 2 +- .../graphql/server/{model => }/Schema.java | 4 +- .../server/{model => }/SchemaArgument.java | 2 +- .../server/{model => }/SchemaDirective.java | 2 +- .../server/{model => }/SchemaEnum.java | 2 +- .../{model => }/SchemaFieldDefinition.java | 2 +- .../server/{model => }/SchemaGenerator.java | 2 +- .../server/{model => }/SchemaInputType.java | 2 +- .../server/{model => }/SchemaScalar.java | 2 +- .../server/{model => }/SchemaType.java | 2 +- .../server/{util => }/SchemaUtils.java | 391 ++---------------- .../graphql/server/SchemaUtilsHelper.java | 382 +++++++++++++++++ .../server/application/package-info.java | 21 - .../graphql/server/model/package-info.java | 20 - .../graphql/server/util/package-info.java | 20 - .../server/src/main/java/module-info.java | 4 +- .../graphql/server/AbstractGraphQLIT.java | 6 +- .../graphql/server/AbstractGraphQLTest.java | 3 +- .../graphql/server/GraphQLIT.java | 2 - .../{resource => }/GraphQLResourceTest.java | 2 +- .../server/{util => }/JandexUtilsTest.java | 3 +- .../server/{util => }/JsonUtilsTest.java | 2 +- .../{model => }/SchemaArgumentTest.java | 3 +- .../{model => }/SchemaDirectiveTest.java | 5 +- .../server/{model => }/SchemaEnumTest.java | 3 +- .../SchemaFieldDefinitionTest.java | 4 +- .../server/{model => }/SchemaScalarTest.java | 3 +- .../server/{model => }/SchemaTest.java | 9 +- .../server/{model => }/SchemaTypeTest.java | 6 +- .../graphql/server/SchemaUtilsIT.java | 9 +- .../server/{util => }/SchemaUtilsTest.java | 51 ++- .../graphql/server/test/db/TestDB.java | 28 +- .../test/queries/SimpleQueriesNoArgs.java | 20 +- .../test/types/MultiLevelListsAndArrays.java | 53 ++- 41 files changed, 582 insertions(+), 521 deletions(-) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/AbstractDescriptiveElement.java (96%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{util => }/DataFetcherUtils.java (94%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/DescriptiveElement.java (83%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{application => }/GraphQLApplication.java (95%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{application => }/GraphQLResource.java (95%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{util => }/JandexUtils.java (98%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{util => }/JsonUtils.java (96%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/Schema.java (99%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/SchemaArgument.java (99%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/SchemaDirective.java (98%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/SchemaEnum.java (97%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/SchemaFieldDefinition.java (99%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/SchemaGenerator.java (97%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/SchemaInputType.java (96%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/SchemaScalar.java (98%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{model => }/SchemaType.java (99%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{util => }/SchemaUtils.java (76%) create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/package-info.java delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/package-info.java delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/package-info.java rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{resource => }/GraphQLResourceTest.java (92%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{util => }/JandexUtilsTest.java (97%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{util => }/JsonUtilsTest.java (98%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{model => }/SchemaArgumentTest.java (97%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{model => }/SchemaDirectiveTest.java (95%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{model => }/SchemaEnumTest.java (96%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{model => }/SchemaFieldDefinitionTest.java (98%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{model => }/SchemaScalarTest.java (93%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{model => }/SchemaTest.java (92%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{model => }/SchemaTypeTest.java (97%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{util => }/SchemaUtilsTest.java (87%) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java similarity index 96% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java index 5ebfed967c0..1c797544d87 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/AbstractDescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.Objects; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java similarity index 94% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index ca54d45c6b1..e79c7c6789e 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.util; +package io.helidon.microprofile.graphql.server; -import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Map; @@ -24,11 +23,9 @@ import javax.enterprise.inject.spi.CDI; -import io.helidon.microprofile.graphql.server.model.SchemaArgument; - import graphql.schema.DataFetcher; -import static io.helidon.microprofile.graphql.server.util.SchemaUtils.ID; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.ID; /** * Utilities for working with {@link DataFetcher}s. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java similarity index 83% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java index 1540c8d61d1..4286b487749 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/DescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; -import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.NEWLINE; -import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.NOTHING; -import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.QUOTE; -import static io.helidon.microprofile.graphql.server.model.SchemaGenerator.TRIPLE_QUOTE; +import static io.helidon.microprofile.graphql.server.SchemaGenerator.NEWLINE; +import static io.helidon.microprofile.graphql.server.SchemaGenerator.NOTHING; +import static io.helidon.microprofile.graphql.server.SchemaGenerator.QUOTE; +import static io.helidon.microprofile.graphql.server.SchemaGenerator.TRIPLE_QUOTE; /** * Describes an element that has a description. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index 90018c0c5ae..aff788ef26f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -20,9 +20,6 @@ import java.util.Map; import java.util.logging.Logger; -import io.helidon.microprofile.graphql.server.model.Schema; -import io.helidon.microprofile.graphql.server.util.SchemaUtils; - import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLApplication.java similarity index 95% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLApplication.java index aee3615b088..c9856a0fdcd 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLApplication.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLApplication.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.application; +package io.helidon.microprofile.graphql.server; import java.util.Collections; import java.util.Set; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java similarity index 95% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java index b4e8dc875a3..ed0de8d273b 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.application; +package io.helidon.microprofile.graphql.server; import java.util.HashMap; import java.util.Map; @@ -31,9 +31,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import io.helidon.microprofile.graphql.server.ExecutionContext; -import io.helidon.microprofile.graphql.server.util.JsonUtils; - import com.fasterxml.jackson.core.JsonProcessingException; import graphql.ExecutionResult; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java similarity index 98% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java index 833be0b90cc..7258722c983 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JandexUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.util; +package io.helidon.microprofile.graphql.server; import java.io.File; import java.io.FileInputStream; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JsonUtils.java similarity index 96% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JsonUtils.java index af277e84f9d..5a9d69c4f8f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/JsonUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JsonUtils.java @@ -1,4 +1,4 @@ -package io.helidon.microprofile.graphql.server.util; +package io.helidon.microprofile.graphql.server; import java.util.Collections; import java.util.Map; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java similarity index 99% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java index 18710542158..aec48f739bd 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/Schema.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.ArrayList; import java.util.HashMap; @@ -35,7 +35,7 @@ import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring; import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring; -import static io.helidon.microprofile.graphql.server.util.SchemaUtils.getSafeClass; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getSafeClass; /** * The representation of a GraphQL Schema. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java similarity index 99% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index bb4b52b6a95..ae2909fd146 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.Objects; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaDirective.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java similarity index 98% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaDirective.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java index a20e9412617..cee330ca8e5 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaDirective.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.ArrayList; import java.util.LinkedHashSet; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaEnum.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java similarity index 97% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaEnum.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java index 8c9d8f4b087..4936ac17494 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaEnum.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.ArrayList; import java.util.List; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java similarity index 99% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinition.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index 091817d6d6a..3b7375006f5 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.ArrayList; import java.util.List; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java similarity index 97% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 0be2bc745a5..be46a812e05 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; /** * An interface to represent a element that can generate a schema. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java similarity index 96% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java index bfdf8f77917..9b099d54ab3 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaInputType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; /** * The representation of a GraphQL Input Type. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaScalar.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java similarity index 98% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaScalar.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java index b74873e5b10..ce789e036f8 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaScalar.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.Objects; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java similarity index 99% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaType.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java index d2520de21a2..e2dcf092511 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/SchemaType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.ArrayList; import java.util.List; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java similarity index 76% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java index 13e54751e48..31591aae818 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.util; +package io.helidon.microprofile.graphql.server; import java.beans.IntrospectionException; import java.beans.Introspector; @@ -25,12 +25,6 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.OffsetTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -45,22 +39,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.json.bind.annotation.JsonbProperty; - -import io.helidon.microprofile.graphql.server.model.Schema; -import io.helidon.microprofile.graphql.server.model.SchemaArgument; -import io.helidon.microprofile.graphql.server.model.SchemaEnum; -import io.helidon.microprofile.graphql.server.model.SchemaFieldDefinition; -import io.helidon.microprofile.graphql.server.model.SchemaInputType; -import io.helidon.microprofile.graphql.server.model.SchemaScalar; -import io.helidon.microprofile.graphql.server.model.SchemaType; - -import graphql.Scalars; -import graphql.scalars.ExtendedScalars; import graphql.schema.DataFetcher; import org.eclipse.microprofile.graphql.DefaultValue; import org.eclipse.microprofile.graphql.Description; -import org.eclipse.microprofile.graphql.Enum; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Id; import org.eclipse.microprofile.graphql.Input; @@ -69,7 +50,20 @@ import org.eclipse.microprofile.graphql.Query; import org.eclipse.microprofile.graphql.Type; -import static io.helidon.microprofile.graphql.server.util.SchemaUtils.DiscoveredMethod.READ; +import static io.helidon.microprofile.graphql.server.SchemaUtils.DiscoveredMethod.READ; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.ID; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.PRIMITIVE_ARRAY_MAP; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.checkScalars; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getFieldName; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getGraphQLType; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getMethodName; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getNameAnnotationValue; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getSafeClass; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getScalar; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getSimpleName; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getTypeName; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.isEnumClass; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.isValidIDType; /** * Various utilities for generating {@link Schema}s from classes. @@ -81,122 +75,6 @@ public class SchemaUtils { */ private static final Logger LOGGER = Logger.getLogger(SchemaUtils.class.getName()); - /** - * List of supported scalars keyed by the full class name. - */ - private static final Map SUPPORTED_SCALARS = new HashMap<>() {{ - put(OffsetTime.class.getName(), new SchemaScalar("Time", OffsetTime.class.getName(), ExtendedScalars.Time)); - put(LocalTime.class.getName(), new SchemaScalar("LocalTime", OffsetTime.class.getName(), ExtendedScalars.Time)); - put(Object.class.getName(), new SchemaScalar("Object", Object.class.getName(), ExtendedScalars.Object)); - put(OffsetDateTime.class.getName(), - new SchemaScalar("DateTime", OffsetDateTime.class.getName(), ExtendedScalars.DateTime)); - put(LocalDate.class.getName(), new SchemaScalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); - put(BigDecimal.class.getName(), new SchemaScalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); - put(BigInteger.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); - put(long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); - put(Long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); - }}; - - /** - * List of types the should map to a GraphQL Float. - */ - private static final List FLOAT_LIST = new ArrayList<>() {{ - add("double"); - add("Double"); - add("java.lang.Double"); - add("float"); - add("Float"); - add("java.lang.Float"); - add("java.lang.Number"); - }}; - - private static final List BOOLEAN_LIST = new ArrayList<>() {{ - add("boolean"); - add(Boolean.class.getName()); - }}; - - /** - * List of types that should map to a GraphQL Int. - */ - private static final List INTEGER_LIST = new ArrayList<>() {{ - add("short"); - add("int"); - add("Short"); - add("Integer"); - add("java.lang.Integer"); - add("java.lang.Short"); - add("java.lang.Byte"); - add("byte"); - }}; - - /** - * List of types that should map to a GraphQL String. - */ - private static final List STRING_LIST = new ArrayList<>() {{ - add("java.lang.String"); - add("java.lang.Character"); - add("char"); - }}; - - /** - * List of array primitive types and their array mapping. See https://docs.oracle.com/javase/6/docs/api/java/lang/Class - * .html#getName%28%29 - */ - private static final Map PRIMITIVE_ARRAY_MAP = new HashMap<>() {{ - put("[Z", "boolean"); - put("[B", "byte"); - put("[C", "char"); - put("[D", "double"); - put("[F", "float"); - put("[I", "int"); - put("[J", "long"); - put("[S", "short"); - }}; - - /** - * List of all Java primitive objects. - */ - private static final List JAVA_PRIMITIVE_OBJECTS = new ArrayList<>() {{ - addAll(STRING_LIST); - addAll(FLOAT_LIST); - addAll(INTEGER_LIST); - }}; - - /** - * GraphQL Int. - */ - protected static final String INT = "Int"; - - /** - * GraphQL Float. - */ - protected static final String FLOAT = "Float"; - - /** - * GraphQL String. - */ - protected static final String STRING = "String"; - - /** - * GraphQL ID. - */ - protected static final String ID = "ID"; - - /** - * GraphQL Boolean. - */ - protected static final String BOOLEAN = "Boolean"; - - /** - * Class for long primitive. - */ - protected static final String LONG_PRIMITIVE = long.class.getName(); - - /** - * Class for Long object. - */ - protected static final String LONG_OBJECT = Long.class.getName(); - /** * {@link JandexUtils} instance to hold indexes. */ @@ -591,91 +469,6 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String return fd; } - /** - * Checks a {@link SchemaType} for {@link SchemaFieldDefinition}s which contain known scalars and replace them with the scalar - * name. - * - * @param schema {@link Schema} to check scalars for. - * @param type {@link SchemaType} to check - */ - private void checkScalars(Schema schema, SchemaType type) { - type.getFieldDefinitions().forEach(fd -> { - SchemaScalar scalar = getScalar(fd.getReturnType()); - if (scalar != null) { - fd.setReturnType(scalar.getName()); - if (!schema.containsScalarWithName(scalar.getName())) { - schema.addScalar(scalar); - } - } - }); - } - - /** - * Return the {@link Name} annotation value if it exists or null. - * - * @param clazz {@link Class} to check - * @return the {@link Name} annotation value if it exists or null - */ - protected String getNameAnnotationValue(Class clazz) { - Name nameAnnotation = clazz.getAnnotation(Name.class); - if (nameAnnotation != null && !"".equals(nameAnnotation.value())) { - return nameAnnotation.value(); - } - return null; - } - - /** - * Return correct name for a Type or Enum based upon the value of the annotation or the {@link Name}. - * - * @param clazz {@link Class} to introspect. - * @return the correct name - */ - protected String getTypeName(Class clazz) { - Type typeAnnotation = clazz.getAnnotation(Type.class); - Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); - Input inputAnnotation = clazz.getAnnotation(Input.class); - Enum enumAnnotation = clazz.getAnnotation(Enum.class); - Query queryAnnotation = clazz.getAnnotation(Query.class); - - String name = ""; - if (typeAnnotation != null) { - name = typeAnnotation.value(); - } else if (interfaceAnnotation != null) { - name = interfaceAnnotation.value(); - } else if (inputAnnotation != null) { - name = inputAnnotation.value(); - } else if (enumAnnotation != null) { - name = enumAnnotation.value(); - } else if (queryAnnotation != null) { - name = queryAnnotation.value(); - } - - if ("".equals(name)) { - name = getNameAnnotationValue(clazz); - } - - return name == null || name.isBlank() ? clazz.getSimpleName() : name; - } - - /** - * Return the simple name from a given class as a String. This takes into account any annotations that may be present. - * - * @param className class name - * @return the simple class name - * @throws ClassNotFoundException if invalid class name - */ - protected String getSimpleName(String className) - throws ClassNotFoundException { - if (INT.equals(className) - || FLOAT.equals(className) || ID.equals(className) - || STRING.equals(className) || BOOLEAN.equalsIgnoreCase(className) - || LONG_OBJECT.equals(className) || LONG_PRIMITIVE.equals(className)) { - return className; - } - // return the type name taking into account any annotations - Class clazz = Class.forName(className); - return getTypeName(clazz); - } /** * Look in the given {@link Schema} for any field definitions, arguments and key value classes that contain the return type of @@ -706,65 +499,6 @@ private void updateLongTypes(Schema schema, String longReturnType, String shortR }); } - /** - * Return true if the fully qualified class is an enum. - * - * @param clazz class to check - * @return true if the fully qualified class is an enum. - */ - private boolean isEnumClass(String clazz) { - try { - return (Class.forName(clazz)).isEnum(); - } catch (ClassNotFoundException e) { - return false; - } - } - - /** - * Returns a {@link SchemaScalar} if one matches the known list of scalars available from the {@link ExtendedScalars} helper. - * - * @param clazzName class name to check for - * @return a {@link SchemaScalar} if one matches the known list of scalars or null if none found - */ - private static SchemaScalar getScalar(String clazzName) { - return SUPPORTED_SCALARS.get(clazzName); - } - - /** - * Return a Class from a class name and ignore any exceptions. - * - * @param clazzName the class name as a String - * @return a Class name - */ - public static Class getSafeClass(String clazzName) { - try { - return Class.forName(clazzName); - } catch (ClassNotFoundException e) { - return null; - } - } - - /** - * Return the GraphQL type for the given Java type. - * - * @param className fully qualified class name - * @return the GraphQL type - */ - protected static String getGraphQLType(String className) { - if (INTEGER_LIST.contains(className)) { - return INT; - } else if (FLOAT_LIST.contains(className)) { - return FLOAT; - } else if (BOOLEAN_LIST.contains(className)) { - return BOOLEAN; - } else if (STRING_LIST.contains(className)) { - return STRING; - } else if (java.util.UUID.class.getName().equals(className)) { - return ID; - } - - return className; - } /** * Return a {@link Map} of all the discovered methods which have the {@link Query} annotation. @@ -941,18 +675,6 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class return discoveredMethod; } - /** - * Return true if the {@link Class} is a valid {@link Class} to apply the {@link Id} annotation. - * - * @param clazz {@link Class} to check - * @return true if the {@link Class} is a valid {@link Class} to apply the {@link Id} annotation - */ - private static boolean isValidIDType(Class clazz) { - return clazz.equals(Long.class) || clazz.equals(Integer.class) - || clazz.equals(java.util.UUID.class) || clazz.equals(int.class) - || clazz.equals(String.class) || clazz.equals(long.class); - } - /** * Return the {@link ReturnType} for this return class and method. * @@ -1015,68 +737,6 @@ private static ReturnType getReturnType(Class returnClazz, java.lang.reflect. return actualReturnType; } - /** - * Return the field name after checking both the {@link Name} and {@link JsonbProperty} annotations are present on the {@link - * Method}.

Name will take precedence if both are specified. - * - * @param method {@link Method} to check - * @return the field name or null if non exist - */ - protected static String getMethodName(Method method) { - Query queryAnnotation = method.getAnnotation(Query.class); - Name nameAnnotation = method.getAnnotation(Name.class); - JsonbProperty jsonbPropertyAnnotation = method.getAnnotation(JsonbProperty.class); - if (queryAnnotation != null && !queryAnnotation.value().isBlank()) { - return queryAnnotation.value(); - } - if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { - // Name annotation is specified so use this and don't bother checking JsonbProperty - return nameAnnotation.value(); - } - if (jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank()) { - return jsonbPropertyAnnotation.value(); - } - return null; - } - - /** - * Return the field name after checking both the {@link Name} and {@link JsonbProperty} annotations are present on the field - * name..

Name will take precedence if both are specified. - * - * @param clazz {@link Class} to check - * @param fieldName field name to check - * @return the field name or null if none exist - */ - protected static String getFieldName(Class clazz, String fieldName) { - try { - Field field = clazz.getDeclaredField(fieldName); - if (field != null) { - Name nameAnnotation = field.getAnnotation(Name.class); - // Name annotation is specified so use this and don't bother checking JsonbProperty - if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { - return nameAnnotation.value(); - } - // check for JsonbProperty - JsonbProperty jsonbPropertyAnnotation = field.getAnnotation(JsonbProperty.class); - return jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank() - ? jsonbPropertyAnnotation.value() - : null; - } - return null; - } catch (NoSuchFieldException e) { - return null; - } - } - - /** - * Return the list of Java primitive types which are Objects. - * - * @return the list of Java primitive types - */ - public static List getJavaPrimitiveObjects() { - return JAVA_PRIMITIVE_OBJECTS; - } - /** * Defines discovered methods for a class. */ @@ -1356,6 +1016,11 @@ public static class ReturnType { */ private String collectionType; + /** + * Number of levels in the Array. + */ + private int arrayLevels = 0; + /** * Default constructor. */ @@ -1433,5 +1098,21 @@ public String getCollectionType() { public void setCollectionType(String collectionType) { this.collectionType = collectionType; } + + /** + * Return the level of arrays or 0 if not an array. + * @return the level of arrays + */ + public int getArrayLevels() { + return arrayLevels; + } + + /** + * Set the level of arrays or 0 if not an array. + * @param arrayLevels the level of arrays or 0 if not an array + */ + public void setArrayLevels(int arrayLevels) { + this.arrayLevels = arrayLevels; + } } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java new file mode 100644 index 00000000000..46552e7c94a --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.json.bind.annotation.JsonbProperty; + +import graphql.Scalars; +import graphql.scalars.ExtendedScalars; +import org.eclipse.microprofile.graphql.Enum; +import org.eclipse.microprofile.graphql.Id; +import org.eclipse.microprofile.graphql.Input; +import org.eclipse.microprofile.graphql.Interface; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Type; + +/** + * Helper class for {@link SchemaUtils}. + */ +public class SchemaUtilsHelper { + + /** + * List of supported scalars keyed by the full class name. + */ + private static final Map SUPPORTED_SCALARS = new HashMap<>() {{ + put(OffsetTime.class.getName(), new SchemaScalar("Time", OffsetTime.class.getName(), ExtendedScalars.Time)); + put(LocalTime.class.getName(), new SchemaScalar("LocalTime", OffsetTime.class.getName(), ExtendedScalars.Time)); + put(Object.class.getName(), new SchemaScalar("Object", Object.class.getName(), ExtendedScalars.Object)); + put(OffsetDateTime.class.getName(), + new SchemaScalar("DateTime", OffsetDateTime.class.getName(), ExtendedScalars.DateTime)); + put(LocalDate.class.getName(), new SchemaScalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); + put(BigDecimal.class.getName(), new SchemaScalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); + put(BigInteger.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + put(long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + put(Long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + }}; + + /** + * List of types the should map to a GraphQL Float. + */ + protected static final List FLOAT_LIST = new ArrayList<>() {{ + add("double"); + add("Double"); + add("java.lang.Double"); + add("float"); + add("Float"); + add("java.lang.Float"); + add("java.lang.Number"); + }}; + + protected static final List BOOLEAN_LIST = new ArrayList<>() {{ + add("boolean"); + add(Boolean.class.getName()); + }}; + + /** + * List of types that should map to a GraphQL Int. + */ + protected static final List INTEGER_LIST = new ArrayList<>() {{ + add("short"); + add("int"); + add("Short"); + add("Integer"); + add("java.lang.Integer"); + add("java.lang.Short"); + add("java.lang.Byte"); + add("byte"); + }}; + + /** + * List of types that should map to a GraphQL String. + */ + protected static final List STRING_LIST = new ArrayList<>() {{ + add("java.lang.String"); + add("java.lang.Character"); + add("char"); + }}; + + /** + * List of array primitive types and their array mapping. See https://docs.oracle.com/javase/6/docs/api/java/lang/Class + * .html#getName%28%29 + */ + protected static final Map PRIMITIVE_ARRAY_MAP = new HashMap<>() {{ + put("[Z", "boolean"); + put("[B", "byte"); + put("[C", "char"); + put("[D", "double"); + put("[F", "float"); + put("[I", "int"); + put("[J", "long"); + put("[S", "short"); + }}; + + /** + * List of all Java primitive objects. + */ + protected static final List JAVA_PRIMITIVE_OBJECTS = new ArrayList<>() {{ + addAll(STRING_LIST); + addAll(FLOAT_LIST); + addAll(INTEGER_LIST); + }}; + + /** + * GraphQL Int. + */ + protected static final String INT = "Int"; + + /** + * GraphQL Float. + */ + protected static final String FLOAT = "Float"; + + /** + * GraphQL String. + */ + protected static final String STRING = "String"; + + /** + * GraphQL ID. + */ + protected static final String ID = "ID"; + + /** + * GraphQL Boolean. + */ + protected static final String BOOLEAN = "Boolean"; + + /** + * Class for long primitive. + */ + protected static final String LONG_PRIMITIVE = long.class.getName(); + + /** + * Class for Long object. + */ + protected static final String LONG_OBJECT = Long.class.getName(); + + /** + * Private no-args constructor. + */ + private SchemaUtilsHelper() { + } + + /** + * Return the simple name from a given class as a String. This takes into account any annotations that may be present. + * + * @param className class name + * @return the simple class name + * @throws ClassNotFoundException if invalid class name + */ + protected static String getSimpleName(String className) + throws ClassNotFoundException { + if (INT.equals(className) + || FLOAT.equals(className) || ID.equals(className) + || STRING.equals(className) || BOOLEAN.equalsIgnoreCase(className) + || LONG_OBJECT.equals(className) || LONG_PRIMITIVE.equals(className)) { + return className; + } + // return the type name taking into account any annotations + Class clazz = Class.forName(className); + return getTypeName(clazz); + } + + /** + * Returns a {@link SchemaScalar} if one matches the known list of scalars available from the {@link ExtendedScalars} helper. + * + * @param clazzName class name to check for + * @return a {@link SchemaScalar} if one matches the known list of scalars or null if none found + */ + protected static SchemaScalar getScalar(String clazzName) { + return SUPPORTED_SCALARS.get(clazzName); + } + + /** + * Return the GraphQL type for the given Java type. + * + * @param className fully qualified class name + * @return the GraphQL type + */ + protected static String getGraphQLType(String className) { + if (INTEGER_LIST.contains(className)) { + return INT; + } else if (FLOAT_LIST.contains(className)) { + return FLOAT; + } else if (BOOLEAN_LIST.contains(className)) { + return BOOLEAN; + } else if (STRING_LIST.contains(className)) { + return STRING; + } else if (java.util.UUID.class.getName().equals(className)) { + return ID; + } + + return className; + } + + /** + * Return the field name after checking both the {@link Name} and {@link JsonbProperty} annotations are present on the field + * name..

Name will take precedence if both are specified. + * + * @param clazz {@link Class} to check + * @param fieldName field name to check + * @return the field name or null if none exist + */ + protected static String getFieldName(Class clazz, String fieldName) { + try { + Field field = clazz.getDeclaredField(fieldName); + if (field != null) { + Name nameAnnotation = field.getAnnotation(Name.class); + // Name annotation is specified so use this and don't bother checking JsonbProperty + if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { + return nameAnnotation.value(); + } + // check for JsonbProperty + JsonbProperty jsonbPropertyAnnotation = field.getAnnotation(JsonbProperty.class); + return jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank() + ? jsonbPropertyAnnotation.value() + : null; + } + return null; + } catch (NoSuchFieldException e) { + return null; + } + } + + /** + * Return the field name after checking both the {@link Name} and {@link JsonbProperty} annotations are present on the {@link + * Method}.

Name will take precedence if both are specified. + * + * @param method {@link Method} to check + * @return the field name or null if non exist + */ + protected static String getMethodName(Method method) { + Query queryAnnotation = method.getAnnotation(Query.class); + Name nameAnnotation = method.getAnnotation(Name.class); + JsonbProperty jsonbPropertyAnnotation = method.getAnnotation(JsonbProperty.class); + if (queryAnnotation != null && !queryAnnotation.value().isBlank()) { + return queryAnnotation.value(); + } + if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { + // Name annotation is specified so use this and don't bother checking JsonbProperty + return nameAnnotation.value(); + } + if (jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank()) { + return jsonbPropertyAnnotation.value(); + } + return null; + } + + /** + * Return a Class from a class name and ignore any exceptions. + * + * @param clazzName the class name as a String + * @return a Class name + */ + protected static Class getSafeClass(String clazzName) { + try { + return Class.forName(clazzName); + } catch (ClassNotFoundException e) { + return null; + } + } + + /** + * Return true if the {@link Class} is a valid {@link Class} to apply the {@link Id} annotation. + * + * @param clazz {@link Class} to check + * @return true if the {@link Class} is a valid {@link Class} to apply the {@link Id} annotation + */ + protected static boolean isValidIDType(Class clazz) { + return clazz.equals(Long.class) || clazz.equals(Integer.class) + || clazz.equals(java.util.UUID.class) || clazz.equals(int.class) + || clazz.equals(String.class) || clazz.equals(long.class); + } + + /** + * Return true if the fully qualified class is an enum. + * + * @param clazz class to check + * @return true if the fully qualified class is an enum. + */ + protected static boolean isEnumClass(String clazz) { + try { + return (Class.forName(clazz)).isEnum(); + } catch (ClassNotFoundException e) { + return false; + } + } + + /** + * Return the {@link Name} annotation value if it exists or null. + * + * @param clazz {@link Class} to check + * @return the {@link Name} annotation value if it exists or null + */ + protected static String getNameAnnotationValue(Class clazz) { + Name nameAnnotation = clazz.getAnnotation(Name.class); + if (nameAnnotation != null && !"".equals(nameAnnotation.value())) { + return nameAnnotation.value(); + } + return null; + } + + /** + * Return correct name for a Type or Enum based upon the value of the annotation or the {@link Name}. + * + * @param clazz {@link Class} to introspect. + * @return the correct name + */ + protected static String getTypeName(Class clazz) { + Type typeAnnotation = clazz.getAnnotation(Type.class); + Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); + Input inputAnnotation = clazz.getAnnotation(Input.class); + Enum enumAnnotation = clazz.getAnnotation(Enum.class); + Query queryAnnotation = clazz.getAnnotation(Query.class); + + String name = ""; + if (typeAnnotation != null) { + name = typeAnnotation.value(); + } else if (interfaceAnnotation != null) { + name = interfaceAnnotation.value(); + } else if (inputAnnotation != null) { + name = inputAnnotation.value(); + } else if (enumAnnotation != null) { + name = enumAnnotation.value(); + } else if (queryAnnotation != null) { + name = queryAnnotation.value(); + } + + if ("".equals(name)) { + name = getNameAnnotationValue(clazz); + } + + return name == null || name.isBlank() ? clazz.getSimpleName() : name; + } + + /** + * Checks a {@link SchemaType} for {@link SchemaFieldDefinition}s which contain known scalars and replace them with the scalar + * name. + * + * @param schema {@link Schema} to check scalars for. + * @param type {@link SchemaType} to check + */ + protected static void checkScalars(Schema schema, SchemaType type) { + type.getFieldDefinitions().forEach(fd -> { + SchemaScalar scalar = getScalar(fd.getReturnType()); + if (scalar != null) { + fd.setReturnType(scalar.getName()); + if (!schema.containsScalarWithName(scalar.getName())) { + schema.addScalar(scalar); + } + } + }); + } + +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/package-info.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/package-info.java deleted file mode 100644 index 6708d7b1f2f..00000000000 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/application/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. - * - * 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. - */ - -/** - * Microprofile GraphQL application classes. - * - */ -package io.helidon.microprofile.graphql.server.application; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/package-info.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/package-info.java deleted file mode 100644 index 5b00089857a..00000000000 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/model/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. - * - * 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. - */ - -/** - * Microprofile GraphQL model classes. - */ -package io.helidon.microprofile.graphql.server.model; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/package-info.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/package-info.java deleted file mode 100644 index 32439e9679e..00000000000 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/util/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. - * - * 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. - */ - -/** - * Microprofile GraphQL utility classes. - */ -package io.helidon.microprofile.graphql.server.util; diff --git a/microprofile/graphql/server/src/main/java/module-info.java b/microprofile/graphql/server/src/main/java/module-info.java index 24b231cf73a..7299393ceff 100644 --- a/microprofile/graphql/server/src/main/java/module-info.java +++ b/microprofile/graphql/server/src/main/java/module-info.java @@ -33,7 +33,7 @@ requires graphql.java.extended.scalars; requires microprofile.graphql.api; - exports io.helidon.microprofile.graphql.server.application; + exports io.helidon.microprofile.graphql.server; - opens io.helidon.microprofile.graphql.server.application to weld.core.impl; + opens io.helidon.microprofile.graphql.server to weld.core.impl; } \ No newline at end of file diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java index c6f10f34265..ade72978a92 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -16,7 +16,6 @@ package io.helidon.microprofile.graphql.server; -import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.HashMap; @@ -29,11 +28,8 @@ import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response; -import com.fasterxml.jackson.core.JsonProcessingException; import io.helidon.microprofile.cdi.Main; -import io.helidon.microprofile.graphql.server.util.JandexUtils; -import io.helidon.microprofile.graphql.server.util.JsonUtils; -import io.helidon.microprofile.server.Server; + import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.logging.LoggingFeature; import org.junit.jupiter.api.AfterAll; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java index 9765fdd3ecf..0f80ce24dbd 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java @@ -28,8 +28,7 @@ import graphql.ExecutionResult; import graphql.schema.GraphQLSchema; import graphql.schema.idl.SchemaPrinter; -import io.helidon.microprofile.graphql.server.model.Schema; -import io.helidon.microprofile.graphql.server.util.JandexUtils; + import org.hamcrest.CoreMatchers; import org.jboss.jandex.Index; import org.jboss.jandex.IndexWriter; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java index e8add8ec3af..7ddd3120db2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java @@ -25,9 +25,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import com.fasterxml.jackson.core.JsonProcessingException; import io.helidon.microprofile.graphql.server.test.types.Person; -import io.helidon.microprofile.graphql.server.util.JsonUtils; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.BeforeAll; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/resource/GraphQLResourceTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLResourceTest.java similarity index 92% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/resource/GraphQLResourceTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLResourceTest.java index 14280778f11..8ea9f12fc27 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/resource/GraphQLResourceTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLResourceTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.resource; +package io.helidon.microprofile.graphql.server; import org.junit.jupiter.api.Test; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java similarity index 97% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java index 8c603393a4f..ab5944728d0 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JandexUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java @@ -14,12 +14,11 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.util; +package io.helidon.microprofile.graphql.server; import java.io.File; import java.io.IOException; import java.util.Arrays; -import java.util.Set; import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; import org.jboss.jandex.ClassInfo; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JsonUtilsTest.java similarity index 98% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JsonUtilsTest.java index d6d7bcc481d..4ec495250a1 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/JsonUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JsonUtilsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.util; +package io.helidon.microprofile.graphql.server; import java.util.HashMap; import java.util.Map; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java similarity index 97% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaArgumentTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index 231bb2cb13f..7c3d5fe83e5 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; +import io.helidon.microprofile.graphql.server.SchemaArgument; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaDirectiveTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java similarity index 95% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaDirectiveTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java index 11739075abc..71da55559eb 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaDirectiveTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java @@ -14,8 +14,11 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; +import io.helidon.microprofile.graphql.server.SchemaArgument; +import io.helidon.microprofile.graphql.server.SchemaDirective; +import io.helidon.microprofile.graphql.server.SchemaScalar; import org.junit.jupiter.api.Test; import static graphql.introspection.Introspection.DirectiveLocation.FIELD; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaEnumTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java similarity index 96% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaEnumTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java index 5178bb8185f..4ec48eae3d1 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaEnumTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; +import io.helidon.microprofile.graphql.server.SchemaEnum; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java similarity index 98% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinitionTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java index fa8843a24eb..a4bb9c20d8b 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaFieldDefinitionTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import graphql.schema.DataFetcher; import graphql.schema.StaticDataFetcher; +import io.helidon.microprofile.graphql.server.SchemaArgument; +import io.helidon.microprofile.graphql.server.SchemaFieldDefinition; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaScalarTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java similarity index 93% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaScalarTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java index 5be833dedcd..b63739fc581 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaScalarTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.Date; import graphql.scalars.ExtendedScalars; +import io.helidon.microprofile.graphql.server.SchemaScalar; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java similarity index 92% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java index 49446d01138..6eef01b31d8 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; import java.util.ArrayList; import java.util.Date; @@ -22,6 +22,13 @@ import graphql.scalars.ExtendedScalars; import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; +import io.helidon.microprofile.graphql.server.Schema; +import io.helidon.microprofile.graphql.server.SchemaArgument; +import io.helidon.microprofile.graphql.server.SchemaDirective; +import io.helidon.microprofile.graphql.server.SchemaEnum; +import io.helidon.microprofile.graphql.server.SchemaInputType; +import io.helidon.microprofile.graphql.server.SchemaScalar; +import io.helidon.microprofile.graphql.server.SchemaType; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTypeTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java similarity index 97% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTypeTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java index 4668eb21f67..6f86282e74e 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/model/SchemaTypeTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java @@ -14,8 +14,12 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.model; +package io.helidon.microprofile.graphql.server; +import io.helidon.microprofile.graphql.server.SchemaArgument; +import io.helidon.microprofile.graphql.server.SchemaFieldDefinition; +import io.helidon.microprofile.graphql.server.SchemaInputType; +import io.helidon.microprofile.graphql.server.SchemaType; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index e4b5c620c45..e8ed3a716b9 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -32,7 +32,6 @@ import graphql.ExecutionResult; -import io.helidon.microprofile.graphql.server.model.Schema; import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; @@ -47,14 +46,12 @@ import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; import io.helidon.microprofile.graphql.server.test.types.Vehicle; import io.helidon.microprofile.graphql.server.test.types.VehicleIncident; -import io.helidon.microprofile.graphql.server.util.JandexUtils; -import io.helidon.microprofile.graphql.server.util.JsonUtils; -import io.helidon.microprofile.graphql.server.util.SchemaUtils; -import io.helidon.microprofile.graphql.server.util.SchemaUtilsTest; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -125,6 +122,7 @@ public void testLevel0() throws IOException, IntrospectionException, ClassNotFou } @Test + @Disabled public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassNotFoundException, IOException { setupIndex(indexFileName, MultiLevelListsAndArrays.class); SchemaUtils schemaUtils = new SchemaUtils(); @@ -202,6 +200,7 @@ public void testIds() throws IOException { @Test @SuppressWarnings("unchecked") + @Disabled public void testSimpleQueryGenerationNoArgs() throws IOException { setupIndex(indexFileName, SimpleQueriesNoArgs.class); ExecutionContext executionContext = new ExecutionContext<>(dummyContext); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java similarity index 87% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java index 37a32681f70..ccb36441cb6 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/util/SchemaUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.helidon.microprofile.graphql.server.util; +package io.helidon.microprofile.graphql.server; import java.beans.IntrospectionException; import java.math.BigDecimal; @@ -24,9 +24,6 @@ import java.util.List; import java.util.Map; -import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; -import io.helidon.microprofile.graphql.server.model.SchemaEnum; -import io.helidon.microprofile.graphql.server.model.Schema; import io.helidon.microprofile.graphql.server.test.enums.EnumTestNoEnumName; import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAndNameAnnotation; @@ -186,31 +183,29 @@ public void testInterfaceImplementorDiscovery2() throws IntrospectionException { @Test public void testTypeNames() { - SchemaUtils schemaUtils = new SchemaUtils(); - assertThat(schemaUtils.getTypeName(Address.class), is("Address")); - assertThat(schemaUtils.getTypeName(PersonWithName.class), is("Person")); - assertThat(schemaUtils.getTypeName(Person.class), is("Person")); - assertThat(schemaUtils.getTypeName(VehicleIncident.class), is("Incident")); - assertThat(schemaUtils.getTypeName(PersonWithNameValue.class), is("Person")); - assertThat(schemaUtils.getTypeName(Vehicle.class), is("Vehicle")); - assertThat(schemaUtils.getTypeName(EnumTestNoEnumName.class), is("EnumTestNoEnumName")); - assertThat(schemaUtils.getTypeName(EnumTestWithEnumName.class), is("TShirtSize")); - assertThat(schemaUtils.getTypeName(EnumTestWithNameAndNameAnnotation.class), is("ThisShouldWin")); - assertThat(schemaUtils.getTypeName(EnumTestWithNameAnnotation.class), is("TShirtSize")); - assertThat(schemaUtils.getTypeName(InnerClass.AnInnerClass.class), is("AnInnerClass")); - assertThat(schemaUtils.getTypeName(InnerClass.class), is("InnerClass")); - assertThat(schemaUtils.getTypeName(InterfaceWithTypeValue.class), is("NewName")); + assertThat(SchemaUtilsHelper.getTypeName(Address.class), is("Address")); + assertThat(SchemaUtilsHelper.getTypeName(PersonWithName.class), is("Person")); + assertThat(SchemaUtilsHelper.getTypeName(Person.class), is("Person")); + assertThat(SchemaUtilsHelper.getTypeName(VehicleIncident.class), is("Incident")); + assertThat(SchemaUtilsHelper.getTypeName(PersonWithNameValue.class), is("Person")); + assertThat(SchemaUtilsHelper.getTypeName(Vehicle.class), is("Vehicle")); + assertThat(SchemaUtilsHelper.getTypeName(EnumTestNoEnumName.class), is("EnumTestNoEnumName")); + assertThat(SchemaUtilsHelper.getTypeName(EnumTestWithEnumName.class), is("TShirtSize")); + assertThat(SchemaUtilsHelper.getTypeName(EnumTestWithNameAndNameAnnotation.class), is("ThisShouldWin")); + assertThat(SchemaUtilsHelper.getTypeName(EnumTestWithNameAnnotation.class), is("TShirtSize")); + assertThat(SchemaUtilsHelper.getTypeName(InnerClass.AnInnerClass.class), is("AnInnerClass")); + assertThat(SchemaUtilsHelper.getTypeName(InnerClass.class), is("InnerClass")); + assertThat(SchemaUtilsHelper.getTypeName(InterfaceWithTypeValue.class), is("NewName")); } @Test public void testGetSimpleName() throws ClassNotFoundException { - SchemaUtils schemaUtils = new SchemaUtils(); - assertThat(schemaUtils.getSimpleName(InnerClass.AnInnerClass.class.getName()), is("AnInnerClass")); - assertThat(schemaUtils.getSimpleName(SchemaUtils.INT), is(SchemaUtils.INT)); - assertThat(schemaUtils.getSimpleName(SchemaUtils.ID), is(SchemaUtils.ID)); - assertThat(schemaUtils.getSimpleName(SchemaUtils.BOOLEAN), is(SchemaUtils.BOOLEAN)); - assertThat(schemaUtils.getSimpleName(SchemaUtils.FLOAT), is(SchemaUtils.FLOAT)); - assertThat(schemaUtils.getSimpleName(SchemaUtils.STRING), is(SchemaUtils.STRING)); + assertThat(SchemaUtilsHelper.getSimpleName(InnerClass.AnInnerClass.class.getName()), is("AnInnerClass")); + assertThat(SchemaUtilsHelper.getSimpleName(SchemaUtilsHelper.INT), is((SchemaUtilsHelper.INT))); + assertThat(SchemaUtilsHelper.getSimpleName(SchemaUtilsHelper.ID), is(SchemaUtilsHelper.ID)); + assertThat(SchemaUtilsHelper.getSimpleName(SchemaUtilsHelper.BOOLEAN), is(SchemaUtilsHelper.BOOLEAN)); + assertThat(SchemaUtilsHelper.getSimpleName(SchemaUtilsHelper.FLOAT), is(SchemaUtilsHelper.FLOAT)); + assertThat(SchemaUtilsHelper.getSimpleName(SchemaUtilsHelper.STRING), is(SchemaUtilsHelper.STRING)); } @Test @@ -256,9 +251,9 @@ public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassN Map mapMethods = SchemaUtils .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(3)); - Schema schema = schemaUtils.generateSchemaFromClasses(MultiLevelListsAndArrays.class); - generateGraphQLSchema(schema); + assertThat(mapMethods.size(), is(7)); +// Schema schema = schemaUtils.generateSchemaFromClasses(MultiLevelListsAndArrays.class); +// generateGraphQLSchema(schema); } private void testEnum(Class clazz, String expectedName) throws IntrospectionException, ClassNotFoundException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java index 497790020e2..5acaabb38b2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java @@ -67,18 +67,28 @@ public class TestDB { public TestDB() { for (int i = 1; i <= MAX_PEOPLE; i++) { - Address homeAddress = generateHomeAddress(); - Address workAddress = generateWorkAddress(); - Address prevAddress1 = generateWorkAddress(); - Address prevAddress2 = generateWorkAddress(); - Person person = new Person(i, "Person " + i, homeAddress, workAddress, new BigDecimal(RANDOM.nextFloat()), - List.of("BA", "BA Hon"), - List.of(prevAddress1, prevAddress2), new int[0], new String[0], EMPTY_MAP, - LocalDate.now(), System.nanoTime(), BigDecimal.valueOf(10)); - allPeople.put(person.getPersonId(), person); + Person p = generatePerson(i); + allPeople.put(p.getPersonId(), p); + } } + /** + * Generate a random {@link Person}. + * @param personId person id to use + * @return a random {@link Person} + */ + public Person generatePerson(int personId) { + Address homeAddress = generateHomeAddress(); + Address workAddress = generateWorkAddress(); + Address prevAddress1 = generateWorkAddress(); + Address prevAddress2 = generateWorkAddress(); + return new Person(personId, "Person " + personId, homeAddress, workAddress, BigDecimal.valueOf(RANDOM.nextFloat()), + List.of("BA", "BA Hon"), + List.of(prevAddress1, prevAddress2), new int[0], new String[0], EMPTY_MAP, + LocalDate.now(), System.nanoTime(), BigDecimal.valueOf(10)); + } + public Person getPerson(int personId) { return allPeople.get(personId); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index a474626f0b3..3d63ede078d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -44,12 +44,12 @@ @ApplicationScoped public class SimpleQueriesNoArgs { - public SimpleQueriesNoArgs() { - } - @Inject private TestDB testDB; + public SimpleQueriesNoArgs() { + } + @Query public String hero() { return "R2-D2"; @@ -98,6 +98,18 @@ public TypeWithIDs returnTypeWithIDs() { public MultiLevelListsAndArrays returnLists() { List> listListBigDecimal = new ArrayList<>(); listListBigDecimal.add(Collections.singletonList(new BigDecimal(100))); - return new MultiLevelListsAndArrays(listListBigDecimal, null, null); + int[][] intMultiLevelArray = new int[2][]; + intMultiLevelArray[0] = new int[] { 1, 2, 3 }; + intMultiLevelArray[1] = new int[] { 4, 5, 6 }; + Person[][] personMultiLevelArray = new Person[3][]; + personMultiLevelArray[0] = new Person[] { testDB.generatePerson(1), testDB.generatePerson(2)}; + personMultiLevelArray[1] = new Person[] { testDB.generatePerson(3), testDB.generatePerson(4)}; + List listOfStringArrays = new ArrayList<>(); + listOfStringArrays.add(new String[] {"one", "two", "three"}); + listOfStringArrays.add(new String[] {"four", "five"}); + String[][][] multiStringArray = { { { "one", "two" }, { "three", "four" } }, { { "five", "six" }, { "seven", "eight" } } }; + + return new MultiLevelListsAndArrays(listListBigDecimal, null, null, intMultiLevelArray, + personMultiLevelArray, listOfStringArrays, multiStringArray); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java index 63984684ef0..5135b2053c8 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java @@ -30,24 +30,33 @@ public class MultiLevelListsAndArrays { private List> listOfListOfBigDecimal; private List> listOfListOfPerson; private List> listOfListOfInteger; + private int[][] intMultiLevelArray; + private Person[][] personMultiLevelArray; + private List listOfStringArrays; + private String[][][] multiStringArray; public MultiLevelListsAndArrays(List> listOfListOfBigDecimal, List> listOfListOfPerson, - List> listOfListOfInteger) { + List> listOfListOfInteger, + int[][] intMultiLevelArray, + Person[][] personMultiLevelArray, + List listOfStringArrays, + String[][][] multiStringArray) { this.listOfListOfBigDecimal = listOfListOfBigDecimal; this.listOfListOfPerson = listOfListOfPerson; this.listOfListOfInteger = listOfListOfInteger; - } - - public MultiLevelListsAndArrays() { + this.intMultiLevelArray = intMultiLevelArray; + this.personMultiLevelArray = personMultiLevelArray; + this.listOfStringArrays = listOfStringArrays; + this.multiStringArray = multiStringArray; } public List> getListOfListOfBigDecimal() { return listOfListOfBigDecimal; } - public void setListOfListOfBigDecimal(List> listOfListOfBigDecimaal) { - this.listOfListOfBigDecimal = listOfListOfBigDecimaal; + public void setListOfListOfBigDecimal(List> listOfListOfBigDecimal) { + this.listOfListOfBigDecimal = listOfListOfBigDecimal; } public List> getListOfListOfPerson() { @@ -65,4 +74,36 @@ public List> getListOfListOfInteger() { public void setListOfListOfInteger(List> listOfListOfInteger) { this.listOfListOfInteger = listOfListOfInteger; } + + public int[][] getIntMultiLevelArray() { + return intMultiLevelArray; + } + + public void setIntMultiLevelArray(int[][] intMultiLevelArray) { + this.intMultiLevelArray = intMultiLevelArray; + } + + public Person[][] getPersonMultiLevelArray() { + return personMultiLevelArray; + } + + public void setPersonMultiLevelArray(Person[][] personMultiLevelArray) { + this.personMultiLevelArray = personMultiLevelArray; + } + + public List getListOfStringArrays() { + return listOfStringArrays; + } + + public void setListOfStringArrays(List listOfStringArrays) { + this.listOfStringArrays = listOfStringArrays; + } + + public String[][][] getMultiStringArray() { + return multiStringArray; + } + + public void setMultiStringArray(String[][][] multiStringArray) { + this.multiStringArray = multiStringArray; + } } From 56f7e2465925b7992a278ecccf0d2fd686cffec0 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 11 Mar 2020 17:51:29 +0800 Subject: [PATCH 012/178] working on arrays --- .../graphql/server/SchemaFieldDefinition.java | 81 +++++++++++++---- .../graphql/server/SchemaGenerator.java | 20 ++--- .../graphql/server/SchemaUtils.java | 89 ++++++++++--------- .../graphql/server/SchemaUtilsHelper.java | 70 +++++++++++++++ .../graphql/server/SchemaArgumentTest.java | 1 - .../graphql/server/SchemaDirectiveTest.java | 3 - .../graphql/server/SchemaEnumTest.java | 2 - .../server/SchemaFieldDefinitionTest.java | 46 ++++++---- .../graphql/server/SchemaScalarTest.java | 2 +- .../graphql/server/SchemaTest.java | 9 +- .../graphql/server/SchemaTypeTest.java | 30 +++---- .../graphql/server/SchemaUtilsIT.java | 1 - .../graphql/server/SchemaUtilsTest.java | 64 ++++++++++++- 13 files changed, 296 insertions(+), 122 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index 3b7375006f5..6d0df547ecc 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -27,7 +27,7 @@ */ public class SchemaFieldDefinition extends AbstractDescriptiveElement implements SchemaGenerator { - /** + /** * Name of the field definition. */ private final String name; @@ -42,6 +42,11 @@ public class SchemaFieldDefinition extends AbstractDescriptiveElement */ private final boolean isArrayReturnType; + /** + * The number of array levels if return type is an array. + */ + private final int arrayLevels; + /** * Indicates if the return type is mandatory. */ @@ -60,17 +65,24 @@ public class SchemaFieldDefinition extends AbstractDescriptiveElement /** * Construct a {@link SchemaFieldDefinition}. * - * @param name field definition name - * @param returnType return type - * @param isArrayReturnType indicates if the return type is an array type such as a native array([]) or a List, Collection, etc - * @param isReturnTypeMandatory indicates if the return type is mandatory. - */ - public SchemaFieldDefinition(String name, String returnType, boolean isArrayReturnType, boolean isReturnTypeMandatory) { - this.name = name; - this.returnType = returnType; + * @param name field definition name + * @param returnType return type + * @param isArrayReturnType indicates if the return type is an array type such as a native array([]) or a List, + * Collection, etc + * @param isReturnTypeMandatory indicates if the return type is mandatory. + * @param arrayLevels the number of array levels if return type is an array. + */ + public SchemaFieldDefinition(String name, + String returnType, + boolean isArrayReturnType, + boolean isReturnTypeMandatory, + int arrayLevels) { + this.name = name; + this.returnType = returnType; this.listSchemaArguments = new ArrayList<>(); - this.isArrayReturnType = isArrayReturnType; + this.isArrayReturnType = isArrayReturnType; this.isReturnTypeMandatory = isReturnTypeMandatory; + this.arrayLevels = arrayLevels; } @Override @@ -80,17 +92,20 @@ public String getSchemaAsString() { if (listSchemaArguments.size() > 0) { sb.append(OPEN_PARENTHESES) - .append(NEWLINE) - .append(listSchemaArguments.stream() - .map(SchemaArgument::getSchemaAsString) - .collect(Collectors.joining(COMMA_SPACE + NEWLINE))); + .append(NEWLINE) + .append(listSchemaArguments.stream() + .map(SchemaArgument::getSchemaAsString) + .collect(Collectors.joining(COMMA_SPACE + NEWLINE))); sb.append(NEWLINE).append(CLOSE_PARENTHESES); } sb.append(COLON); if (isArrayReturnType()) { - sb.append(SPACER).append(OPEN_SQUARE).append(getReturnType()).append(CLOSE_SQUARE); + int count = getArrayLevels(); + sb.append(SPACER).append(repeat(count, OPEN_SQUARE)) + .append(getReturnType()) + .append(repeat(count, CLOSE_SQUARE)); } else { sb.append(SPACER).append(getReturnType()); } @@ -102,10 +117,21 @@ public String getSchemaAsString() { return sb.toString(); } + /** + * Repeate create a {@link String} with the value repeated the requested number of times. + * + * @param count number of times to repeat + * @param string {@link String} to repeat + * @return a new {@link String} + */ + private String repeat(int count, String string) { + return new String(new char[count]).replace("\0", string); + } + /** * Return the name for the field definition. * - * @return the name for the field definition + * @return the name for the field definition */ public String getName() { return name; @@ -113,6 +139,7 @@ public String getName() { /** * Return the {@link List} of {@link SchemaArgument}s. + * * @return the {@link List} of {@link SchemaArgument}s */ public List getArguments() { @@ -121,6 +148,7 @@ public List getArguments() { /** * Return the return type. + * * @return the return type */ public String getReturnType() { @@ -129,7 +157,8 @@ public String getReturnType() { /** * Indicates if the return type is an array type. - * @return if the return type is an array type + * + * @return if the return type is an array type */ public boolean isArrayReturnType() { return isArrayReturnType; @@ -137,7 +166,8 @@ public boolean isArrayReturnType() { /** * Indicates if the return type is mandatory. - * @return if the return type is mandatory + * + * @return if the return type is mandatory */ public boolean isReturnTypeMandatory() { return isReturnTypeMandatory; @@ -145,6 +175,7 @@ public boolean isReturnTypeMandatory() { /** * Return the {@link DataFetcher} for this {@link SchemaFieldDefinition}. + * * @return he {@link DataFetcher} for this {@link SchemaFieldDefinition} */ public DataFetcher getDataFetcher() { @@ -153,6 +184,7 @@ public DataFetcher getDataFetcher() { /** * Set the return type. + * * @param sReturnType the return type */ public void setReturnType(String sReturnType) { @@ -161,7 +193,8 @@ public void setReturnType(String sReturnType) { /** * Set the {@link DataFetcher} which will override the default {@link DataFetcher} for the field. - * @param dataFetcher the {@link DataFetcher} + * + * @param dataFetcher the {@link DataFetcher} */ public void setDataFetcher(DataFetcher dataFetcher) { this.dataFetcher = dataFetcher; @@ -176,6 +209,15 @@ public void addArgument(SchemaArgument schemaArgument) { listSchemaArguments.add(schemaArgument); } + /** + * Return the number of array levels if return type is an array. + * + * @return the number of array levels if return type is an array + */ + public int getArrayLevels() { + return arrayLevels; + } + @Override public String toString() { return "FieldDefinition{" @@ -184,6 +226,7 @@ public String toString() { + ", isArrayReturnType=" + isArrayReturnType + ", isReturnTypeMandatory=" + isReturnTypeMandatory + ", listArguments=" + listSchemaArguments + + ", arrayLevels=" + arrayLevels + ", description='" + getDescription() + '\'' + '}'; } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index be46a812e05..d034567ed88 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -50,6 +50,16 @@ public interface SchemaGenerator { */ String SPACER = " "; + /** + * Open square bracket. + */ + String OPEN_SQUARE = "["; + + /** + * Close square bracket. + */ + String CLOSE_SQUARE = "]"; + /** * Newline. */ @@ -80,16 +90,6 @@ public interface SchemaGenerator { */ char CLOSE_CURLY = '}'; - /** - * Open square bracket. - */ - char OPEN_SQUARE = '['; - - /** - * Close square bracket. - */ - char CLOSE_SQUARE = ']'; - /** * Open parenthesis. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java index 31591aae818..243a0606c5d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java @@ -54,14 +54,18 @@ import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.ID; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.PRIMITIVE_ARRAY_MAP; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.checkScalars; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getArrayLevels; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getFieldName; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getGraphQLType; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getMethodName; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getNameAnnotationValue; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getRootArrayClass; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getRootTypeName; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getSafeClass; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getScalar; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getSimpleName; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getTypeName; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.isArrayType; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.isEnumClass; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.isValidIDType; @@ -261,7 +265,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec * @throws ClassNotFoundException */ private SchemaType generateType(String realReturnType) - throws IntrospectionException, ClassNotFoundException { + throws IntrospectionException, ClassNotFoundException { String simpleName = getSimpleName(realReturnType); SchemaType type = new SchemaType(simpleName, realReturnType); @@ -464,12 +468,12 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String : method.name, getGraphQLType(sValueClassName), isArrayReturnType, - false); + false, + method.getArrayLevels()); fd.setDataFetcher(dataFetcher); return fd; } - /** * Look in the given {@link Schema} for any field definitions, arguments and key value classes that contain the return type of * the long return type and replace with short return type. @@ -499,7 +503,6 @@ private void updateLongTypes(Schema schema, String longReturnType, String shortR }); } - /** * Return a {@link Map} of all the discovered methods which have the {@link Query} annotation. * @@ -672,6 +675,8 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class discoveredMethod.setMethod(method); } + discoveredMethod.setArrayLevels(realReturnType.getArrayLevels()); + return discoveredMethod; } @@ -687,50 +692,25 @@ private static ReturnType getReturnType(Class returnClazz, java.lang.reflect. String returnClazzName = returnClazz.getName(); if (Collection.class.isAssignableFrom(returnClazz)) { actualReturnType.setCollectionType(returnClazzName); - if (genericReturnType instanceof ParameterizedType) { - ParameterizedType paramReturnType = (ParameterizedType) genericReturnType; - // loop until we get the actual return type in the case we have List> - java.lang.reflect.Type actualTypeArgument = paramReturnType.getActualTypeArguments()[0]; - while (actualTypeArgument instanceof ParameterizedType) { - ParameterizedType parameterizedType2 = (ParameterizedType) actualTypeArgument; - actualTypeArgument = parameterizedType2.getActualTypeArguments()[0]; - } - - actualReturnType.setReturnClass(actualTypeArgument.getTypeName()); + String rootType = getRootTypeName(genericReturnType, 0); + if (isArrayType(rootType)) { + actualReturnType.setReturnClass(getRootArrayClass(rootType)); } else { - actualReturnType.setReturnClass(Object.class.getName()); + actualReturnType.setReturnClass(rootType); } } else if (Map.class.isAssignableFrom(returnClazz)) { actualReturnType.setMap(true); - if (genericReturnType instanceof ParameterizedType) { - ParameterizedType paramReturnType = (ParameterizedType) genericReturnType; - // we are only interested in the value type as for this implementation we return a collection of values() - actualReturnType.setReturnClass(paramReturnType.getActualTypeArguments()[1].getTypeName()); + String rootType = getRootTypeName(genericReturnType, 1); + if (isArrayType(rootType)) { + actualReturnType.setReturnClass(getRootArrayClass(rootType)); } else { - actualReturnType.setReturnClass(Object.class.getName()); + actualReturnType.setReturnClass(rootType); } } else if (!returnClazzName.isEmpty() && returnClazzName.startsWith("[")) { - // return type is array of either primitives or Objects/Interfaces. + // return type is array of either primitives or Objects/Interface/Enum. actualReturnType.setArrayType(true); - String sPrimitiveType = PRIMITIVE_ARRAY_MAP.get(returnClazzName); - if (sPrimitiveType != null) { - actualReturnType.setReturnClass(sPrimitiveType); - } else { - // Array of Object/Interface - // We are only supporting single level arrays currently - int cArrayCount = 0; - for (int i = 0; i < returnClazzName.length(); i++) { - if (returnClazzName.charAt(i) == '[') { - cArrayCount++; - } - } - - if (cArrayCount > 1) { - LOGGER.warning("Multi-dimension arrays are not yet supported. Ignoring class " + returnClazzName); - } else { - actualReturnType.setReturnClass(returnClazzName.substring(2, returnClazzName.length() - 1)); - } - } + actualReturnType.setArrayLevels(getArrayLevels(returnClazzName)); + actualReturnType.setReturnClass(getRootArrayClass(returnClazzName)); } else { actualReturnType.setReturnClass(returnClazzName); } @@ -794,6 +774,11 @@ public static class DiscoveredMethod { */ private Method method; + /** + * Number of levels in the Array. + */ + private int arrayLevels = 0; + /** * Default constructor. */ @@ -944,6 +929,24 @@ public List getArguments() { return this.listArguments; } + /** + * Return the number of levels in the Array. + * + * @return Return the number of levels in the Array + */ + public int getArrayLevels() { + return arrayLevels; + } + + /** + * Sets the number of levels in the Array. + * + * @param arrayLevels the number of levels in the Array + */ + public void setArrayLevels(int arrayLevels) { + this.arrayLevels = arrayLevels; + } + /** * Add a {@link SchemaArgument}. * @@ -963,6 +966,7 @@ public String toString() { + ", isArrayReturnType=" + isArrayReturnType + ", isMap=" + isMap + ", listArguments=" + listArguments + + ", arrayLevels=" + arrayLevels + ", method=" + method + '}'; } @@ -978,6 +982,7 @@ public boolean equals(Object o) { return methodType == that.methodType && isArrayReturnType == that.isArrayReturnType && isMap == that.isMap + && arrayLevels == that.arrayLevels && Objects.equals(name, that.name) && Objects.equals(returnType, that.returnType) && Objects.equals(method, that.method) @@ -986,7 +991,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(name, returnType, methodType, method, + return Objects.hash(name, returnType, methodType, method, arrayLevels, collectionType, isArrayReturnType, isMap); } } @@ -1101,6 +1106,7 @@ public void setCollectionType(String collectionType) { /** * Return the level of arrays or 0 if not an array. + * * @return the level of arrays */ public int getArrayLevels() { @@ -1109,6 +1115,7 @@ public int getArrayLevels() { /** * Set the level of arrays or 0 if not an array. + * * @param arrayLevels the level of arrays or 0 if not an array */ public void setArrayLevels(int arrayLevels) { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java index 46552e7c94a..d31dc7211ed 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java @@ -18,6 +18,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; @@ -25,6 +26,7 @@ import java.time.OffsetDateTime; import java.time.OffsetTime; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,6 +43,8 @@ import org.eclipse.microprofile.graphql.Query; import org.eclipse.microprofile.graphql.Type; +import static io.helidon.microprofile.graphql.server.SchemaGenerator.OPEN_SQUARE; + /** * Helper class for {@link SchemaUtils}. */ @@ -379,4 +383,70 @@ protected static void checkScalars(Schema schema, SchemaType type) { }); } + /** + * Return the number of array levels in the class. + * + * @param clazz the class name retrieved via Class.getName() + * @return the number of array levels in the class + */ + protected static int getArrayLevels(String clazz) { + int c = 0; + for (int i = 0; i < clazz.length(); i++) { + if (clazz.charAt(i) == '[') { + c++; + } + } + return c; + } + + /** + * Indicates if the class is an array type. + * @param clazz the class name retrieved via Class.getName() + * @return true if the class is an array type + */ + protected static boolean isArrayType(String clazz) { + return clazz.startsWith(OPEN_SQUARE); + } + + /** + * Return the root array class from the given class. + * + * @param clazz the class name retrieved via Class.getName() + * @return the root class name + */ + protected static String getRootArrayClass(String clazz) { + if (clazz == null || "".equals(clazz.trim()) || clazz.length() < 2) { + throw new IllegalArgumentException("Class must be not null"); + } + // check to see if it is a primitive array + String type = PRIMITIVE_ARRAY_MAP.get(clazz.substring(clazz.length() - 2)); + if (type != null) { + return type; + } + // must be an object + return clazz.replaceAll("\\[", "").replaceAll(";", "").replaceAll("^L", ""); + } + + /** + * Return the inner most root type such as {@link String} for + *

List>
+ * + * @param genericReturnType the {@link java.lang.reflect.Type} + * @param index the index to use, either 0 for {@link Collection} or 1 for {@link Map} + * @return the inner most root type + */ + protected static String getRootTypeName(java.lang.reflect.Type genericReturnType, int index) { + if (genericReturnType instanceof ParameterizedType) { + ParameterizedType paramReturnType = (ParameterizedType) genericReturnType; + // loop until we get the actual return type in the case we have List> + java.lang.reflect.Type actualTypeArgument = paramReturnType.getActualTypeArguments()[index]; + while (actualTypeArgument instanceof ParameterizedType) { + ParameterizedType parameterizedType2 = (ParameterizedType) actualTypeArgument; + actualTypeArgument = parameterizedType2.getActualTypeArguments()[index]; + } + return ((Class) actualTypeArgument).getName(); + } else { + return ((Class) genericReturnType).getName(); + } + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index 7c3d5fe83e5..93f98c3e876 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -16,7 +16,6 @@ package io.helidon.microprofile.graphql.server; -import io.helidon.microprofile.graphql.server.SchemaArgument; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java index 71da55559eb..ed093139fdf 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java @@ -16,9 +16,6 @@ package io.helidon.microprofile.graphql.server; -import io.helidon.microprofile.graphql.server.SchemaArgument; -import io.helidon.microprofile.graphql.server.SchemaDirective; -import io.helidon.microprofile.graphql.server.SchemaScalar; import org.junit.jupiter.api.Test; import static graphql.introspection.Introspection.DirectiveLocation.FIELD; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java index 4ec48eae3d1..f7ea76aa5ea 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java @@ -16,8 +16,6 @@ package io.helidon.microprofile.graphql.server; -import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; -import io.helidon.microprofile.graphql.server.SchemaEnum; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java index a4bb9c20d8b..a8f97cbf4b2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java @@ -37,11 +37,12 @@ class SchemaFieldDefinitionTest { @Test public void testConstructors() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("name", "Integer", true, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("name", "Integer", true, true, 1); assertThat(schemaFieldDefinition.getName(), is("name")); assertThat(schemaFieldDefinition.getReturnType(), is("Integer")); assertThat(schemaFieldDefinition.getArguments(), is(notNullValue())); assertThat(schemaFieldDefinition.isArrayReturnType(), is(true)); + assertThat(schemaFieldDefinition.getArrayLevels(), is(1)); SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null, STRING); schemaFieldDefinition.addArgument(schemaArgument); @@ -53,11 +54,12 @@ public void testConstructors() { assertThat(schemaFieldDefinition.getArguments().size(), is(2)); assertThat(schemaFieldDefinition.getArguments().contains(schemaArgument2), is(true)); - schemaFieldDefinition = new SchemaFieldDefinition("name2", "String", false, false); + schemaFieldDefinition = new SchemaFieldDefinition("name2", "String", false, false, 0); assertThat(schemaFieldDefinition.getName(), is("name2")); assertThat(schemaFieldDefinition.getReturnType(), is("String")); assertThat(schemaFieldDefinition.isArrayReturnType(), is(false)); assertThat(schemaFieldDefinition.isReturnTypeMandatory(), is(false)); + assertThat(schemaFieldDefinition.getArrayLevels(), is(0)); schemaFieldDefinition.setReturnType("BLAH"); assertThat(schemaFieldDefinition.getReturnType(), is("BLAH")); @@ -65,33 +67,45 @@ public void testConstructors() { @Test public void testFieldDefinitionWithNoArguments() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person: Person!")); } @Test public void testFieldDefinitionWithNoArgumentsAndDescription() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.setDescription("Description"); assertThat(schemaFieldDefinition.getSchemaAsString(), is("\"Description\"\nperson: Person!")); } @Test - public void testFieldDefinitionWithNoArgumentsAndArrayType() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true); + public void testFieldDefinitionWithNoArgumentsAndArrayType1ArrayLevel() { + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true, 1); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person: [Person]!")); } + @Test + public void testFieldDefinitionWithNoArgumentsAndArrayType2ArrayLevels() { + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true, 2); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person: [[Person]]!")); + } + + @Test + public void testFieldDefinitionWithNoArgumentsAndArrayType3ArrayLevels() { + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("result", "String", true, true, 3); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("result: [[[String]]]!")); + } + @Test public void testFieldDefinitionWith1Argument() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): Person!")); } @Test public void testFieldDefinitionWith1ArgumentAndDescription() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument.setDescription("Optional Filter"); schemaFieldDefinition.addArgument(schemaArgument); @@ -100,28 +114,28 @@ public void testFieldDefinitionWith1ArgumentAndDescription() { @Test public void testFieldDefinitionWith1ArgumentAndArrayType() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true, 1); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): [Person]!")); } @Test public void testFieldDefinitionWith1MandatoryArgument() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", true, null, STRING)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String!\n): Person!")); } @Test public void testFieldDefinitionWith1MandatoryArgumentAndArrayType() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true, 1); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): [Person]!")); } @Test public void testFieldDefinitionWithMultipleArguments() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String, \nage: Int!\n): Person!")); @@ -129,7 +143,7 @@ public void testFieldDefinitionWithMultipleArguments() { @Test public void testFieldDefinitionWithMultipleArgumentsAndDescription() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument1.setDescription("Optional filter"); SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); @@ -142,7 +156,7 @@ public void testFieldDefinitionWithMultipleArgumentsAndDescription() { @Test public void testFieldDefinitionWithMultipleArgumentsAndBothDescriptions() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.setDescription("Description of field definition"); SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument1.setDescription("Optional filter"); @@ -157,7 +171,7 @@ public void testFieldDefinitionWithMultipleArgumentsAndBothDescriptions() { @Test public void testFieldDefinitionWithMultipleArgumentsWithArray() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, false); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, false, 1); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaFieldDefinition.addArgument(new SchemaArgument("job", "String", false, null, STRING)); @@ -167,7 +181,7 @@ public void testFieldDefinitionWithMultipleArgumentsWithArray() { @Test @SuppressWarnings("unchecked") public void testDataFetchers() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, false); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, false, 0); assertThat(schemaFieldDefinition.getDataFetcher(), is(nullValue())); schemaFieldDefinition.setDataFetcher(new StaticDataFetcher("Value")); DataFetcher dataFetcher = schemaFieldDefinition.getDataFetcher(); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java index b63739fc581..2fab67378ce 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java @@ -19,7 +19,7 @@ import java.util.Date; import graphql.scalars.ExtendedScalars; -import io.helidon.microprofile.graphql.server.SchemaScalar; + import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java index 6eef01b31d8..1ff5a48bef2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java @@ -21,14 +21,7 @@ import java.util.List; import graphql.scalars.ExtendedScalars; -import io.helidon.microprofile.graphql.server.AbstractGraphQLTest; -import io.helidon.microprofile.graphql.server.Schema; -import io.helidon.microprofile.graphql.server.SchemaArgument; -import io.helidon.microprofile.graphql.server.SchemaDirective; -import io.helidon.microprofile.graphql.server.SchemaEnum; -import io.helidon.microprofile.graphql.server.SchemaInputType; -import io.helidon.microprofile.graphql.server.SchemaScalar; -import io.helidon.microprofile.graphql.server.SchemaType; + import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java index 6f86282e74e..0aa1ff919e2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java @@ -16,10 +16,6 @@ package io.helidon.microprofile.graphql.server; -import io.helidon.microprofile.graphql.server.SchemaArgument; -import io.helidon.microprofile.graphql.server.SchemaFieldDefinition; -import io.helidon.microprofile.graphql.server.SchemaInputType; -import io.helidon.microprofile.graphql.server.SchemaType; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; @@ -42,10 +38,10 @@ public void testConstructors() { assertThat(schemaType.getValueClassName(), is("com.oracle.test.Value")); assertThat(schemaType.getFieldDefinitions(), is(notNullValue())); - schemaType.addFieldDefinition(new SchemaFieldDefinition("orderId", "Integer", false, true)); - schemaType.addFieldDefinition(new SchemaFieldDefinition("personId", "Integer", false, true)); - schemaType.addFieldDefinition(new SchemaFieldDefinition("personId", "Integer", false, true)); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("orders", "Order", true, true); + schemaType.addFieldDefinition(new SchemaFieldDefinition("orderId", "Integer", false, true, 0)); + schemaType.addFieldDefinition(new SchemaFieldDefinition("personId", "Integer", false, true, 0)); + schemaType.addFieldDefinition(new SchemaFieldDefinition("personId", "Integer", false, true, 0)); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("orders", "Order", true, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -60,7 +56,7 @@ public void testConstructors() { @Test public void testImplementingInterface() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -74,7 +70,7 @@ public void testImplementingInterface() { @Test public void testTypeSchemaOutput() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -89,7 +85,7 @@ public void testTypeSchemaOutput() { @Test public void testTypeSchemaOutputWithDescription() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); schemaType.setDescription("Type Description"); @@ -105,7 +101,7 @@ public void testTypeSchemaOutputWithDescription() { @Test public void testTypeStringOutputWith2Arguments() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -118,7 +114,7 @@ public void testTypeStringOutputWith2Arguments() { @Test public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument1.setDescription("Argument1 Description"); schemaFieldDefinition.addArgument(schemaArgument1); @@ -136,7 +132,7 @@ public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { @Test public void testTypeInterfaceStringOutputWith2Arguments() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, 30, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -152,11 +148,11 @@ public void testTypeInterfaceStringOutputWith2Arguments() { public void testTypeStringOutputWith2Fields() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); - SchemaFieldDefinition schemaFieldDefinition2 = new SchemaFieldDefinition("people", "Person", true, false); + SchemaFieldDefinition schemaFieldDefinition2 = new SchemaFieldDefinition("people", "Person", true, false, 1); schemaFieldDefinition2.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition2.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition2); @@ -171,7 +167,7 @@ public void testTypeStringOutputWith2Fields() { public void testCreatingInputTypeFromType() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true); + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index e8ed3a716b9..e5efa38d990 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -122,7 +122,6 @@ public void testLevel0() throws IOException, IntrospectionException, ClassNotFou } @Test - @Disabled public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassNotFoundException, IOException { setupIndex(indexFileName, MultiLevelListsAndArrays.class); SchemaUtils schemaUtils = new SchemaUtils(); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java index ccb36441cb6..7698c401c74 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java @@ -17,8 +17,13 @@ package io.helidon.microprofile.graphql.server; import java.beans.IntrospectionException; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.math.BigDecimal; import java.time.LocalDate; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -49,6 +54,7 @@ import io.helidon.microprofile.graphql.server.test.types.VehicleIncident; import org.junit.jupiter.api.Test; +import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getRootTypeName; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -225,10 +231,20 @@ public void testAllMethods() throws IntrospectionException { false, false, false); assertDiscoveredMethod(mapMethods.get("returnTypeWithIDs"), "returnTypeWithIDs", TypeWithIDs.class.getName(), null, false, false, false); - assertDiscoveredMethod(mapMethods.get("getMultiLevelList"), "getMultiLevelList", MultiLevelListsAndArrays.class.getName(), null, + assertDiscoveredMethod(mapMethods.get("getMultiLevelList"), "getMultiLevelList", MultiLevelListsAndArrays.class.getName(), + null, false, false, false); } + @Test + public void testArrayDiscoveredMethods() throws IntrospectionException { + Map mapMethods = SchemaUtils + .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class); + assertThat(mapMethods, is(notNullValue())); + assertThat(mapMethods.size(), is(9)); + assertDiscoveredMethod(mapMethods.get("multiStringArray"), "multiStringArray", STRING, null, true, false, false); + } + private void assertDiscoveredMethod(SchemaUtils.DiscoveredMethod discoveredMethod, String name, String returnType, @@ -252,8 +268,50 @@ public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassN .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); -// Schema schema = schemaUtils.generateSchemaFromClasses(MultiLevelListsAndArrays.class); -// generateGraphQLSchema(schema); + // Schema schema = schemaUtils.generateSchemaFromClasses(MultiLevelListsAndArrays.class); + // generateGraphQLSchema(schema); + } + + @Test + public void testArrayLevelsAndRootArray() { + String[] oneLevelString = new String[1]; + String[][] twoLevelString = new String[2][1]; + String[][][] threeLevelString = new String[2][1][2]; + int[] oneLevelInt = new int[1]; + int[][] twoLevelInt = new int[1][1]; + int[][][] threeLevelInt = new int[1][1][2]; + + assertThat(SchemaUtilsHelper.getArrayLevels(oneLevelString.getClass().getName()), is(1)); + assertThat(SchemaUtilsHelper.getArrayLevels(twoLevelString.getClass().getName()), is(2)); + assertThat(SchemaUtilsHelper.getArrayLevels(threeLevelString.getClass().getName()), is(3)); + assertThat(SchemaUtilsHelper.getArrayLevels(oneLevelInt.getClass().getName()), is(1)); + assertThat(SchemaUtilsHelper.getArrayLevels(twoLevelInt.getClass().getName()), is(2)); + assertThat(SchemaUtilsHelper.getArrayLevels(threeLevelInt.getClass().getName()), is(3)); + + assertThat(SchemaUtilsHelper.getRootArrayClass(oneLevelString.getClass().getName()), is(String.class.getName())); + assertThat(SchemaUtilsHelper.getRootArrayClass(twoLevelString.getClass().getName()), is(String.class.getName())); + assertThat(SchemaUtilsHelper.getRootArrayClass(threeLevelString.getClass().getName()), is(String.class.getName())); + assertThat(SchemaUtilsHelper.getRootArrayClass(oneLevelInt.getClass().getName()), is(int.class.getName())); + assertThat(SchemaUtilsHelper.getRootArrayClass(twoLevelInt.getClass().getName()), is(int.class.getName())); + assertThat(SchemaUtilsHelper.getRootArrayClass(threeLevelInt.getClass().getName()), is(int.class.getName())); + } + + private List listStringArray = new ArrayList<>(); + private List listString = new ArrayList<>(); + private List> listListString = new ArrayList<>(); + + @Test + public void testGetRootType() throws NoSuchFieldException { + ParameterizedType stringArrayListType = getParameterizedType("listStringArray"); + assertThat(getRootTypeName(stringArrayListType.getActualTypeArguments()[0], 0), is(String.class.getName())); + + ParameterizedType stringListType = getParameterizedType("listString"); + assertThat(getRootTypeName(stringListType.getActualTypeArguments()[0], 0), is(String.class.getName())); + } + + private ParameterizedType getParameterizedType(String fieldName) throws NoSuchFieldException { + Field listStringArray = SchemaUtilsTest.class.getDeclaredField(fieldName); + return (ParameterizedType) listStringArray.getGenericType(); } private void testEnum(Class clazz, String expectedName) throws IntrospectionException, ClassNotFoundException { From 06bc2545b6cbef9079d4c695bb3edea166cf934d Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 12 Mar 2020 09:19:45 +0800 Subject: [PATCH 013/178] fixup arrays and lists --- microprofile/graphql/server/pom.xml | 14 ++++ .../graphql/server/SchemaUtils.java | 17 ++++- .../graphql/server/SchemaUtilsHelper.java | 56 +++++++++++++- .../graphql/server/SchemaUtilsIT.java | 58 +++++++++------ .../graphql/server/SchemaUtilsTest.java | 20 +++-- .../graphql/server/test/db/TestDB.java | 2 +- .../test/queries/ArrayAndListQueries.java | 74 +++++++++++++++++++ .../test/queries/SimpleQueriesNoArgs.java | 4 +- .../test/queries/SimpleQueriesWithArgs.java | 2 +- .../test/types/MultiLevelListsAndArrays.java | 16 +++- 10 files changed, 222 insertions(+), 41 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 2f182f7f4d3..6d7ac19222e 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -51,6 +51,16 @@ io.helidon.microprofile.server helidon-microprofile-server + + + org.jboss.spec.javax.el + jboss-el-api_3.0_spec + + + org.jboss.spec.javax.interceptor + jboss-interceptors-api_1.2_spec + + io.helidon.microprofile.config @@ -61,6 +71,10 @@ javax.interceptor javax.interceptor-api + + javax.el-api + javax.el + com.fasterxml.jackson.core jackson-databind diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java index 243a0606c5d..44c15a4edbb 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java @@ -689,23 +689,36 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class */ private static ReturnType getReturnType(Class returnClazz, java.lang.reflect.Type genericReturnType) { ReturnType actualReturnType = new ReturnType(); + SchemaUtilsHelper.RootTypeResult rootTypeResult = null; String returnClazzName = returnClazz.getName(); if (Collection.class.isAssignableFrom(returnClazz)) { actualReturnType.setCollectionType(returnClazzName); - String rootType = getRootTypeName(genericReturnType, 0); + + rootTypeResult = getRootTypeName(genericReturnType, 0); + String rootType = rootTypeResult.getRootTypeName(); + // set the initial number of array levels to the levels of the root type + int arrayLevels = rootTypeResult.getLevels(); + if (isArrayType(rootType)) { actualReturnType.setReturnClass(getRootArrayClass(rootType)); + arrayLevels += getArrayLevels(rootType); } else { actualReturnType.setReturnClass(rootType); } + actualReturnType.setArrayLevels(arrayLevels); } else if (Map.class.isAssignableFrom(returnClazz)) { actualReturnType.setMap(true); - String rootType = getRootTypeName(genericReturnType, 1); + rootTypeResult = getRootTypeName(genericReturnType, 1); + String rootType = rootTypeResult.getRootTypeName(); + int arrayLevels = rootTypeResult.getLevels(); + if (isArrayType(rootType)) { actualReturnType.setReturnClass(getRootArrayClass(rootType)); + arrayLevels += getArrayLevels(rootType); } else { actualReturnType.setReturnClass(rootType); } + actualReturnType.setArrayLevels(arrayLevels); } else if (!returnClazzName.isEmpty() && returnClazzName.startsWith("[")) { // return type is array of either primitives or Objects/Interface/Enum. actualReturnType.setArrayType(true); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java index d31dc7211ed..ec9dab15491 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java @@ -401,6 +401,7 @@ protected static int getArrayLevels(String clazz) { /** * Indicates if the class is an array type. + * * @param clazz the class name retrieved via Class.getName() * @return true if the class is an array type */ @@ -432,21 +433,68 @@ protected static String getRootArrayClass(String clazz) { *
List>
* * @param genericReturnType the {@link java.lang.reflect.Type} - * @param index the index to use, either 0 for {@link Collection} or 1 for {@link Map} + * @param index the index to use, either 0 for {@link Collection} or 1 for {@link Map} * @return the inner most root type */ - protected static String getRootTypeName(java.lang.reflect.Type genericReturnType, int index) { + protected static RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnType, int index) { + int level = 1; if (genericReturnType instanceof ParameterizedType) { ParameterizedType paramReturnType = (ParameterizedType) genericReturnType; // loop until we get the actual return type in the case we have List> java.lang.reflect.Type actualTypeArgument = paramReturnType.getActualTypeArguments()[index]; while (actualTypeArgument instanceof ParameterizedType) { + level++; ParameterizedType parameterizedType2 = (ParameterizedType) actualTypeArgument; actualTypeArgument = parameterizedType2.getActualTypeArguments()[index]; } - return ((Class) actualTypeArgument).getName(); + return new RootTypeResult(((Class) actualTypeArgument).getName(), level); } else { - return ((Class) genericReturnType).getName(); + return new RootTypeResult(((Class) genericReturnType).getName(), level); + } + } + + /** + * Represents a result for the method getRootTypeName. + */ + public static class RootTypeResult { + + /** + * The root type of the {@link Collection} or {@link Map}. + */ + private final String rootTypeName; + + /** + * The number of levels in total. + */ + private final int levels; + + /** + * Construct a root type result. + * + * @param rootTypeName root type of the {@link Collection} or {@link Map} + * @param levels number of levels in total + */ + public RootTypeResult(String rootTypeName, int levels) { + this.rootTypeName = rootTypeName; + this.levels = levels; + } + + /** + * Return the root type of the {@link Collection} or {@link Map}. + * + * @return root type of the {@link Collection} or {@link Map} + */ + public String getRootTypeName() { + return rootTypeName; + } + + /** + * Return the number of levels in total. + * + * @return the number of levels in total + */ + public int getLevels() { + return levels; } } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index e5efa38d990..e2740ad3c77 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -34,6 +34,7 @@ import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; +import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; @@ -126,7 +127,7 @@ public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassN setupIndex(indexFileName, MultiLevelListsAndArrays.class); SchemaUtils schemaUtils = new SchemaUtils(); Schema schema = schemaUtils.generateSchema(); - assertThat(schema.containsTypeWithName("MultiLevelListsAndArrays"), is(true)); + assertThat(schema.containsTypeWithName("MultiMLevelListsAndArrays"), is(true)); assertThat(schema.containsTypeWithName("Person"), is(true)); assertThat(schema.containsScalarWithName("BigDecimal"), is(true)); generateGraphQLSchema(schema); @@ -250,11 +251,22 @@ public void testSimpleQueryGenerationNoArgs() throws IOException { assertThat(value.getLongPrimitiveId(), is(10L)); assertThat(value.getStringId(), is("string")); assertThat(value.getUuidId(), is(notNullValue())); - -// result = executionContext.execute("query { getMultiLevelList { listOfListOfBigDecimal } }"); -// mapResults = getAndAssertResult(result); -// assertThat(mapResults.size(), is(1)); -// assertThat(mapResults.get("getMultiLevelList"), is(notNullValue())); + + result = executionContext.execute("query { getMultiLevelList { listOfListOfBigDecimal } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("getMultiLevelList"), is(notNullValue())); + } + + @Test + public void testMultiLevelListsAndArraysQueries() throws IOException { + setupIndex(indexFileName, ArrayAndListQueries.class, MultiLevelListsAndArrays.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + + ExecutionResult result = executionContext.execute("query { getMultiLevelList { intMultiLevelArray } }"); + Map mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + // assertThat(mapResults.get("hero"), is("Luke")); } @Test @@ -268,6 +280,13 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("hero"), is("Luke")); + result = executionContext.execute("query { findLocalDates(numberOfValues: 10) }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findLocalDates"), is(notNullValue())); + List listLocalDate = (List) mapResults.get("findLocalDates"); + assertThat(listLocalDate.size(), is(10)); + result = executionContext.execute("query { canFindContact(contact: { id: \"10\" name: \"tim\" age: 52 }) }"); mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); @@ -289,24 +308,6 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("findAPerson"), is(notNullValue())); - result = executionContext.execute( - "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }"); - mapResults = getAndAssertResult(result); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("findPeopleFromState"), is(notNullValue())); - - ArrayList> arrayList = (ArrayList>) mapResults.get("findPeopleFromState"); - assertThat(arrayList, is(notNullValue())); - // since its random data we can't be sure if anyone was created in MA - assertThat(arrayList.size() >= 0, is(true)); - - result = executionContext.execute("query { findLocalDates(numberOfValues: 10) }"); - mapResults = getAndAssertResult(result); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("findLocalDates"), is(notNullValue())); - List listLocalDate = (List) mapResults.get("findLocalDates"); - assertThat(listLocalDate.size(), is(10)); - result = executionContext.execute("query { findEnums(arg0: S) }"); mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); @@ -355,6 +356,15 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("returnUUIDAsId"), is(uuid.toString())); + result = executionContext.execute( + "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findPeopleFromState"), is(notNullValue())); + ArrayList> arrayList = (ArrayList>) mapResults.get("findPeopleFromState"); + assertThat(arrayList, is(notNullValue())); + // since its random data we can't be sure if anyone was created in MA + assertThat(arrayList.size() >= 0, is(true)); } private void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java index 7698c401c74..12e451755f0 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java @@ -19,8 +19,7 @@ import java.beans.IntrospectionException; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; + import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; @@ -241,7 +240,7 @@ public void testArrayDiscoveredMethods() throws IntrospectionException { Map mapMethods = SchemaUtils .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(9)); + assertThat(mapMethods.size(), is(7)); assertDiscoveredMethod(mapMethods.get("multiStringArray"), "multiStringArray", STRING, null, true, false, false); } @@ -298,15 +297,24 @@ public void testArrayLevelsAndRootArray() { private List listStringArray = new ArrayList<>(); private List listString = new ArrayList<>(); - private List> listListString = new ArrayList<>(); + private List>> listListString = new ArrayList<>(); @Test public void testGetRootType() throws NoSuchFieldException { ParameterizedType stringArrayListType = getParameterizedType("listStringArray"); - assertThat(getRootTypeName(stringArrayListType.getActualTypeArguments()[0], 0), is(String.class.getName())); + SchemaUtilsHelper.RootTypeResult rootTypeName = getRootTypeName(stringArrayListType.getActualTypeArguments()[0], 0); + assertThat(rootTypeName.getRootTypeName(), is(String[].class.getName())); + assertThat(rootTypeName.getLevels(), is(1)); ParameterizedType stringListType = getParameterizedType("listString"); - assertThat(getRootTypeName(stringListType.getActualTypeArguments()[0], 0), is(String.class.getName())); + rootTypeName = getRootTypeName(stringListType.getActualTypeArguments()[0], 0); + assertThat(rootTypeName.getRootTypeName(), is(String.class.getName())); + assertThat(rootTypeName.getLevels(), is(1)); + + ParameterizedType listListStringType = getParameterizedType("listListString"); + rootTypeName = getRootTypeName(listListStringType.getActualTypeArguments()[0], 0); + assertThat(rootTypeName.getRootTypeName(), is(String.class.getName())); + assertThat(rootTypeName.getLevels(), is(2)); } private ParameterizedType getParameterizedType(String fieldName) throws NoSuchFieldException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java index 5acaabb38b2..2bddaac79f2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java @@ -61,7 +61,7 @@ public class TestDB { /** * Maximum number people to create. */ - public static final int MAX_PEOPLE = 100; + public static final int MAX_PEOPLE = 1000; private final Map allPeople = new HashMap<>(); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java new file mode 100644 index 00000000000..9a3094c92b1 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.json.bind.annotation.JsonbProperty; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; +import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; +import io.helidon.microprofile.graphql.server.test.types.Person; +import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds simple query definitions with no-argument. + */ +@GraphQLApi +@ApplicationScoped +public class ArrayAndListQueries { + + @Inject + private TestDB testDB; + + public ArrayAndListQueries() { + } + + @Query + @Name("getMultiLevelList") + public MultiLevelListsAndArrays returnLists() { + List> listListBigDecimal = new ArrayList<>(); + listListBigDecimal.add(Collections.singletonList(new BigDecimal(100))); + int[][] intMultiLevelArray = new int[2][]; + intMultiLevelArray[0] = new int[] { 1, 2, 3 }; + intMultiLevelArray[1] = new int[] { 4, 5, 6 }; + Person[][] personMultiLevelArray = new Person[3][]; + personMultiLevelArray[0] = new Person[] { testDB.generatePerson(1), testDB.generatePerson(2)}; + personMultiLevelArray[1] = new Person[] { testDB.generatePerson(3), testDB.generatePerson(4)}; + List listOfStringArrays = new ArrayList<>(); + listOfStringArrays.add(new String[] {"one", "two", "three"}); + listOfStringArrays.add(new String[] {"four", "five"}); + String[][][] multiStringArray = { { { "one", "two" }, { "three", "four" } }, { { "five", "six" }, { "seven", "eight" } } }; + Collection>> colColColString = new ArrayList<>(); + colColColString.add(Collections.singletonList(Collections.singleton("a"))); + + return new MultiLevelListsAndArrays(listListBigDecimal, null, null, intMultiLevelArray, + personMultiLevelArray, listOfStringArrays, multiStringArray, colColColString); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index 3d63ede078d..619818fdb1f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -108,8 +108,10 @@ public MultiLevelListsAndArrays returnLists() { listOfStringArrays.add(new String[] {"one", "two", "three"}); listOfStringArrays.add(new String[] {"four", "five"}); String[][][] multiStringArray = { { { "one", "two" }, { "three", "four" } }, { { "five", "six" }, { "seven", "eight" } } }; + Collection>> colColColString = new ArrayList<>(); + colColColString.add(Collections.singletonList(Collections.singleton("a"))); return new MultiLevelListsAndArrays(listListBigDecimal, null, null, intMultiLevelArray, - personMultiLevelArray, listOfStringArrays, multiStringArray); + personMultiLevelArray, listOfStringArrays, multiStringArray, colColColString); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java index b1aa6b7cd38..f5186cd2a43 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java @@ -18,7 +18,7 @@ import java.time.LocalDate; import java.util.ArrayList; -import java.util.Arrays; + import java.util.Collection; import java.util.Collections; import java.util.List; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java index 5135b2053c8..c9cd77a009c 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/MultiLevelListsAndArrays.java @@ -17,6 +17,7 @@ package io.helidon.microprofile.graphql.server.test.types; import java.math.BigDecimal; +import java.util.Collection; import java.util.List; import org.eclipse.microprofile.graphql.Type; @@ -30,9 +31,10 @@ public class MultiLevelListsAndArrays { private List> listOfListOfBigDecimal; private List> listOfListOfPerson; private List> listOfListOfInteger; + private List listOfStringArrays; + private Collection>> colColColString; private int[][] intMultiLevelArray; private Person[][] personMultiLevelArray; - private List listOfStringArrays; private String[][][] multiStringArray; public MultiLevelListsAndArrays(List> listOfListOfBigDecimal, @@ -41,7 +43,8 @@ public MultiLevelListsAndArrays(List> listOfListOfBigDecimal, int[][] intMultiLevelArray, Person[][] personMultiLevelArray, List listOfStringArrays, - String[][][] multiStringArray) { + String[][][] multiStringArray, + Collection>> colColColString) { this.listOfListOfBigDecimal = listOfListOfBigDecimal; this.listOfListOfPerson = listOfListOfPerson; this.listOfListOfInteger = listOfListOfInteger; @@ -49,6 +52,7 @@ public MultiLevelListsAndArrays(List> listOfListOfBigDecimal, this.personMultiLevelArray = personMultiLevelArray; this.listOfStringArrays = listOfStringArrays; this.multiStringArray = multiStringArray; + this.colColColString = colColColString; } public List> getListOfListOfBigDecimal() { @@ -106,4 +110,12 @@ public String[][][] getMultiStringArray() { public void setMultiStringArray(String[][][] multiStringArray) { this.multiStringArray = multiStringArray; } + + public Collection>> getColColColString() { + return colColColString; + } + + public void setColColColString(Collection>> colColColString) { + this.colColColString = colColColString; + } } From 9589fa2fd63f3812707677c6c2c454c8ec7500c1 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 12 Mar 2020 09:40:50 +0800 Subject: [PATCH 014/178] fixup missing scalars --- .../graphql/server/SchemaUtilsHelper.java | 16 ++++++++++++---- .../graphql/server/SchemaUtilsIT.java | 2 +- .../graphql/server/SchemaUtilsTest.java | 8 ++++---- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java index ec9dab15491..2d2520fa41c 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java @@ -22,9 +22,11 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -54,13 +56,19 @@ public class SchemaUtilsHelper { * List of supported scalars keyed by the full class name. */ private static final Map SUPPORTED_SCALARS = new HashMap<>() {{ - put(OffsetTime.class.getName(), new SchemaScalar("Time", OffsetTime.class.getName(), ExtendedScalars.Time)); - put(LocalTime.class.getName(), new SchemaScalar("LocalTime", OffsetTime.class.getName(), ExtendedScalars.Time)); put(Object.class.getName(), new SchemaScalar("Object", Object.class.getName(), ExtendedScalars.Object)); - put(OffsetDateTime.class.getName(), - new SchemaScalar("DateTime", OffsetDateTime.class.getName(), ExtendedScalars.DateTime)); + // Time scalars + put(OffsetTime.class.getName(), new SchemaScalar("Time", OffsetTime.class.getName(), ExtendedScalars.Time)); + put(LocalTime.class.getName(), new SchemaScalar("Time", OffsetTime.class.getName(), ExtendedScalars.Time)); + // DateTime scalars + put(OffsetDateTime.class.getName(), new SchemaScalar("DateTime", OffsetDateTime.class.getName(), ExtendedScalars.DateTime)); + put(ZonedDateTime.class.getName(), new SchemaScalar("DateTime", ZonedDateTime.class.getName(), ExtendedScalars.DateTime)); + put(LocalDateTime.class.getName(), new SchemaScalar("DateTime", LocalDateTime.class.getName(), ExtendedScalars.Date)); + // Date scalar put(LocalDate.class.getName(), new SchemaScalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); + // BigDecimal scalars put(BigDecimal.class.getName(), new SchemaScalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); + // BigInter scalars put(BigInteger.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); put(long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); put(Long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java index e2740ad3c77..8dce3a865ff 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsIT.java @@ -127,7 +127,7 @@ public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassN setupIndex(indexFileName, MultiLevelListsAndArrays.class); SchemaUtils schemaUtils = new SchemaUtils(); Schema schema = schemaUtils.generateSchema(); - assertThat(schema.containsTypeWithName("MultiMLevelListsAndArrays"), is(true)); + assertThat(schema.containsTypeWithName("MultiLevelListsAndArrays"), is(true)); assertThat(schema.containsTypeWithName("Person"), is(true)); assertThat(schema.containsScalarWithName("BigDecimal"), is(true)); generateGraphQLSchema(schema); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java index 12e451755f0..e4356163702 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaUtilsTest.java @@ -240,7 +240,7 @@ public void testArrayDiscoveredMethods() throws IntrospectionException { Map mapMethods = SchemaUtils .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(7)); + assertThat(mapMethods.size(), is(8)); assertDiscoveredMethod(mapMethods.get("multiStringArray"), "multiStringArray", STRING, null, true, false, false); } @@ -266,9 +266,9 @@ public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassN Map mapMethods = SchemaUtils .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(7)); - // Schema schema = schemaUtils.generateSchemaFromClasses(MultiLevelListsAndArrays.class); - // generateGraphQLSchema(schema); + assertThat(mapMethods.size(), is(8)); + Schema schema = schemaUtils.generateSchemaFromClasses(MultiLevelListsAndArrays.class); + generateGraphQLSchema(schema); } @Test From 9cfb81959f8dfb48fbcb0a84153d7068978b0948 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 12 Mar 2020 09:59:34 +0800 Subject: [PATCH 015/178] refactoring --- .../graphql/server/AbstractDescriptiveElement.java | 3 ++- .../graphql/server/DescriptiveElement.java | 8 ++++---- ...{SchemaGenerator.java => ElementGenerator.java} | 10 +++------- .../graphql/server/GraphQLApplication.java | 3 ++- .../microprofile/graphql/server/Schema.java | 14 ++------------ .../graphql/server/SchemaArgument.java | 5 +++-- .../graphql/server/SchemaDirective.java | 3 ++- .../microprofile/graphql/server/SchemaEnum.java | 5 +++-- .../graphql/server/SchemaFieldDefinition.java | 5 +++-- .../graphql/server/SchemaInputType.java | 3 ++- .../microprofile/graphql/server/SchemaScalar.java | 3 ++- .../microprofile/graphql/server/SchemaType.java | 5 +++-- .../microprofile/graphql/server/SchemaUtils.java | 3 +-- .../graphql/server/SchemaUtilsHelper.java | 2 +- 14 files changed, 33 insertions(+), 39 deletions(-) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{SchemaGenerator.java => ElementGenerator.java} (92%) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java index 1c797544d87..bf68ca71b23 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java @@ -21,7 +21,8 @@ /** * An abstract implementation of a {@link DescriptiveElement}. */ -public class AbstractDescriptiveElement implements DescriptiveElement { +public class AbstractDescriptiveElement + implements DescriptiveElement { /** * The description for an element. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java index 4286b487749..1e8213ee107 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java @@ -16,10 +16,10 @@ package io.helidon.microprofile.graphql.server; -import static io.helidon.microprofile.graphql.server.SchemaGenerator.NEWLINE; -import static io.helidon.microprofile.graphql.server.SchemaGenerator.NOTHING; -import static io.helidon.microprofile.graphql.server.SchemaGenerator.QUOTE; -import static io.helidon.microprofile.graphql.server.SchemaGenerator.TRIPLE_QUOTE; +import static io.helidon.microprofile.graphql.server.ElementGenerator.NEWLINE; +import static io.helidon.microprofile.graphql.server.ElementGenerator.NOTHING; +import static io.helidon.microprofile.graphql.server.ElementGenerator.QUOTE; +import static io.helidon.microprofile.graphql.server.ElementGenerator.TRIPLE_QUOTE; /** * Describes an element that has a description. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ElementGenerator.java similarity index 92% rename from microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java rename to microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ElementGenerator.java index d034567ed88..36e1059ac0f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ElementGenerator.java @@ -17,9 +17,10 @@ package io.helidon.microprofile.graphql.server; /** - * An interface to represent a element that can generate a schema. + * An interface representing a class which can generate + * a GraphQL representation of it's state. */ -public interface SchemaGenerator { +public interface ElementGenerator { /** * Empty string. */ @@ -30,11 +31,6 @@ public interface SchemaGenerator { */ String COMMA_SPACE = ", "; - /** - * Comma. - */ - String COMMA = ","; - /** * Triple quote. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLApplication.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLApplication.java index c9856a0fdcd..faf85a6d6ff 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLApplication.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLApplication.java @@ -28,7 +28,8 @@ */ @ApplicationScoped @ApplicationPath("/graphql") -public class GraphQLApplication extends Application { +public class GraphQLApplication + extends Application { @Override public Set> getClasses() { return Collections.singleton( diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java index aec48f739bd..23dafa71013 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java @@ -40,7 +40,8 @@ /** * The representation of a GraphQL Schema. */ -public class Schema implements SchemaGenerator { +public class Schema + implements ElementGenerator { private static final Logger LOGGER = Logger.getLogger(Schema.class.getName()); @@ -200,17 +201,6 @@ public RuntimeWiring getRuntimeWiring() { } final TypeRuntimeWiring.Builder typeRuntimeBuilder = newTypeWiring(getQueryName()); -// -// queryType.getFieldDefinitions().forEach(fd -> { -// String cacheMapping = fd.getCacheMapping(); -// if (fd.isArrayReturnType()) { -// typeRuntimeBuilder.dataFetcher(fd.getName(), -// DataFetcherUtils.newGenericFilterDataFetcher(cacheMapping)); -// } else { -// typeRuntimeBuilder.dataFetcher(fd.getName(), -// DataFetcherUtils.newGenericSingleKeyDataFetcher(cacheMapping, "key")); -// } -// }); // register a type resolver for any interfaces if we have at least one Set setInterfaces = getTypes().stream().filter(SchemaType::isInterface).collect(Collectors.toSet()); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index ae2909fd146..51b39d9ac7d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -21,8 +21,9 @@ /** * The representation of a GraphQL Argument or Parameter. */ -public class SchemaArgument extends AbstractDescriptiveElement - implements SchemaGenerator { +public class SchemaArgument + extends AbstractDescriptiveElement + implements ElementGenerator { /** * Argument name. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java index cee330ca8e5..84c705f82c7 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java @@ -27,7 +27,8 @@ /** * The representation of a GraphQL directive. */ -public class SchemaDirective implements SchemaGenerator { +public class SchemaDirective + implements ElementGenerator { /** * The name of the directive. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java index 4936ac17494..d9e1a5f73c7 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java @@ -23,8 +23,9 @@ /** * The representation of a GraphQL Enum. */ -public class SchemaEnum extends AbstractDescriptiveElement - implements SchemaGenerator { +public class SchemaEnum + extends AbstractDescriptiveElement + implements ElementGenerator { /** * The name of the enum. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index 6d0df547ecc..619d60a5b60 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -25,8 +25,9 @@ /** * The representation of a GraphQL Field Definition. */ -public class SchemaFieldDefinition extends AbstractDescriptiveElement - implements SchemaGenerator { +public class SchemaFieldDefinition + extends AbstractDescriptiveElement + implements ElementGenerator { /** * Name of the field definition. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java index 9b099d54ab3..11e9dfb5bcf 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java @@ -19,7 +19,8 @@ /** * The representation of a GraphQL Input Type. */ -public class SchemaInputType extends SchemaType { +public class SchemaInputType + extends SchemaType { /** * Construct an {@link SchemaInputType}. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java index ce789e036f8..09c950bf54c 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java @@ -23,7 +23,8 @@ /** * The representation of a GraphQL Scalar. */ -public class SchemaScalar implements SchemaGenerator { +public class SchemaScalar + implements ElementGenerator { /** * Name of the Scalar. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java index e2dcf092511..683a50eb14b 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java @@ -23,8 +23,9 @@ /** * The representation of a GraphQL Type. */ -public class SchemaType extends AbstractDescriptiveElement - implements SchemaGenerator { +public class SchemaType + extends AbstractDescriptiveElement + implements ElementGenerator { /** * Name of the type. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java index 44c15a4edbb..0b731559ce9 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtils.java @@ -24,7 +24,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; -import java.lang.reflect.ParameterizedType; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -52,7 +52,6 @@ import static io.helidon.microprofile.graphql.server.SchemaUtils.DiscoveredMethod.READ; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.ID; -import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.PRIMITIVE_ARRAY_MAP; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.checkScalars; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getArrayLevels; import static io.helidon.microprofile.graphql.server.SchemaUtilsHelper.getFieldName; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java index 2d2520fa41c..b92018596b9 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaUtilsHelper.java @@ -45,7 +45,7 @@ import org.eclipse.microprofile.graphql.Query; import org.eclipse.microprofile.graphql.Type; -import static io.helidon.microprofile.graphql.server.SchemaGenerator.OPEN_SQUARE; +import static io.helidon.microprofile.graphql.server.ElementGenerator.OPEN_SQUARE; /** * Helper class for {@link SchemaUtils}. From 4eaeeaf5f9a13dc71b4f6c52991123afb8755025 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 12 Mar 2020 10:38:30 +0800 Subject: [PATCH 016/178] copyright and checkstyle --- microprofile/graphql/pom.xml | 3 +- microprofile/graphql/runner/pom.xml | 3 +- microprofile/graphql/server/pom.xml | 3 +- .../graphql/server/DataFetcherUtils.java | 4 +- .../graphql/server/ExecutionContext.java | 8 +- .../graphql/server/GraphQLResource.java | 5 +- .../graphql/server/JsonUtils.java | 16 +++ .../microprofile/graphql/server/Schema.java | 2 +- ...{SchemaUtils.java => SchemaGenerator.java} | 46 ++++---- ...Helper.java => SchemaGeneratorHelper.java} | 17 +-- .../META-INF/microprofile-config.properties | 16 +++ ...emaUtilsIT.java => SchemaGeneratorIT.java} | 28 ++--- ...tilsTest.java => SchemaGeneratorTest.java} | 106 +++++++++--------- .../graphql/server/SchemaTest.java | 9 ++ .../META-INF/microprofile-config.properties | 15 +++ microprofile/graphql/tck/pom.xml | 3 +- 16 files changed, 172 insertions(+), 112 deletions(-) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{SchemaUtils.java => SchemaGenerator.java} (96%) rename microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/{SchemaUtilsHelper.java => SchemaGeneratorHelper.java} (97%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{SchemaUtilsIT.java => SchemaGeneratorIT.java} (95%) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{SchemaUtilsTest.java => SchemaGeneratorTest.java} (74%) diff --git a/microprofile/graphql/pom.xml b/microprofile/graphql/pom.xml index 11e36cddfdb..07bff30e128 100644 --- a/microprofile/graphql/pom.xml +++ b/microprofile/graphql/pom.xml @@ -1,4 +1,5 @@ - + org.apache.maven.plugins maven-dependency-plugin diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 1f5d6a04bb4..32c2c07441b 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -43,17 +43,28 @@ private DataFetcherUtils() { * * @param clazz {@link Class} to call * @param method {@link Method} to call + * @param source defines the source for a @Source annotation - may be null * @param args optional {@link SchemaArgument}s * @param value type * @return a new {@link DataFetcher} */ @SuppressWarnings({ "unchecked", "rawtypes" }) - public static DataFetcher newMethodDataFetcher(Class clazz, Method method, SchemaArgument... args) { + public static DataFetcher newMethodDataFetcher(Class clazz, Method method, String source, SchemaArgument... args) { Object instance = CDI.current().select(clazz).get(); return environment -> { - ArrayList listArgumentValues = new ArrayList<>(); + // only one @Source annotation should be present and it should be the first argument + if (source != null) { + Class sourceClazz; + try { + sourceClazz = Class.forName(source); + listArgumentValues.add(sourceClazz.cast(environment.getSource())); + } catch (ClassNotFoundException e) { + // this should not happen + } + } + if (args.length > 0) { for (SchemaArgument argument : args) { Object key = environment.getArgument(argument.getArgumentName()); @@ -81,26 +92,6 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met }; } - /** - * Create a new {@link DataFetcher} for a {@link Class} and {@link Method} which accepts the source as an argument. - * - * @param clazz {@link Class} to call - * @param method {@link Method} to call - * @param sourceClass the source class for the environment.getSource() - * @return a new {@link DataFetcher} - */ - public static DataFetcher newSourceMethodDataFetcher(Class clazz, Method method, String sourceClass) { - Object instance = CDI.current().select(clazz).get(); - Class sourceClazz; - try { - sourceClazz = Class.forName(sourceClass); - } catch (ClassNotFoundException e) { - // this should not happen - throw new RuntimeException("Cannot find class " + sourceClass); - } - return environment -> method.invoke(instance, (sourceClazz.cast(environment.getSource()))); - } - /** * Convert the ID type back to the original type for the method call. * diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index 51b39d9ac7d..90a3e79c3f6 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -49,6 +49,12 @@ public class SchemaArgument */ private final Class originalType; + /** + * Indicates if this argument is a source argument, which should be excluded from the + * query parameters. + */ + private boolean sourceArgument; + /** * Construct a {@link SchemaArgument} instance. * @@ -155,6 +161,22 @@ public Class getOriginalType() { return originalType; } + /** + * Indicates if the argument is a source argument. + * @return if the argument is a source argument. + */ + public boolean isSourceArgument() { + return sourceArgument; + } + + /** + * Set if the argument is a source argument. + * @param sourceArgument if the argument is a source argument. + */ + public void setSourceArgument(boolean sourceArgument) { + this.sourceArgument = sourceArgument; + } + @Override public String toString() { return "Argument{" @@ -163,6 +185,7 @@ public String toString() { + ", isMandatory=" + isMandatory + ", defaultValue=" + defaultValue + ", originalType=" + originalType + + ", sourceArgument=" + sourceArgument + ", description='" + getDescription() + '\'' + '}'; } @@ -182,13 +205,14 @@ public boolean equals(Object o) { && Objects.equals(argumentName, schemaArgument.argumentName) && Objects.equals(argumentType, schemaArgument.argumentType) && Objects.equals(originalType, schemaArgument.originalType) + && Objects.equals(sourceArgument, schemaArgument.sourceArgument) && Objects.equals(getDescription(), schemaArgument.getDescription()) && Objects.equals(defaultValue, schemaArgument.defaultValue); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), argumentName, argumentType, + return Objects.hash(super.hashCode(), argumentName, argumentType, sourceArgument, isMandatory, defaultValue, getDescription(), originalType); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 576252521d2..24192aadd2d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -278,12 +278,20 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec }); // process any additional methods requires via the @Source annotation - for (DiscoveredMethod dm : setAdditionalMethods) {// add the discovered method to the type + for (DiscoveredMethod dm : setAdditionalMethods) { + // add the discovered method to the type SchemaType type = schema.getTypeByClass(dm.source); if (type != null) { SchemaFieldDefinition fd = newFieldDefinition(dm, null); - fd.setDataFetcher(DataFetcherUtils.newSourceMethodDataFetcher( - dm.method.getDeclaringClass(), dm.method, dm.getSource())); + // add all arguments which are not source arguments + if (dm.getArguments().size() > 0) { + dm.getArguments().stream().filter(a -> !a.isSourceArgument()) + .forEach(fd::addArgument); + } + + fd.setDataFetcher(DataFetcherUtils.newMethodDataFetcher( + dm.method.getDeclaringClass(), dm.method, dm.getSource(), + fd.getArguments().toArray(new SchemaArgument[0]))); type.addFieldDefinition(fd); String simpleName = getSimpleName(fd.getReturnType()); @@ -450,7 +458,7 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, if (fd != null) { DataFetcher dataFetcher = DataFetcherUtils - .newMethodDataFetcher(clazz, method, fd.getArguments().toArray(new SchemaArgument[0])); + .newMethodDataFetcher(clazz, method, null, fd.getArguments().toArray(new SchemaArgument[0])); fd.setDataFetcher(dataFetcher); schemaType.addFieldDefinition(fd); @@ -616,7 +624,7 @@ protected static Map retrieveAllAnnotatedBeanMethods(C if (isQuery || isMutation || hasSourceAnnotation) { LOGGER.info("Processing Query or Mutation " + m.getName()); DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null); - discoveredMethod.setMethodType(isQuery ? QUERY_TYPE : MUTATION_TYPE); + discoveredMethod.setMethodType(isQuery || hasSourceAnnotation ? QUERY_TYPE : MUTATION_TYPE); mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); } } @@ -776,14 +784,16 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class returnType.setReturnClass(ID); } + SchemaArgument argument = new SchemaArgument(parameterName, returnType.getReturnClass(), false, null, paramType); + Source sourceAnnotation = parameter.getAnnotation(Source.class); if (sourceAnnotation != null) { discoveredMethod.setSource(returnType.getReturnClass()); discoveredMethod.setQueryAnnotated(method.getAnnotation(Query.class) != null); + argument.setSourceArgument(true); } - discoveredMethod - .addArgument(new SchemaArgument(parameterName, returnType.getReturnClass(), false, null, paramType)); + discoveredMethod.addArgument(argument); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index 93f98c3e876..2745ed20f77 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -29,7 +29,7 @@ class SchemaArgumentTest { private static final Class STRING = String.class; private static final Class INTEGER = Integer.class; - + @Test public void testConstructors() { SchemaArgument schemaArgument = new SchemaArgument("name", "Integer", true, null, INTEGER); @@ -51,7 +51,11 @@ public void testConstructors() { assertThat(schemaArgument.getDescription(), is(nullValue())); schemaArgument.setDescription("description"); - assertThat(schemaArgument.getDescription(), is("description")); + assertThat(schemaArgument.getDescription(), is("description")); + + assertThat(schemaArgument.isSourceArgument(), is(false)); + schemaArgument.setSourceArgument(true); + assertThat(schemaArgument.isSourceArgument(), is(true)); } @Test @@ -59,13 +63,13 @@ public void testSchemaGeneration() { SchemaArgument schemaArgument = new SchemaArgument("name", "Integer", true, null, INTEGER); assertThat(schemaArgument.getSchemaAsString(), is("name: Integer!")); - schemaArgument = new SchemaArgument("name", "Integer", true, 10,INTEGER); + schemaArgument = new SchemaArgument("name", "Integer", true, 10, INTEGER); assertThat(schemaArgument.getSchemaAsString(), is("name: Integer! = 10")); - schemaArgument = new SchemaArgument("name", "Integer", false, null,INTEGER); + schemaArgument = new SchemaArgument("name", "Integer", false, null, INTEGER); assertThat(schemaArgument.getSchemaAsString(), is("name: Integer")); - schemaArgument = new SchemaArgument("name", "Integer", false, 10,INTEGER); + schemaArgument = new SchemaArgument("name", "Integer", false, 10, INTEGER); assertThat(schemaArgument.getSchemaAsString(), is("name: Integer = 10")); schemaArgument = new SchemaArgument("name", "String", false, "The Default Value", STRING); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index c92f8cb1b3a..b6f34beacd9 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -388,7 +388,6 @@ public void testSimpleQueriesWithSource() throws IOException { result = executionContext.execute("query { currentJob (" + json + ") }"); mapResults = getAndAssertResult(result); - assertThat(mapResults.size(), is(1)); String currentJob = (String) mapResults.get("currentJob"); assertThat(currentJob, is(notNullValue())); @@ -402,6 +401,17 @@ public void testSimpleQueriesWithSource() throws IOException { assertThat(mapResults2.get("id"), is(notNullValue())); assertThat(mapResults2.get("idAndName"), is(notNullValue())); assertThat(mapResults2.get("currentJob"), is(notNullValue())); + + // test the query from the object + result = executionContext.execute("query { findContact { id lastNAddress(count: 1) { city } } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + mapResults2 = (Map) mapResults.get("findContact"); + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("id"), is(notNullValue())); + assertThat(mapResults2.get("lastNAddress"), is(notNullValue())); + + // lastNAddress on top level query } @Test @@ -565,8 +575,8 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { private String getContactAsQueryInput(SimpleContact contact) { return new StringBuilder("{") - .append("id: \"").append(contact.getId()).append("\"") - .append("name: \"").append(contact.getName()).append("\"") + .append("id: \"").append(contact.getId()).append("\" ") + .append("name: \"").append(contact.getName()).append("\" ") .append("age: ").append(contact.getAge()) .append("} ").toString(); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithSource.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithSource.java index 19863a4a9a6..6609461a6d2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithSource.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithSource.java @@ -20,7 +20,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -76,4 +78,12 @@ public SimpleContact retrieveSimpleContact() { public Address returnTheLastAddress(@Source @Name("contact") SimpleContact contact) { return testDB.generateWorkAddress(); } + + @Name("lastNAddress") + public Collection
returnTheLastNAddress(@Source @Name("contact") SimpleContact contact, @Name("count") int count) { + Set
setAddresses = new HashSet<>(); + setAddresses.add(testDB.generateWorkAddress()); + setAddresses.add(testDB.generateHomeAddress()); + return setAddresses; + } } diff --git a/microprofile/graphql/tck/pom.xml b/microprofile/graphql/tck/pom.xml index a2cb865a562..14eb6785941 100644 --- a/microprofile/graphql/tck/pom.xml +++ b/microprofile/graphql/tck/pom.xml @@ -1,5 +1,4 @@ - - - io.helidon.microprofile.bundles - internal-test-libs + org.jboss.arquillian.testng + arquillian-testng-container test @@ -72,13 +81,6 @@ - - org.sonatype.plugins - nexus-staging-maven-plugin - - true - - From 8e2e775ba05279b556fda5a7f929ae3bb2d8a5ff Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 17 Mar 2020 12:18:05 +0800 Subject: [PATCH 031/178] checkstyle --- .../graphql/server/SchemaGenerator.java | 92 ++++++++++--------- microprofile/graphql/tck/pom.xml | 29 ++++-- .../HelidonGraphQLArchiveProcessor.java | 55 +++++++++++ .../graphql/HelidonGraphQLExtension.java | 32 +++++++ .../META-INF/microprofile-config.properties | 17 ++++ 5 files changed, 177 insertions(+), 48 deletions(-) create mode 100644 microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLArchiveProcessor.java create mode 100644 microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLExtension.java create mode 100644 microprofile/graphql/tck/src/test/resources/META-INF/microprofile-config.properties diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 24192aadd2d..122ffd7693f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -221,47 +221,8 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec schema.addType(rootQueryType); schema.addType(rootMutationType); - // create any types that are still unresolved. e.g. an Order that contains OrderLine objects - // we must also ensure if the unresolved type contains another unresolved type then we process it - while (setUnresolvedTypes.size() > 0) { - String returnType = setUnresolvedTypes.iterator().next(); - - setUnresolvedTypes.remove(returnType); - try { - //LOGGER.info("Checking unresolved type " + returnType); - String simpleName = getSimpleName(returnType); - - SchemaScalar scalar = getScalar(returnType); - if (scalar != null) { - if (!schema.containsScalarWithName(scalar.getName())) { - schema.addScalar(scalar); - } - // update the return type with the scalar - updateLongTypes(schema, returnType, scalar.getName()); - } else if (isEnumClass(returnType)) { - SchemaEnum newEnum = generateEnum(Class.forName(returnType)); - if (!schema.containsEnumWithName(simpleName)) { - schema.addEnum(newEnum); - } - updateLongTypes(schema, returnType, newEnum.getName()); - } else { - // we will either know this type already or need to add it - boolean fExists = schema.getTypes().stream() - .filter(t -> t.getName().equals(simpleName)).count() > 0; - if (!fExists) { - SchemaType newType = generateType(returnType); - - // update any return types to the discovered scalars - checkScalars(schema, newType); - schema.addType(newType); - } - // need to update any FieldDefinitions that contained the original "long" type of c - updateLongTypes(schema, returnType, simpleName); - } - } catch (Exception e) { - throw new RuntimeException("Cannot get GraphQL type for " + returnType, e); - } - } + // process unresolved types + processUnresolvedTypes(schema); // look though all of interface type and see if any of the known types implement // the interface and if so, add the interface to the type @@ -311,6 +272,55 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec return schema; } + /** + * Process any unresolved types. + * + * @param schema {@link Schema} to add types to + */ + private void processUnresolvedTypes(Schema schema) { + // create any types that are still unresolved. e.g. an Order that contains OrderLine objects + // also ensure if the unresolved type contains another unresolved type then we process it + while (setUnresolvedTypes.size() > 0) { + String returnType = setUnresolvedTypes.iterator().next(); + + setUnresolvedTypes.remove(returnType); + try { + //LOGGER.info("Checking unresolved type " + returnType); + String simpleName = getSimpleName(returnType); + + SchemaScalar scalar = getScalar(returnType); + if (scalar != null) { + if (!schema.containsScalarWithName(scalar.getName())) { + schema.addScalar(scalar); + } + // update the return type with the scalar + updateLongTypes(schema, returnType, scalar.getName()); + } else if (isEnumClass(returnType)) { + SchemaEnum newEnum = generateEnum(Class.forName(returnType)); + if (!schema.containsEnumWithName(simpleName)) { + schema.addEnum(newEnum); + } + updateLongTypes(schema, returnType, newEnum.getName()); + } else { + // we will either know this type already or need to add it + boolean fExists = schema.getTypes().stream() + .filter(t -> t.getName().equals(simpleName)).count() > 0; + if (!fExists) { + SchemaType newType = generateType(returnType); + + // update any return types to the discovered scalars + checkScalars(schema, newType); + schema.addType(newType); + } + // need to update any FieldDefinitions that contained the original "long" type of c + updateLongTypes(schema, returnType, simpleName); + } + } catch (Exception e) { + throw new RuntimeException("Cannot get GraphQL type for " + returnType, e); + } + } + } + /** * Generate a {@link SchemaType} from a given class. * diff --git a/microprofile/graphql/tck/pom.xml b/microprofile/graphql/tck/pom.xml index 14eb6785941..c417ad01f28 100644 --- a/microprofile/graphql/tck/pom.xml +++ b/microprofile/graphql/tck/pom.xml @@ -38,12 +38,13 @@ - + - io.helidon.microprofile.graphql - helidon-microprofile-graphql-server - 2.0.0-SNAPSHOT + io.helidon.microprofile.tests + helidon-arquillian + ${project.version} + test org.eclipse.microprofile.graphql @@ -51,10 +52,21 @@ test - org.jboss.arquillian.testng - arquillian-testng-container + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-depchain + pom + test + + + org.jboss.arquillian.junit + arquillian-junit-container test + + io.helidon.microprofile.graphql + helidon-microprofile-graphql-server + 2.0.0-SNAPSHOT + @@ -71,14 +83,17 @@ - org.apache.maven.plugins maven-surefire-plugin + false org.eclipse.microprofile.graphql:microprofile-graphql-tck + + tier=integration + diff --git a/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLArchiveProcessor.java b/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLArchiveProcessor.java new file mode 100644 index 00000000000..643fe424a21 --- /dev/null +++ b/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLArchiveProcessor.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql; + +import java.io.File; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor; +import org.jboss.arquillian.test.spi.TestClass; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.resolver.api.maven.Maven; + +/** + * Creates a deployable unit with all the dependencies. + */ +public class HelidonGraphQLArchiveProcessor + implements ApplicationArchiveProcessor { + @Override + public void process(Archive applicationArchive, TestClass testClass) { + if (applicationArchive instanceof WebArchive) { + WebArchive testDeployment = (WebArchive) applicationArchive; + + final File[] dependencies = Maven.resolver() + .loadPomFromFile("pom.xml") + .resolve("io.helidon.microprofile.graphql:helidon-microprofile-graphql-server") + .withTransitivity() + .asFile(); + // Make sure it's unique + Set dependenciesSet = new LinkedHashSet<>(Arrays.asList(dependencies)); + testDeployment.addAsLibraries(dependenciesSet.toArray(new File[] {})); + // MicroProfile properties + testDeployment.addAsResource( + HelidonGraphQLArchiveProcessor.class.getClassLoader() + .getResource("META-INF/microprofile-config.properties"), + "META-INF/microprofile-config.properties"); + } + } +} diff --git a/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLExtension.java b/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLExtension.java new file mode 100644 index 00000000000..40cf8f08a46 --- /dev/null +++ b/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLExtension.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql; + +import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor; +import org.jboss.arquillian.core.spi.LoadableExtension; + +/** + * Active the extension for TCK tests. + */ +public class HelidonGraphQLExtension + implements LoadableExtension { + + @Override + public void register(ExtensionBuilder extensionBuilder) { + extensionBuilder.service(ApplicationArchiveProcessor.class, HelidonGraphQLArchiveProcessor.class); + } +} diff --git a/microprofile/graphql/tck/src/test/resources/META-INF/microprofile-config.properties b/microprofile/graphql/tck/src/test/resources/META-INF/microprofile-config.properties new file mode 100644 index 00000000000..bbe6485011d --- /dev/null +++ b/microprofile/graphql/tck/src/test/resources/META-INF/microprofile-config.properties @@ -0,0 +1,17 @@ +# +# Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. +# +# 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. +# + +mp.graphql.printDataFetcherException=true From bef5d383e659dbe64355a604c18081271206a508 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 17 Mar 2020 13:36:40 +0800 Subject: [PATCH 032/178] add in missed tests for Input annotation --- .../graphql/server/ExecutionContext.java | 13 +- .../graphql/server/SchemaGenerator.java | 122 ++++++++++-------- .../graphql/server/SchemaGeneratorHelper.java | 8 +- .../graphql/server/SchemaType.java | 10 +- .../graphql/server/SchemaGeneratorIT.java | 18 ++- .../graphql/server/SchemaTypeTest.java | 3 + .../test/types/SimpleContactInputType.java | 83 ++++++++++++ .../SimpleContactInputTypeWithAddress.java | 74 +++++++++++ .../types/SimpleContactInputTypeWithName.java | 83 ++++++++++++ .../SimpleContactInputTypeWithNameValue.java | 85 ++++++++++++ 10 files changed, 432 insertions(+), 67 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputType.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithAddress.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithName.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithNameValue.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index d9ef3ab3882..2d56f0ff57f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -16,6 +16,7 @@ package io.helidon.microprofile.graphql.server; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; @@ -42,7 +43,7 @@ public class ExecutionContext { /** * An empty map. */ - protected static final Map EMPTY_MAP = new HashMap<>(); + static final Map EMPTY_MAP = Collections.emptyMap(); /** * {@link GraphQL} instance to use for execution. @@ -107,11 +108,11 @@ public ExecutionContext(C context) { GraphQL.Builder builder = GraphQL.newGraphQL(this.graphQLSchema) .subscriptionExecutionStrategy(new SubscriptionExecutionStrategy()); - Instrumentation instrumentation = null; // getInstrumentation(); - - if (instrumentation != null) { - builder.instrumentation(instrumentation); - } +// Instrumentation instrumentation = null; // getInstrumentation(); +// +// if (instrumentation != null) { +// builder.instrumentation(instrumentation); +// } graphQL = builder.build(); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 122ffd7693f..1b531d113f0 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -205,10 +205,18 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec } } else if (inputAnnotation != null) { - // InputType - String inputTypeName = inputAnnotation.value(); - listSchemaTypes.add(new SchemaInputType(inputTypeName.isBlank() ? clazz.getSimpleName() : inputTypeName, - clazz.getName())); + String clazzName = clazz.getName(); + String simpleName = clazz.getSimpleName(); + + SchemaInputType inputType = generateType(clazzName).createInputType(""); + // if the name of the InputType was not changed then append "Input" + if (inputType.getName().equals(simpleName)) { + inputType.setName(inputType.getName() + "Input"); + } + if (!schema.containsInputTypeWithName(inputType.getName())) { + schema.addInputType(inputType); + checkInputType(schema, inputType); + } } // obtain top level query API's @@ -423,42 +431,8 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, } a.setArgumentType(inputType.getName()); - // if this new Type contains any types, then they must also have - // InputTypes created for them if they are not enums or scalars - Set setInputTypes = new HashSet<>(); - setInputTypes.add(inputType); - - // setInputTypes contains all InputTypes that need to be checked for types other than - // Enum, Scalar or GraphQL Type - while (setInputTypes.size() > 0) { - SchemaInputType type = setInputTypes.iterator().next(); - setInputTypes.remove(type); - LOGGER.info("Checking input type: " + type.getName()); - // check each field definition to see if any return types are unknownInputTypes - for (SchemaFieldDefinition fdi : type.getFieldDefinitions()) { - String fdReturnType = fdi.getReturnType(); - - if (!isGraphQLType(fdReturnType)) { - // must be either an unknown input type, Scalar or Enum - if (getScalar(fdReturnType) != null || isEnumClass(fdReturnType)) { - setUnresolvedTypes.add(fdReturnType); - } else { - // must be a type, create a new input Type but do not add it to - // the schema if it already exists - SchemaInputType newInputType = generateType(fdReturnType) - .createInputType("Input"); - if (!schema.containsInputTypeWithName(newInputType.getName())) { - schema.addInputType(newInputType); - setInputTypes.add(newInputType); - LOGGER.info("Adding new input type " + newInputType.getName()); - } else { - LOGGER.info("Ignoring: " + newInputType.getName()); - } - fdi.setReturnType(newInputType.getName()); - } - } - } - } + //check the input type for any other input types + checkInputType(schema, inputType); } } if (fd != null) { @@ -486,12 +460,61 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, } } + /** + * Check this new {@link SchemaInputType} contains any types, then they must also have InputTypes created for them if they are + * not enums or scalars. + * + * @param schema {@link Schema} to add to + * @param schemaInputType {@link SchemaInputType} to check + * @throws IntrospectionException if issues with introspection + * @throws ClassNotFoundException if class not found + */ + private void checkInputType(Schema schema, SchemaInputType schemaInputType) + throws IntrospectionException, ClassNotFoundException { + // if this new Type contains any types, then they must also have + // InputTypes created for them if they are not enums or scalars + Set setInputTypes = new HashSet<>(); + setInputTypes.add(schemaInputType); + + // setInputTypes contains all InputTypes that need to be checked for types other than + // Enum, Scalar or GraphQL Type + while (setInputTypes.size() > 0) { + SchemaInputType type = setInputTypes.iterator().next(); + setInputTypes.remove(type); + LOGGER.info("Checking input type: " + type.getName()); + // check each field definition to see if any return types are unknownInputTypes + for (SchemaFieldDefinition fdi : type.getFieldDefinitions()) { + String fdReturnType = fdi.getReturnType(); + + if (!isGraphQLType(fdReturnType)) { + // must be either an unknown input type, Scalar or Enum + if (getScalar(fdReturnType) != null || isEnumClass(fdReturnType)) { + setUnresolvedTypes.add(fdReturnType); + } else { + // must be a type, create a new input Type but do not add it to + // the schema if it already exists + SchemaInputType newInputType = generateType(fdReturnType) + .createInputType("Input"); + if (!schema.containsInputTypeWithName(newInputType.getName())) { + schema.addInputType(newInputType); + setInputTypes.add(newInputType); + LOGGER.info("Adding new input type " + newInputType.getName()); + } else { + LOGGER.info("Ignoring: " + newInputType.getName()); + } + fdi.setReturnType(newInputType.getName()); + } + } + } + } + } + /** * Add the given {@link SchemaType} to the {@link Schema}. * * @param schema the {@link Schema} to add to - * @throws IntrospectionException - * @throws ClassNotFoundException + * @throws IntrospectionException if issues with introspection + * @throws ClassNotFoundException if class not found */ private void addTypeToSchema(Schema schema, SchemaType type) throws IntrospectionException, ClassNotFoundException { @@ -530,19 +553,6 @@ private void addTypeToSchema(Schema schema, SchemaType type) */ private SchemaEnum generateEnum(Class clazz) { if (clazz.isEnum()) { - // check for the Enum annotation as this method may be called from different paths - org.eclipse.microprofile.graphql.Enum annotation = clazz - .getAnnotation(org.eclipse.microprofile.graphql.Enum.class); - String name = annotation == null ? "" : annotation.value(); - - // only check for Name annotation if the Enum didn't have a value - if ("".equals(name)) { - // check to see if this class has @Name annotation - String nameValue = getNameAnnotationValue(clazz); - if (nameValue != null) { - name = nameValue; - } - } SchemaEnum newSchemaEnum = new SchemaEnum(getTypeName(clazz)); Arrays.stream(clazz.getEnumConstants()) @@ -717,6 +727,8 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class prefix = GET; } else if (name.startsWith(SET)) { prefix = SET; + } else { + prefix = ""; } // remove the prefix and make first letter lowercase diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index f9215a1e500..cd8d74d8a9e 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -108,7 +108,7 @@ public class SchemaGeneratorHelper { /** * List of types that should map to a GraphQL Int. */ - protected static final List INTEGER_LIST = new ArrayList<>() {{ + static final List INTEGER_LIST = new ArrayList<>() {{ add("short"); add("int"); add("Short"); @@ -122,7 +122,7 @@ public class SchemaGeneratorHelper { /** * List of types that should map to a GraphQL String. */ - protected static final List STRING_LIST = new ArrayList<>() {{ + static final List STRING_LIST = new ArrayList<>() {{ add("java.lang.String"); add("java.lang.Character"); add("char"); @@ -132,7 +132,7 @@ public class SchemaGeneratorHelper { * List of array primitive types and their array mapping. See https://docs.oracle.com/javase/6/docs/api/java/lang/Class * .html#getName%28%29 */ - protected static final Map PRIMITIVE_ARRAY_MAP = new HashMap<>() {{ + static final Map PRIMITIVE_ARRAY_MAP = new HashMap<>() {{ put("[Z", "boolean"); put("[B", "byte"); put("[C", "char"); @@ -146,7 +146,7 @@ public class SchemaGeneratorHelper { /** * List of all Java primitive objects. */ - protected static final List JAVA_PRIMITIVE_OBJECTS = new ArrayList<>() {{ + static final List JAVA_PRIMITIVE_OBJECTS = new ArrayList<>() {{ addAll(STRING_LIST); addAll(FLOAT_LIST); addAll(INTEGER_LIST); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java index 683a50eb14b..a8a043a996d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java @@ -30,7 +30,7 @@ public class SchemaType /** * Name of the type. */ - private final String name; + private String name; /** * Value class name. @@ -122,6 +122,14 @@ public String getName() { return name; } + /** + * Set the name of the {@link SchemaType}. + * @param name the name of the {@link SchemaType} + */ + public void setName(String name) { + this.name = name; + } + /** * Return the value class name for the @{link Type}. * diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index b6f34beacd9..4155ddf754e 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -52,6 +52,10 @@ import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.PersonWithName; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputType; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithAddress; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithName; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithNameValue; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; import io.helidon.microprofile.graphql.server.test.types.Vehicle; @@ -410,8 +414,20 @@ public void testSimpleQueriesWithSource() throws IOException { assertThat(mapResults2, is(notNullValue())); assertThat(mapResults2.get("id"), is(notNullValue())); assertThat(mapResults2.get("lastNAddress"), is(notNullValue())); + } - // lastNAddress on top level query + @Test + public void testInputType() throws IOException { + setupIndex(indexFileName, SimpleContactInputType.class, SimpleContactInputTypeWithName.class, + SimpleContactInputTypeWithNameValue.class, SimpleContactInputTypeWithAddress.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + Schema schema = executionContext.getSchema(); + assertThat(schema.getInputTypes().size(), is(5)); + assertThat(schema.containsInputTypeWithName("MyInputType"), is(true)); + assertThat(schema.containsInputTypeWithName("SimpleContactInputTypeInput"), is(true)); + assertThat(schema.containsInputTypeWithName("NameInput"), is(true)); + assertThat(schema.containsInputTypeWithName("SimpleContactInputTypeWithAddressInput"), is(true)); + assertThat(schema.containsInputTypeWithName("AddressInput"), is(true)); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java index 0aa1ff919e2..fe2d613ce72 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java @@ -51,6 +51,9 @@ public void testConstructors() { assertThat(schemaType.getImplementingInterface(), is(nullValue())); schemaType.setImplementingInterface("Contact"); assertThat(schemaType.getImplementingInterface(), is("Contact")); + + schemaType.setName("Name"); + assertThat(schemaType.getName(), is("Name")); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputType.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputType.java new file mode 100644 index 00000000000..caca8b812df --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputType.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import java.util.Objects; + +import org.eclipse.microprofile.graphql.Input; + +/** + * Defines a simple contact input type. + */ +@Input +public class SimpleContactInputType { + private String id; + private String name; + private int age; + + public SimpleContactInputType(String id, String name, int age) { + this.id = id; + this.name = name; + this.age = age; + } + + public SimpleContactInputType() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SimpleContactInputType that = (SimpleContactInputType) o; + return age == that.age + && Objects.equals(id, that.id) + && Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, age); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithAddress.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithAddress.java new file mode 100644 index 00000000000..9ea187d8d90 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithAddress.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import java.util.Objects; + +import org.eclipse.microprofile.graphql.Input; + +/** + * Defines a simple contact input type which contains an address. + */ +@Input +public class SimpleContactInputTypeWithAddress { + private String id; + private String name; + private int age; + private Address address; + + public SimpleContactInputTypeWithAddress(String id, + String name, + int age, + Address address) { + this.id = id; + this.name = name; + this.age = age; + this.address = address; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithName.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithName.java new file mode 100644 index 00000000000..79572e8fad7 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithName.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import java.util.Objects; + +import org.eclipse.microprofile.graphql.Input; + +/** + * Defines a simple contact input type with a name. + */ +@Input("MyInputType") +public class SimpleContactInputTypeWithName { + private String id; + private String name; + private int age; + + public SimpleContactInputTypeWithName(String id, String name, int age) { + this.id = id; + this.name = name; + this.age = age; + } + + public SimpleContactInputTypeWithName() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SimpleContactInputTypeWithName that = (SimpleContactInputTypeWithName) o; + return age == that.age + && Objects.equals(id, that.id) + && Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, age); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithNameValue.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithNameValue.java new file mode 100644 index 00000000000..0a5a6b4ae8c --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactInputTypeWithNameValue.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import java.util.Objects; + +import org.eclipse.microprofile.graphql.Input; +import org.eclipse.microprofile.graphql.Name; + +/** + * Defines a simple contact input type with a {@link Name} value. + */ +@Input +@Name("NameInput") +public class SimpleContactInputTypeWithNameValue { + private String id; + private String name; + private int age; + + public SimpleContactInputTypeWithNameValue(String id, String name, int age) { + this.id = id; + this.name = name; + this.age = age; + } + + public SimpleContactInputTypeWithNameValue() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SimpleContactInputTypeWithNameValue that = (SimpleContactInputTypeWithNameValue) o; + return age == that.age + && Objects.equals(id, that.id) + && Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, age); + } +} From 8711b637a8df1c1412df8f9c04ab71e18539e2b0 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 17 Mar 2020 14:26:44 +0800 Subject: [PATCH 033/178] spotbugs --- .../graphql/server/ExecutionContext.java | 2 -- .../graphql/server/NumberFormatUtils.java | 34 +++++++++++++++++++ .../graphql/server/SchemaGenerator.java | 2 -- .../graphql/server/SchemaGeneratorHelper.java | 26 +++++++------- 4 files changed, 46 insertions(+), 18 deletions(-) create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/NumberFormatUtils.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index 2d56f0ff57f..903ee68e0f6 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -17,7 +17,6 @@ package io.helidon.microprofile.graphql.server; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; @@ -25,7 +24,6 @@ import graphql.ExecutionResult; import graphql.GraphQL; import graphql.execution.SubscriptionExecutionStrategy; -import graphql.execution.instrumentation.Instrumentation; import graphql.schema.GraphQLSchema; import graphql.schema.idl.SchemaPrinter; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/NumberFormatUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/NumberFormatUtils.java new file mode 100644 index 00000000000..e3fce247004 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/NumberFormatUtils.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server; + +import java.util.logging.Logger; + + +/** + * Utilities for working with number formatting. + */ +public class NumberFormatUtils { + + private static final Logger LOGGER = Logger.getLogger(NumberFormatUtils.class.getName()); + + /** + * No-args private constructor. + */ + private NumberFormatUtils() { + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 1b531d113f0..1dbf85552ce 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -63,7 +63,6 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldName; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getGraphQLType; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getMethodName; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getNameAnnotationValue; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootArrayClass; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootTypeName; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getSafeClass; @@ -157,7 +156,6 @@ public Schema generateSchema() throws IntrospectionException, ClassNotFoundExcep */ protected Schema generateSchemaFromClasses(Class... clazzes) throws IntrospectionException, ClassNotFoundException { Schema schema = new Schema(); - List listSchemaTypes = new ArrayList<>(); setUnresolvedTypes.clear(); setAdditionalMethods.clear(); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index cd8d74d8a9e..6592475a96d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -87,7 +87,7 @@ public class SchemaGeneratorHelper { /** * List of types the should map to a GraphQL Float. */ - protected static final List FLOAT_LIST = new ArrayList<>() {{ + static final List FLOAT_LIST = new ArrayList<>() {{ add("double"); add("Double"); add("java.lang.Double"); @@ -100,7 +100,7 @@ public class SchemaGeneratorHelper { /** * List of types that should map to a GraphQL Boolean. */ - protected static final List BOOLEAN_LIST = new ArrayList<>() {{ + static final List BOOLEAN_LIST = new ArrayList<>() {{ add("boolean"); add(Boolean.class.getName()); }}; @@ -268,19 +268,17 @@ protected static boolean isGraphQLType(String type) { protected static String getFieldName(Class clazz, String fieldName) { try { Field field = clazz.getDeclaredField(fieldName); - if (field != null) { - Name nameAnnotation = field.getAnnotation(Name.class); - // Name annotation is specified so use this and don't bother checking JsonbProperty - if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { - return nameAnnotation.value(); - } - // check for JsonbProperty - JsonbProperty jsonbPropertyAnnotation = field.getAnnotation(JsonbProperty.class); - return jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank() - ? jsonbPropertyAnnotation.value() - : null; + Name nameAnnotation = field.getAnnotation(Name.class); + // Name annotation is specified so use this and don't bother checking JsonbProperty + if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { + return nameAnnotation.value(); } - return null; + // check for JsonbProperty + JsonbProperty jsonbPropertyAnnotation = field.getAnnotation(JsonbProperty.class); + return jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank() + ? jsonbPropertyAnnotation.value() + : null; + } catch (NoSuchFieldException e) { return null; } From a28d5337848ab9b909c7fa8f102288812468b7e2 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 18 Mar 2020 13:21:45 +0800 Subject: [PATCH 034/178] add tests and helpers for number formatting --- .../graphql/server/DataFetcherUtils.java | 29 ++++ .../graphql/server/NumberFormatUtils.java | 34 ----- .../graphql/server/SchemaGeneratorHelper.java | 110 ++++++++++++++- .../graphql/server/SchemaGeneratorIT.java | 8 ++ .../graphql/server/SchemaGeneratorTest.java | 127 ++++++++++++++++- .../types/SimpleContactWithNumberFormats.java | 129 ++++++++++++++++++ 6 files changed, 391 insertions(+), 46 deletions(-) delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/NumberFormatUtils.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 32c2c07441b..dba3337d8cc 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -17,15 +17,19 @@ package io.helidon.microprofile.graphql.server; import java.lang.reflect.Method; +import java.text.NumberFormat; import java.util.ArrayList; +import java.util.Locale; import java.util.Map; import java.util.UUID; import javax.enterprise.inject.spi.CDI; import graphql.schema.DataFetcher; +import graphql.schema.PropertyDataFetcherHelper; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getCorrectFormat; /** * Utilities for working with {@link DataFetcher}s. @@ -92,6 +96,31 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met }; } + /** + * Create a new {@link DataFetcher} which formats a number. + * + * @param propertyName property to extract + * @param type GraphQL type of the property + * @param valueFormat formatting value + * @param locale formatting locale + * @param type of the source + * @return a new {@link DataFetcher} + */ + public static DataFetcher newNumberFormatDataFetcher(String propertyName, String type, String valueFormat, String locale) { + NumberFormat numberFormat = getCorrectFormat(type, locale, valueFormat); + + return environment -> { + S source = environment.getSource(); + if (source == null) { + return null; + } + Object rawValue = PropertyDataFetcherHelper + .getPropertyValue(propertyName, source, environment.getFieldType(), environment); + + return rawValue != null ? numberFormat.format(rawValue) : null; + }; + } + /** * Convert the ID type back to the original type for the method call. * diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/NumberFormatUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/NumberFormatUtils.java deleted file mode 100644 index e3fce247004..00000000000 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/NumberFormatUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. - * - * 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 io.helidon.microprofile.graphql.server; - -import java.util.logging.Logger; - - -/** - * Utilities for working with number formatting. - */ -public class NumberFormatUtils { - - private static final Logger LOGGER = Logger.getLogger(NumberFormatUtils.class.getName()); - - /** - * No-args private constructor. - */ - private NumberFormatUtils() { - } -} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 6592475a96d..685fa42a9d6 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -18,9 +18,12 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; import java.math.BigInteger; +import java.text.DecimalFormat; +import java.text.NumberFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -31,8 +34,10 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import javax.json.bind.annotation.JsonbNumberFormat; import javax.json.bind.annotation.JsonbProperty; import graphql.Scalars; @@ -51,12 +56,22 @@ /** * Helper class for {@link SchemaGenerator}. */ -public class SchemaGeneratorHelper { +public final class SchemaGeneratorHelper { + + /** + * Defines a {@link BigDecimal} type. + */ + static final String BIG_DECIMAL = "BigDecimal"; + + /** + * Defines a {@link BigInteger} type. + */ + static final String BIG_INTEGER = "BigInteger"; /** * List of supported scalars keyed by the full class name. */ - private static final Map SUPPORTED_SCALARS = new HashMap<>() {{ + static final Map SUPPORTED_SCALARS = new HashMap<>() {{ // Object Scalar put(Object.class.getName(), new SchemaScalar("Object", Object.class.getName(), ExtendedScalars.Object)); @@ -76,12 +91,12 @@ public class SchemaGeneratorHelper { put(LocalDate.class.getName(), new SchemaScalar("Date", LocalDate.class.getName(), ExtendedScalars.Date)); // BigDecimal scalars - put(BigDecimal.class.getName(), new SchemaScalar("BigDecimal", Long.class.getName(), Scalars.GraphQLBigDecimal)); + put(BigDecimal.class.getName(), new SchemaScalar(BIG_DECIMAL, Long.class.getName(), Scalars.GraphQLBigDecimal)); // BigInter scalars - put(BigInteger.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); - put(long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); - put(Long.class.getName(), new SchemaScalar("BigInteger", Long.class.getName(), Scalars.GraphQLBigInteger)); + put(BigInteger.class.getName(), new SchemaScalar(BIG_INTEGER, Long.class.getName(), Scalars.GraphQLBigInteger)); + put(long.class.getName(), new SchemaScalar(BIG_INTEGER, Long.class.getName(), Scalars.GraphQLBigInteger)); + put(Long.class.getName(), new SchemaScalar(BIG_INTEGER, Long.class.getName(), Scalars.GraphQLBigInteger)); }}; /** @@ -152,6 +167,11 @@ public class SchemaGeneratorHelper { addAll(INTEGER_LIST); }}; + /** + * Value that indicates that default {@link java.util.Locale}. + */ + static String DEFAULT_LOCALE = "##default"; + /** * GraphQL Int. */ @@ -463,6 +483,84 @@ protected static String getRootArrayClass(String clazz) { return clazz.replaceAll("\\[", "").replaceAll(";", "").replaceAll("^L", ""); } + /** + * Returna {@link NumberFormat} for the given type, locale and format. + * + * @param type the GraphQL type or scalar + * @param locale the locale, either "" or the correct locale + * @param format the format to use, may be null + * @return The correct {@link NumberFormat} for the given type and locale + */ + protected static NumberFormat getCorrectFormat(String type, String locale, String format) { + Locale actualLocale = DEFAULT_LOCALE.equals(locale) ? Locale.getDefault() : Locale.forLanguageTag(locale); + NumberFormat numberFormat; + if (FLOAT.equals(type) || BIG_DECIMAL.equals(type)) { + numberFormat = NumberFormat.getNumberInstance(actualLocale); + } else if (INT.equals(type) || BIG_INTEGER.equals(type)) { + numberFormat = NumberFormat.getIntegerInstance(actualLocale); + } else { + return null; + } + if (format != null && !format.trim().equals("")) { + ((DecimalFormat) numberFormat).applyPattern(format); + } + return numberFormat; + } + + /** + * Returna {@link NumberFormat} for the given type and locale. + * + * @param type the GraphQL type or scalar + * @param locale the locale, either "" or the correct locale + * @return The correct {@link NumberFormat} for the given type and locale + */ + protected static NumberFormat getCorrectFormat(String type, String locale) { + return getCorrectFormat(type, locale, null); + } + + /** + * Return the format and locale for a field if they exist in a {@link String} array. + * + * @param field the {@link Field} to check + * @return the format and locale for a field in a {@link String} array or an empty array if not + */ + protected static String[] getFormatAnnotation(Field field) { + JsonbNumberFormat jsonbNumberFormat = field.getAnnotation(JsonbNumberFormat.class); + org.eclipse.microprofile.graphql.NumberFormat numberFormat = field + .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class); + + // check @NumberFormat first as this takes precedence + if (numberFormat != null) { + return new String[] { numberFormat.value(), numberFormat.locale() }; + } + if (jsonbNumberFormat != null) { + return new String[] { jsonbNumberFormat.value(), jsonbNumberFormat.locale() }; + } + return new String[0]; + } + + /** + * Return the format and locale for a parameter if they exist in a {@link String} array. + * + * @param parameter the {@link Field} to check + * @return the format and locale for a parameter in a {@link String} array or an empty array if not + */ + protected static String[] getFormatAnnotation(Parameter parameter) { + JsonbNumberFormat jsonbNumberFormat = parameter.getAnnotation(JsonbNumberFormat.class); + org.eclipse.microprofile.graphql.NumberFormat numberFormat = parameter + .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class); + + // check @NumberFormat first as this takes precedence + if (numberFormat != null) { + return new String[] { numberFormat.value(), numberFormat.locale() }; + } + if (jsonbNumberFormat != null) { + return new String[] { jsonbNumberFormat.value(), jsonbNumberFormat.locale() }; + } + return new String[0]; + } + + /** * Return the inner most root type such as {@link String} for a List of List of String. * diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index 4155ddf754e..6b7f1c55979 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -56,6 +56,7 @@ import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithAddress; import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithName; import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithNameValue; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; import io.helidon.microprofile.graphql.server.test.types.Vehicle; @@ -230,6 +231,13 @@ public void testSimpleContactWithSelf() throws IOException { ExecutionResult result = executionContext.execute("query { hero }"); } + @Test + public void testNumberFormats() throws IOException { + setupIndex(indexFileName, SimpleContactWithNumberFormats.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + System.out.println("schema=" + executionContext.getSchema()); + } + @Test public void testDateAndTime() throws IOException { setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesNoArgs.class); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index ad8e6a06f99..2e03809af3f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -18,9 +18,12 @@ import java.beans.IntrospectionException; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; +import java.text.NumberFormat; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; @@ -45,6 +48,7 @@ import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.PersonWithName; import io.helidon.microprofile.graphql.server.test.types.PersonWithNameValue; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; import io.helidon.microprofile.graphql.server.test.types.TypeWithIdOnField; import io.helidon.microprofile.graphql.server.test.types.TypeWithIdOnMethod; @@ -54,7 +58,12 @@ import io.helidon.microprofile.graphql.server.test.types.VehicleIncident; import org.junit.jupiter.api.Test; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BOOLEAN; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_DECIMAL; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_INTEGER; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DEFAULT_LOCALE; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FLOAT; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFormatAnnotation; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootTypeName; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; @@ -73,6 +82,10 @@ public class SchemaGeneratorTest extends AbstractGraphQLTest { private static final String LOCALDATE = LocalDate.class.getName(); private static final String ID = "ID"; + private List listStringArray = new ArrayList<>(); + private List listString = new ArrayList<>(); + private List>> listListString = new ArrayList<>(); + @Test public void testEnumGeneration() throws IntrospectionException, ClassNotFoundException { testEnum(EnumTestNoEnumName.class, EnumTestNoEnumName.class.getSimpleName()); @@ -248,7 +261,8 @@ public void testAllMethods() throws IntrospectionException { assertDiscoveredMethod(mapMethods.get("getMultiLevelList"), "getMultiLevelList", MultiLevelListsAndArrays.class.getName(), null, false, false, false); - assertDiscoveredMethod(mapMethods.get("testIgnorableFields"), "testIgnorableFields", ObjectWithIgnorableFields.class.getName(), + assertDiscoveredMethod(mapMethods.get("testIgnorableFields"), "testIgnorableFields", + ObjectWithIgnorableFields.class.getName(), null, false, false, false); } @@ -313,10 +327,6 @@ public void testArrayLevelsAndRootArray() { assertThat(SchemaGeneratorHelper.getRootArrayClass(threeLevelInt.getClass().getName()), is(int.class.getName())); } - private List listStringArray = new ArrayList<>(); - private List listString = new ArrayList<>(); - private List>> listListString = new ArrayList<>(); - @Test public void testGetRootType() throws NoSuchFieldException { ParameterizedType stringArrayListType = getParameterizedType("listStringArray"); @@ -335,6 +345,111 @@ public void testGetRootType() throws NoSuchFieldException { assertThat(rootTypeName.getLevels(), is(2)); } + @Test + public void testGetCorrectFormat() { + NumberFormat numberFormat = SchemaGeneratorHelper.getCorrectFormat(INT, ""); + assertThat(numberFormat, is(notNullValue())); + assertThat(numberFormat.getMaximumFractionDigits(), is(0)); + + numberFormat = SchemaGeneratorHelper.getCorrectFormat(BIG_INTEGER, ""); + assertThat(numberFormat, is(notNullValue())); + assertThat(numberFormat.getMaximumFractionDigits(), is(0)); + + numberFormat = SchemaGeneratorHelper.getCorrectFormat(FLOAT, ""); + assertThat(numberFormat, is(notNullValue())); + assertThat(numberFormat.getMaximumFractionDigits() > 0, is(true)); + + numberFormat = SchemaGeneratorHelper.getCorrectFormat(BIG_DECIMAL, ""); + assertThat(numberFormat, is(notNullValue())); + assertThat(numberFormat.getMaximumFractionDigits() > 0, is(true)); + } + + @Test + public void testFormatting() { + assertFormat(FLOAT, "en-ZA", "¤ 000.00", 100.0d, "R 100,00"); + assertFormat(FLOAT, "en-AU", "¤ 000.00", 100.0d, "$ 100.00"); + assertFormat(FLOAT, "en-AU", "000.00 'ml'", 125.12d, "125.12 ml"); + assertFormat(INT, "en-AU", "0 'years'", 52, "52 years"); + assertFormat(INT, "en-AU", "0 'years old'", 12, "12 years old"); + assertFormat(BIG_DECIMAL, "en-AU", "###,###.###", 123456.789, "123,456.789"); + assertFormat(BIG_DECIMAL, "en-AU", "000000.000", 123.78, "000123.780"); + } + + @Test + public void testGetFormatAnnotation() throws NoSuchFieldException, NoSuchMethodException { + assertAnnotation(SimpleContactWithNumberFormats.class, "field", "age", 2, "0 'years old'", DEFAULT_LOCALE); + assertAnnotation(SimpleContactWithNumberFormats.class, "field", "bankBalance", 2, "¤ 000.00", "en-AU"); + assertAnnotation(SimpleContactWithNumberFormats.class, "field", "value", 2, "0 'value'", DEFAULT_LOCALE); + assertAnnotation(SimpleContactWithNumberFormats.class, "field", "name", 0, null, null); + + assertAnnotation(SimpleContactWithNumberFormats.class, "method", "getFormatMethod", 2, "0 'years old'", DEFAULT_LOCALE, + int.class); + assertAnnotation(SimpleContactWithNumberFormats.class, "method", "getName", 0, null, null); + } + + /** + * Assert that a {@link org.eclipse.microprofile.graphql.NumberFormat} or {@link javax.json.bind.annotation.JsonbNumberFormat} + * annotation is correclty applied. + * + * @param clazz {@link Class} to apply to + * @param type type to check, "field" or "method" + * @param name field name or method name + * @param expectedLength expected length of format array + * @param expectedFormat expected value for format + * @param expectedLocale expected value for locale + * @param methodArgs arguments ot the method + * @throws NoSuchFieldException + */ + private void assertAnnotation(Class clazz, + String type, + String name, + int expectedLength, + String expectedFormat, + String expectedLocale, + Class... methodArgs) + throws NoSuchFieldException, NoSuchMethodException { + if (expectedLength != 0 && expectedLength != 2) { + throw new IllegalArgumentException("Expected length should be 0 or 2"); + } + String[] annotation = new String[0]; + if ("field".equals(type)) { + Field field = clazz.getDeclaredField(name); + assertThat(field, is(notNullValue())); + annotation = getFormatAnnotation(field); + } else if ("method".equals(type)) { + Method method = clazz.getMethod(name, methodArgs); + assertThat(method, is(notNullValue())); + if (expectedLength == 2) { + annotation = getFormatAnnotation(method.getParameters()[0]); + } + } else { + throw new IllegalArgumentException("Unknown type of " + type); + } + assertThat(annotation, is(notNullValue())); + assertThat(annotation.length, is(expectedLength)); + if (expectedLength == 2) { + assertThat("Format should be " + expectedFormat + " but is " + annotation[0], annotation[0], is(expectedFormat)); + assertThat("locale should be " + expectedLocale + " but is " + annotation[1], annotation[1], is(expectedLocale)); + } + } + + /** + * Assert formatting is correct. + * + * @param type type to format + * @param locale locale to use + * @param format format to apply + * @param value value to format + * @param expectedResult expected result + */ + private void assertFormat(String type, String locale, String format, Object value, String expectedResult) { + NumberFormat numberFormat = SchemaGeneratorHelper.getCorrectFormat(type, locale, format); + assertThat(numberFormat, is(notNullValue())); + String formatted = numberFormat.format(value); + assertThat(formatted, is(notNullValue())); + assertThat(formatted, is(expectedResult)); + } + private ParameterizedType getParameterizedType(String fieldName) throws NoSuchFieldException { Field listStringArray = SchemaGeneratorTest.class.getDeclaredField(fieldName); return (ParameterizedType) listStringArray.getGenericType(); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java new file mode 100644 index 00000000000..487b1458181 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import java.text.DecimalFormat; +import java.util.Objects; + +import javax.json.bind.annotation.JsonbNumberFormat; + +import org.eclipse.microprofile.graphql.NumberFormat; +import org.eclipse.microprofile.graphql.Type; + +/** + * Defines a simple contact which contains number formats. + */ +@Type +public class SimpleContactWithNumberFormats { + private String id; + private String name; + + @NumberFormat("0 'years old'") + private int age; + + @JsonbNumberFormat(value = "¤ 000.00", locale = "en-AU") + private float bankBalance; + + @JsonbNumberFormat(value = "000") // this should be ignored + @NumberFormat("0 'value'") // this should be applied + private int value; + + public int getFormatMethod(@NumberFormat("0 'years old'") int age) { + return age; + } + + public SimpleContactWithNumberFormats(String id, + String name, + int age, float bankBalance, int value) { + this.id = id; + this.name = name; + this.age = age; + this.bankBalance = bankBalance; + this.value = value; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public float getBankBalance() { + return bankBalance; + } + + public void setBankBalance(float bankBalance) { + this.bankBalance = bankBalance; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SimpleContactWithNumberFormats that = (SimpleContactWithNumberFormats) o; + return age == that.age + && Float.compare(that.bankBalance, bankBalance) == 0 + && Objects.equals(id, that.id) + && Objects.equals(value, that.value) + && Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, age, bankBalance, value); + } + + @Override + public String toString() { + return "SimpleContactWithNumberFormats{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", age=" + age + + ", value" + value + + ", bankBalance=" + bankBalance + '}'; + } +} From 048fc8326ecc3b98540dcc19d372c64bc75dc00b Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 18 Mar 2020 13:44:45 +0800 Subject: [PATCH 035/178] tck --- .../graphql/server/SchemaGenerator.java | 10 +++- microprofile/graphql/tck/pom.xml | 24 -------- .../HelidonGraphQLArchiveProcessor.java | 55 ------------------- .../graphql/HelidonGraphQLExtension.java | 32 ----------- .../META-INF/microprofile-config.properties | 17 ------ .../tck/src/test/resources/arquillian.xml | 35 ++++++++++++ .../graphql/tck/src/test/tck-suite.xml | 28 ++++++++++ 7 files changed, 72 insertions(+), 129 deletions(-) delete mode 100644 microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLArchiveProcessor.java delete mode 100644 microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLExtension.java delete mode 100644 microprofile/graphql/tck/src/test/resources/META-INF/microprofile-config.properties create mode 100644 microprofile/graphql/tck/src/test/resources/arquillian.xml create mode 100644 microprofile/graphql/tck/src/test/tck-suite.xml diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 1dbf85552ce..73a5ca03472 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -61,6 +61,7 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.checkScalars; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getArrayLevels; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldName; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFormatAnnotation; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getGraphQLType; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getMethodName; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootArrayClass; @@ -715,6 +716,7 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class String name = method.getName(); String varName; + String numberFormat[]; if (name.startsWith(IS) || name.startsWith(GET) || name.startsWith(SET)) { // this is a getter method @@ -760,10 +762,11 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class if (pd != null) { boolean fieldHasIdAnnotation = false; + Field field = null; // check for Id annotation on class or field associated with the method // and if present change the type to ID try { - Field field = clazz.getDeclaredField(pd.getName()); + field = clazz.getDeclaredField(pd.getName()); fieldHasIdAnnotation = field != null && field.getAnnotation(Id.class) != null; } catch (NoSuchFieldException e) { // ignore @@ -775,6 +778,11 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class } returnClazzName = ID; } + + // check for number format on the property + if (field != null) { + numberFormat = getFormatAnnotation(field); + } } DiscoveredMethod discoveredMethod = new DiscoveredMethod(); diff --git a/microprofile/graphql/tck/pom.xml b/microprofile/graphql/tck/pom.xml index c417ad01f28..f73f5e1a0aa 100644 --- a/microprofile/graphql/tck/pom.xml +++ b/microprofile/graphql/tck/pom.xml @@ -51,17 +51,6 @@ microprofile-graphql-tck test - - org.jboss.shrinkwrap.resolver - shrinkwrap-resolver-depchain - pom - test - - - org.jboss.arquillian.junit - arquillian-junit-container - test - io.helidon.microprofile.graphql helidon-microprofile-graphql-server @@ -83,19 +72,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - false - - - org.eclipse.microprofile.graphql:microprofile-graphql-tck - - - tier=integration - - - diff --git a/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLArchiveProcessor.java b/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLArchiveProcessor.java deleted file mode 100644 index 643fe424a21..00000000000 --- a/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLArchiveProcessor.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. - * - * 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 io.helidon.microprofile.graphql; - -import java.io.File; -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor; -import org.jboss.arquillian.test.spi.TestClass; -import org.jboss.shrinkwrap.api.Archive; -import org.jboss.shrinkwrap.api.spec.WebArchive; -import org.jboss.shrinkwrap.resolver.api.maven.Maven; - -/** - * Creates a deployable unit with all the dependencies. - */ -public class HelidonGraphQLArchiveProcessor - implements ApplicationArchiveProcessor { - @Override - public void process(Archive applicationArchive, TestClass testClass) { - if (applicationArchive instanceof WebArchive) { - WebArchive testDeployment = (WebArchive) applicationArchive; - - final File[] dependencies = Maven.resolver() - .loadPomFromFile("pom.xml") - .resolve("io.helidon.microprofile.graphql:helidon-microprofile-graphql-server") - .withTransitivity() - .asFile(); - // Make sure it's unique - Set dependenciesSet = new LinkedHashSet<>(Arrays.asList(dependencies)); - testDeployment.addAsLibraries(dependenciesSet.toArray(new File[] {})); - // MicroProfile properties - testDeployment.addAsResource( - HelidonGraphQLArchiveProcessor.class.getClassLoader() - .getResource("META-INF/microprofile-config.properties"), - "META-INF/microprofile-config.properties"); - } - } -} diff --git a/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLExtension.java b/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLExtension.java deleted file mode 100644 index 40cf8f08a46..00000000000 --- a/microprofile/graphql/tck/src/test/java/io/helidon/microprofile/graphql/HelidonGraphQLExtension.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. - * - * 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 io.helidon.microprofile.graphql; - -import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor; -import org.jboss.arquillian.core.spi.LoadableExtension; - -/** - * Active the extension for TCK tests. - */ -public class HelidonGraphQLExtension - implements LoadableExtension { - - @Override - public void register(ExtensionBuilder extensionBuilder) { - extensionBuilder.service(ApplicationArchiveProcessor.class, HelidonGraphQLArchiveProcessor.class); - } -} diff --git a/microprofile/graphql/tck/src/test/resources/META-INF/microprofile-config.properties b/microprofile/graphql/tck/src/test/resources/META-INF/microprofile-config.properties deleted file mode 100644 index bbe6485011d..00000000000 --- a/microprofile/graphql/tck/src/test/resources/META-INF/microprofile-config.properties +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. -# -# 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. -# - -mp.graphql.printDataFetcherException=true diff --git a/microprofile/graphql/tck/src/test/resources/arquillian.xml b/microprofile/graphql/tck/src/test/resources/arquillian.xml new file mode 100644 index 00000000000..f3fe2b8e864 --- /dev/null +++ b/microprofile/graphql/tck/src/test/resources/arquillian.xml @@ -0,0 +1,35 @@ + + + + + + target/deployments + 8080 + + + + + false + true + false + + + \ No newline at end of file diff --git a/microprofile/graphql/tck/src/test/tck-suite.xml b/microprofile/graphql/tck/src/test/tck-suite.xml new file mode 100644 index 00000000000..2c5ff3f3c60 --- /dev/null +++ b/microprofile/graphql/tck/src/test/tck-suite.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file From 086ed00aed490286c81876468c4de75b692b5f61 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 18 Mar 2020 15:18:43 +0800 Subject: [PATCH 036/178] add method annotation check --- .../graphql/server/SchemaGeneratorHelper.java | 45 +++++++++++-------- .../graphql/server/SchemaGeneratorTest.java | 16 +++++-- .../types/SimpleContactWithNumberFormats.java | 2 + 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 685fa42a9d6..fb674d86308 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -522,34 +522,44 @@ protected static NumberFormat getCorrectFormat(String type, String locale) { * Return the format and locale for a field if they exist in a {@link String} array. * * @param field the {@link Field} to check - * @return the format and locale for a field in a {@link String} array or an empty array if not + * @return the format ([0]) and locale ([1]) for a field in a {@link String} array or an empty array if not */ protected static String[] getFormatAnnotation(Field field) { - JsonbNumberFormat jsonbNumberFormat = field.getAnnotation(JsonbNumberFormat.class); - org.eclipse.microprofile.graphql.NumberFormat numberFormat = field - .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class); - - // check @NumberFormat first as this takes precedence - if (numberFormat != null) { - return new String[] { numberFormat.value(), numberFormat.locale() }; - } - if (jsonbNumberFormat != null) { - return new String[] { jsonbNumberFormat.value(), jsonbNumberFormat.locale() }; - } - return new String[0]; + return getFormatAnnotationInternal(field.getAnnotation(JsonbNumberFormat.class), field + .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class)); } /** * Return the format and locale for a parameter if they exist in a {@link String} array. * * @param parameter the {@link Field} to check - * @return the format and locale for a parameter in a {@link String} array or an empty array if not + * @return the format ([0]) and locale ([1]) for a parameter in a {@link String} array or an empty array if not */ protected static String[] getFormatAnnotation(Parameter parameter) { - JsonbNumberFormat jsonbNumberFormat = parameter.getAnnotation(JsonbNumberFormat.class); - org.eclipse.microprofile.graphql.NumberFormat numberFormat = parameter - .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class); + return getFormatAnnotationInternal(parameter.getAnnotation(JsonbNumberFormat.class), parameter + .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class)); + } + /** + * Return the format and locale for a method if they exist in a {@link String} array. + * + * @param method the {@link Method} to check + * @return the format ([0]) and locale ([1]) for a method in a {@link String} array or an empty array if not + */ + protected static String[] getFormatAnnotation(Method method) { + return getFormatAnnotationInternal(method.getAnnotation(JsonbNumberFormat.class), + method.getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class)); + } + + /** + * Return the format and locate for the given annotations. + * + * @param jsonbNumberFormat {@link JsonbNumberFormat} annotation, may be null + * @param numberFormat {@Link org.eclipse.microprofile.graphql.NumberFormat} annotation, may be none + * @return the format ([0]) and locale ([1]) for a method in a {@link String} array or an empty array if not + */ + private static String[] getFormatAnnotationInternal(JsonbNumberFormat jsonbNumberFormat, + org.eclipse.microprofile.graphql.NumberFormat numberFormat) { // check @NumberFormat first as this takes precedence if (numberFormat != null) { return new String[] { numberFormat.value(), numberFormat.locale() }; @@ -560,7 +570,6 @@ protected static String[] getFormatAnnotation(Parameter parameter) { return new String[0]; } - /** * Return the inner most root type such as {@link String} for a List of List of String. * diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index 2e03809af3f..bcf74a40e75 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -382,8 +382,12 @@ public void testGetFormatAnnotation() throws NoSuchFieldException, NoSuchMethodE assertAnnotation(SimpleContactWithNumberFormats.class, "field", "value", 2, "0 'value'", DEFAULT_LOCALE); assertAnnotation(SimpleContactWithNumberFormats.class, "field", "name", 0, null, null); - assertAnnotation(SimpleContactWithNumberFormats.class, "method", "getFormatMethod", 2, "0 'years old'", DEFAULT_LOCALE, + assertAnnotation(SimpleContactWithNumberFormats.class, "methodParam", "getFormatMethod", 2, "0 'years old'", + DEFAULT_LOCALE, int.class); + assertAnnotation(SimpleContactWithNumberFormats.class, "methodParam", "getName", 0, null, null); + + assertAnnotation(SimpleContactWithNumberFormats.class, "method", "getId", 2, "0 'id'", DEFAULT_LOCALE); assertAnnotation(SimpleContactWithNumberFormats.class, "method", "getName", 0, null, null); } @@ -392,7 +396,7 @@ public void testGetFormatAnnotation() throws NoSuchFieldException, NoSuchMethodE * annotation is correclty applied. * * @param clazz {@link Class} to apply to - * @param type type to check, "field" or "method" + * @param type type to check, "field", "method" or "methodParam" * @param name field name or method name * @param expectedLength expected length of format array * @param expectedFormat expected value for format @@ -416,12 +420,18 @@ private void assertAnnotation(Class clazz, Field field = clazz.getDeclaredField(name); assertThat(field, is(notNullValue())); annotation = getFormatAnnotation(field); - } else if ("method".equals(type)) { + } else if ("methodParam".equals(type)) { Method method = clazz.getMethod(name, methodArgs); assertThat(method, is(notNullValue())); if (expectedLength == 2) { annotation = getFormatAnnotation(method.getParameters()[0]); } + } else if ("method".equals(type)) { + Method method = clazz.getMethod(name, methodArgs); + assertThat(method, is(notNullValue())); + if (expectedLength == 2) { + annotation = getFormatAnnotation(method); + } } else { throw new IllegalArgumentException("Unknown type of " + type); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java index 487b1458181..2e98543f4b2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java @@ -16,6 +16,7 @@ package io.helidon.microprofile.graphql.server.test.types; +import java.math.BigDecimal; import java.text.DecimalFormat; import java.util.Objects; @@ -56,6 +57,7 @@ public SimpleContactWithNumberFormats(String id, this.value = value; } + @NumberFormat("0 'id'") public String getId() { return id; } From aebfad560c1ae53d66625ea25f3de0361c74131e Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Wed, 18 Mar 2020 13:20:33 +0100 Subject: [PATCH 037/178] GraphQL TCK tests. Signed-off-by: Tomas Langer --- bom/pom.xml | 6 + dependencies/pom.xml | 2 +- microprofile/graphql/pom.xml | 2 - microprofile/graphql/runner/pom.xml | 115 ------------------ .../HelidonDeployableContainer.java | 35 +++++- microprofile/tests/tck/pom.xml | 2 + .../tck => tests/tck/tck-graphql}/pom.xml | 62 +++++----- .../graphql/tck/GraphqlExtension.java | 28 +++++ .../graphql/tck/UriResourceProvider.java | 39 ++++++ .../graphql/tck/UrlResourceProvider.java | 45 +++++++ ...boss.arquillian.core.spi.LoadableExtension | 18 +++ .../src/test/resources/arquillian.xml | 4 +- .../tck/tck-graphql}/src/test/tck-suite.xml | 6 +- 13 files changed, 208 insertions(+), 156 deletions(-) delete mode 100644 microprofile/graphql/runner/pom.xml rename microprofile/{graphql/tck => tests/tck/tck-graphql}/pom.xml (58%) create mode 100644 microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/GraphqlExtension.java create mode 100644 microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/UriResourceProvider.java create mode 100644 microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/UrlResourceProvider.java create mode 100644 microprofile/tests/tck/tck-graphql/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension rename microprofile/{graphql/tck => tests/tck/tck-graphql}/src/test/resources/arquillian.xml (84%) rename microprofile/{graphql/tck => tests/tck/tck-graphql}/src/test/tck-suite.xml (78%) diff --git a/bom/pom.xml b/bom/pom.xml index 6933f5c9241..a41b0ae9a0b 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -182,6 +182,12 @@ helidon-microprofile-cors ${helidon.version} + + + io.helidon.microprofile.graphql + helidon-microprofile-graphql-server + ${helidon.version} + io.helidon.media diff --git a/dependencies/pom.xml b/dependencies/pom.xml index fd034c6c676..83c121105a1 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -53,7 +53,7 @@ 2.3.3 20.1.0 14.0 - 1.1.0 + 1.0.1 19.2.0 1.32.1 28.1-jre diff --git a/microprofile/graphql/pom.xml b/microprofile/graphql/pom.xml index 07bff30e128..e86c8425bb9 100644 --- a/microprofile/graphql/pom.xml +++ b/microprofile/graphql/pom.xml @@ -31,8 +31,6 @@ server - tck - runner diff --git a/microprofile/graphql/runner/pom.xml b/microprofile/graphql/runner/pom.xml deleted file mode 100644 index 30418173539..00000000000 --- a/microprofile/graphql/runner/pom.xml +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - io.helidon.microprofile.graphql - helidon-microprofile-graphql - 2.0.0-SNAPSHOT - - 4.0.0 - - helidon-microprofile-graphql-runner - Helidon Microprofile GraphQL Runner - The microprofile GraphQL Runner - - - - io.helidon.microprofile.graphql - helidon-microprofile-graphql-server - 2.0.0-SNAPSHOT - - - org.eclipse.microprofile.graphql - microprofile-graphql-tck - - - - - ${project.artifactId} - - - org.jboss.jandex - jandex-maven-plugin - - - make-index - - jandex - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - generate-sources - - unpack - - - - - org.eclipse.microprofile.graphql - microprofile-graphql-tck - jar - true - ${project.build.directory}/classes - - - **/tests/,**/dynamic/,**/*Test.class,**/beans.xml - - - - - - - - - - - runner - - - runner - - - - - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - java - - -classpath - - -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 - io.helidon.microprofile.cdi.Main - - - - - - - - diff --git a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java index c8b36daabe0..f6b090e1a94 100644 --- a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java +++ b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java @@ -29,7 +29,9 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; +import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,6 +40,8 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.enterprise.inject.se.SeContainer; import javax.enterprise.inject.spi.CDI; @@ -409,10 +413,12 @@ private static class RunContext { static class MyClassloader extends ClassLoader implements Closeable { private final URLClassLoader wrapped; + private final Pattern excludePattern; - MyClassloader(URLClassLoader wrapped) { + MyClassloader(String excludeArchivePattern, URLClassLoader wrapped) { super(wrapped); this.wrapped = wrapped; + this.excludePattern = (null == excludeArchivePattern ? null : Pattern.compile(excludeArchivePattern)); } @Override @@ -425,6 +431,33 @@ public InputStream getResourceAsStream(String name) { return stream; } + + @Override + public Enumeration getResources(String name) throws IOException { + if (excludePattern == null) { + return super.getResources(name); + } + + if ("META-INF/beans.xml".equals(name)) { + // workaround for graphql tck - need to exclude the TCK jar + Enumeration resources = wrapped.getResources(name); + List theList = new LinkedList<>(); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + String ref = url.toString(); + Matcher matcher = excludePattern.matcher(ref); + if (matcher.matches()) { + LOGGER.info("Excluding " + url + " from bean archives."); + } else { + theList.add(url); + } + } + return Collections.enumeration(theList); + } + + return super.getResources(name); + } + @Override public void close() throws IOException { this.wrapped.close(); diff --git a/microprofile/tests/tck/pom.xml b/microprofile/tests/tck/pom.xml index 97b16837aa6..d08432573af 100644 --- a/microprofile/tests/tck/pom.xml +++ b/microprofile/tests/tck/pom.xml @@ -35,6 +35,8 @@ tck-health tck-metrics tck-messaging + tck-fault-tolerance + tck-graphql tck-jwt-auth tck-openapi tck-opentracing diff --git a/microprofile/graphql/tck/pom.xml b/microprofile/tests/tck/tck-graphql/pom.xml similarity index 58% rename from microprofile/graphql/tck/pom.xml rename to microprofile/tests/tck/tck-graphql/pom.xml index f73f5e1a0aa..ca76903c4db 100644 --- a/microprofile/graphql/tck/pom.xml +++ b/microprofile/tests/tck/tck-graphql/pom.xml @@ -1,5 +1,5 @@ + org.glassfish.jersey.media + jersey-media-json-binding + + + + + io.helidon.microprofile.graphql + helidon-microprofile-graphql-server org.eclipse.microprofile.graphql @@ -52,25 +51,22 @@ test - io.helidon.microprofile.graphql - helidon-microprofile-graphql-server - 2.0.0-SNAPSHOT + javax.xml.bind + jaxb-api + test - org.jboss.jandex - jandex-maven-plugin - - - make-index - - jandex - - - + org.apache.maven.plugins + maven-surefire-plugin + + + src/test/tck-suite.xml + + diff --git a/microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/GraphqlExtension.java b/microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/GraphqlExtension.java new file mode 100644 index 00000000000..00d46243ffa --- /dev/null +++ b/microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/GraphqlExtension.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.tck; + +import org.jboss.arquillian.core.spi.LoadableExtension; +import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; + +public class GraphqlExtension implements LoadableExtension { + @Override + public void register(ExtensionBuilder extensionBuilder) { + extensionBuilder.service(ResourceProvider.class, UrlResourceProvider.class); + extensionBuilder.service(ResourceProvider.class, UriResourceProvider.class); + } +} diff --git a/microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/UriResourceProvider.java b/microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/UriResourceProvider.java new file mode 100644 index 00000000000..9c98f1b2353 --- /dev/null +++ b/microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/UriResourceProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.tck; + +import java.lang.annotation.Annotation; +import java.net.URI; + +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; + +/** + * TCKs use addition when creating URL for a client. The default Arquillian implementation returns url without the trailing + * /. + */ +public class UriResourceProvider implements ResourceProvider { + @Override + public Object lookup(ArquillianResource arquillianResource, Annotation... annotations) { + return URI.create("http://localhost:8080/"); + } + + @Override + public boolean canProvide(Class type) { + return URI.class.isAssignableFrom(type); + } +} diff --git a/microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/UrlResourceProvider.java b/microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/UrlResourceProvider.java new file mode 100644 index 00000000000..17147c2cba7 --- /dev/null +++ b/microprofile/tests/tck/tck-graphql/src/test/java/io/helidon/microprofile/graphql/tck/UrlResourceProvider.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.tck; + +import java.lang.annotation.Annotation; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; + +/** + * TCKs use addition when creating URL for a client. The default Arquillian implementation returns url without the trailing + * /. + */ +public class UrlResourceProvider implements ResourceProvider { + @Override + public Object lookup(ArquillianResource arquillianResource, Annotation... annotations) { + try { + return URI.create("http://localhost:8080/").toURL(); + } catch (MalformedURLException e) { + return null; + } + } + + @Override + public boolean canProvide(Class type) { + return URL.class.isAssignableFrom(type); + } +} diff --git a/microprofile/tests/tck/tck-graphql/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/microprofile/tests/tck/tck-graphql/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension new file mode 100644 index 00000000000..f14b9240b3b --- /dev/null +++ b/microprofile/tests/tck/tck-graphql/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension @@ -0,0 +1,18 @@ +# +# Copyright (c) 2020 Oracle and/or its affiliates. +# +# 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. +# + +io.helidon.microprofile.graphql.tck.GraphqlExtension + diff --git a/microprofile/graphql/tck/src/test/resources/arquillian.xml b/microprofile/tests/tck/tck-graphql/src/test/resources/arquillian.xml similarity index 84% rename from microprofile/graphql/tck/src/test/resources/arquillian.xml rename to microprofile/tests/tck/tck-graphql/src/test/resources/arquillian.xml index f3fe2b8e864..4b1a0e63585 100644 --- a/microprofile/graphql/tck/src/test/resources/arquillian.xml +++ b/microprofile/tests/tck/tck-graphql/src/test/resources/arquillian.xml @@ -29,7 +29,9 @@ false true - false + + true + .*/microprofile-graphql-tck-\d+\.\d+.*?\.jar.* \ No newline at end of file diff --git a/microprofile/graphql/tck/src/test/tck-suite.xml b/microprofile/tests/tck/tck-graphql/src/test/tck-suite.xml similarity index 78% rename from microprofile/graphql/tck/src/test/tck-suite.xml rename to microprofile/tests/tck/tck-graphql/src/test/tck-suite.xml index 2c5ff3f3c60..329b52c04ff 100644 --- a/microprofile/graphql/tck/src/test/tck-suite.xml +++ b/microprofile/tests/tck/tck-graphql/src/test/tck-suite.xml @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. --> - + - + - + From fc9662772b2bdc48da6a29784363ba1baeb6fff8 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Wed, 18 Mar 2020 13:33:40 +0100 Subject: [PATCH 038/178] Fix for getResources Signed-off-by: Tomas Langer --- .../microprofile/arquillian/HelidonDeployableContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java index f6b090e1a94..f6a49354d7b 100644 --- a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java +++ b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java @@ -435,7 +435,7 @@ public InputStream getResourceAsStream(String name) { @Override public Enumeration getResources(String name) throws IOException { if (excludePattern == null) { - return super.getResources(name); + return wrapped.getResources(name); } if ("META-INF/beans.xml".equals(name)) { From ab00ac42e6ba63cd6d88f710fd447f0689a5dd43 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 19 Mar 2020 17:06:18 +0800 Subject: [PATCH 039/178] fixup tck so it runs. Not all tests working --- dependencies/pom.xml | 2 +- .../graphql/server/SchemaGenerator.java | 47 +++++++++++++------ .../graphql/server/SchemaGeneratorHelper.java | 32 +++++++++++-- .../graphql/server/SchemaGeneratorIT.java | 6 ++- microprofile/tests/tck/tck-graphql/pom.xml | 45 ++++++++++++++++++ 5 files changed, 113 insertions(+), 19 deletions(-) diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 83c121105a1..68597b0db29 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -87,7 +87,7 @@ 1.4 2.2 1.0 - 1.0 + 1.0.1 1.1.1 2.3.2 1.1.2 diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 73a5ca03472..d71eed7b324 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -179,6 +179,11 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); Input inputAnnotation = clazz.getAnnotation(Input.class); + if (typeAnnotation != null && inputAnnotation != null) { + throw new RuntimeException("Class " + clazz.getName() + " has been annotated with" + + "Type and Input"); + } + if (typeAnnotation != null || interfaceAnnotation != null) { // interface or type if (interfaceAnnotation != null && !clazz.isInterface()) { @@ -187,7 +192,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec } // assuming value for annotation overrides @Name - String typeName = getTypeName(clazz); + String typeName = getTypeName(clazz, true); SchemaType type = new SchemaType(typeName.isBlank() ? clazz.getSimpleName() : typeName, clazz.getName()); type.setIsInterface(clazz.isInterface()); Description descriptionAnnotation = clazz.getAnnotation(Description.class); @@ -207,12 +212,13 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec String clazzName = clazz.getName(); String simpleName = clazz.getSimpleName(); - SchemaInputType inputType = generateType(clazzName).createInputType(""); + SchemaInputType inputType = generateType(clazzName, true).createInputType(""); // if the name of the InputType was not changed then append "Input" if (inputType.getName().equals(simpleName)) { inputType.setName(inputType.getName() + "Input"); } if (!schema.containsInputTypeWithName(inputType.getName())) { + LOGGER.info("Adding annotated input type " + inputType.getName()); schema.addInputType(inputType); checkInputType(schema, inputType); } @@ -262,7 +268,8 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec fd.getArguments().toArray(new SchemaArgument[0]))); type.addFieldDefinition(fd); - String simpleName = getSimpleName(fd.getReturnType()); + // we are creating this as a type so ignore any Input annotation + String simpleName = getSimpleName(fd.getReturnType(), true); String returnType = fd.getReturnType(); if (!simpleName.equals(returnType)) { updateLongTypes(schema, returnType, simpleName); @@ -292,8 +299,8 @@ private void processUnresolvedTypes(Schema schema) { setUnresolvedTypes.remove(returnType); try { - //LOGGER.info("Checking unresolved type " + returnType); - String simpleName = getSimpleName(returnType); + LOGGER.info("Checking unresolved type " + returnType); + String simpleName = getSimpleName(returnType, true); SchemaScalar scalar = getScalar(returnType); if (scalar != null) { @@ -313,7 +320,8 @@ private void processUnresolvedTypes(Schema schema) { boolean fExists = schema.getTypes().stream() .filter(t -> t.getName().equals(simpleName)).count() > 0; if (!fExists) { - SchemaType newType = generateType(returnType); + SchemaType newType = generateType(returnType, false); + LOGGER.info("Adding new type of " + newType.getName()); // update any return types to the discovered scalars checkScalars(schema, newType); @@ -332,14 +340,17 @@ private void processUnresolvedTypes(Schema schema) { * Generate a {@link SchemaType} from a given class. * * @param realReturnType the class to generate type from + * @param isInputType indicates if the type is an input type and if not the Input annotation will be ignored * @return a {@link SchemaType} * @throws IntrospectionException * @throws ClassNotFoundException */ - private SchemaType generateType(String realReturnType) + private SchemaType generateType(String realReturnType, boolean isInputType) throws IntrospectionException, ClassNotFoundException { - String simpleName = getSimpleName(realReturnType); + // if isInputType=false then we ignore the name annotation in case + // an annotated input type was also used as a return type + String simpleName = getSimpleName(realReturnType, !isInputType); SchemaType type = new SchemaType(simpleName, realReturnType); Description descriptionAnnotation = Class.forName(realReturnType).getAnnotation(Description.class); if (descriptionAnnotation != null && !"".equals(descriptionAnnotation.value())) { @@ -424,14 +435,17 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, setUnresolvedTypes.add(returnType); } else { // create the input Type here - SchemaInputType inputType = generateType(returnType).createInputType("Input"); + SchemaInputType inputType = generateType(returnType, true).createInputType(""); + // if the name of the InputType was not changed then append "Input" + if (inputType.getName().equals(Class.forName(returnType).getSimpleName())) { + inputType.setName(inputType.getName() + "Input"); + } + if (!schema.containsInputTypeWithName(inputType.getName())) { schema.addInputType(inputType); + checkInputType(schema, inputType); } a.setArgumentType(inputType.getName()); - - //check the input type for any other input types - checkInputType(schema, inputType); } } if (fd != null) { @@ -492,8 +506,13 @@ private void checkInputType(Schema schema, SchemaInputType schemaInputType) } else { // must be a type, create a new input Type but do not add it to // the schema if it already exists - SchemaInputType newInputType = generateType(fdReturnType) - .createInputType("Input"); + SchemaInputType newInputType = generateType(fdReturnType, true).createInputType(""); + + // if the name of the InputType was not changed then append "Input" + if (newInputType.getName().equals(Class.forName(newInputType.getValueClassName()).getSimpleName())) { + newInputType.setName(newInputType.getName() + "Input"); + } + if (!schema.containsInputTypeWithName(newInputType.getName())) { schema.addInputType(newInputType); setInputTypes.add(newInputType); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index fb674d86308..31beef4bfd9 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -222,6 +222,20 @@ private SchemaGeneratorHelper() { */ protected static String getSimpleName(String className) throws ClassNotFoundException { + return getSimpleName(className, false); + } + + /** + * Return the simple name from a given class as a String. This takes into account any annotations that may be present. + * + * @param className class name + * @param ignoreInputNameAnnotation indicates if we should ignore the name from {@link Input} annotation as we should not + * change the name of a type if it as and {@link Input} annotation + * @return the simple class name + * @throws ClassNotFoundException if invalid class name + */ + protected static String getSimpleName(String className, boolean ignoreInputNameAnnotation) + throws ClassNotFoundException { if (INT.equals(className) || FLOAT.equals(className) || ID.equals(className) || STRING.equals(className) || BOOLEAN.equalsIgnoreCase(className) @@ -230,7 +244,7 @@ protected static String getSimpleName(String className) } // return the type name taking into account any annotations Class clazz = Class.forName(className); - return getTypeName(clazz); + return getTypeName(clazz, ignoreInputNameAnnotation); } /** @@ -389,13 +403,25 @@ protected static String getNameAnnotationValue(Class clazz) { /** * Return correct name for a Type or Enum based upon the value of the annotation or the {@link Name}. * - * @param clazz {@link Class} to introspect. + * @param clazz {@link Class} to introspect. * @return the correct name */ protected static String getTypeName(Class clazz) { + return getTypeName(clazz, false); + } + + /** + * Return correct name for a Type or Enum based upon the value of the annotation or the {@link Name}. + * + * @param clazz {@link Class} to introspect. + * @param ignoreInputNameAnnotation indicates if we should ignore the name from {@link Input} annotation as we should not + * change the name of a type if it as and {@link Input} annotation + * @return the correct name + */ + protected static String getTypeName(Class clazz, boolean ignoreInputNameAnnotation) { Type typeAnnotation = clazz.getAnnotation(Type.class); Interface interfaceAnnotation = clazz.getAnnotation(Interface.class); - Input inputAnnotation = clazz.getAnnotation(Input.class); + Input inputAnnotation = ignoreInputNameAnnotation ? null : clazz.getAnnotation(Input.class); Enum enumAnnotation = clazz.getAnnotation(Enum.class); Query queryAnnotation = clazz.getAnnotation(Query.class); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index 6b7f1c55979..5a5ecd6105e 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -192,6 +192,7 @@ public void testInterfaceDiscoveryWithImplementorsWithNoTypeAnnotation() */ @Test public void testInterfaceDiscoveryWithUnresolvedType() throws IOException, IntrospectionException, ClassNotFoundException { + System.out.println("TESTING"); setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, VehicleIncident.class, AbstractVehicle.class); assertInterfaceResults(); } @@ -206,7 +207,7 @@ public void testInterfaceDiscoveryWithoutTypes() throws IOException, Introspecti } @Test - public void testObjectWithIgnorableFieldsf() throws IOException { + public void testObjectWithIgnorableFields() throws IOException { setupIndex(indexFileName, ObjectWithIgnorableFields.class); ExecutionContext executionContext = new ExecutionContext<>(dummyContext); ExecutionResult result = executionContext.execute("query { hero }"); @@ -608,13 +609,16 @@ private String getContactAsQueryInput(SimpleContact contact) { private void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException { SchemaGenerator schemaGenerator = new SchemaGenerator(); Schema schema = schemaGenerator.generateSchema(); + System.out.println(schema.generateGraphQLSchema()); assertThat(schema, is(notNullValue())); + schema.getTypes().forEach(t -> System.out.println(t.getName())); assertThat(schema.getTypes().size(), is(6)); assertThat(schema.getTypeByName("Vehicle"), is(notNullValue())); assertThat(schema.getTypeByName("Car"), is(notNullValue())); assertThat(schema.getTypeByName("Motorbike"), is(notNullValue())); assertThat(schema.getTypeByName("Incident"), is(notNullValue())); assertThat(schema.getTypeByName("Query"), is(notNullValue())); + assertThat(schema.getTypeByName("Mutation"), is(notNullValue())); generateGraphQLSchema(schema); } } diff --git a/microprofile/tests/tck/tck-graphql/pom.xml b/microprofile/tests/tck/tck-graphql/pom.xml index ca76903c4db..8064c866ce0 100644 --- a/microprofile/tests/tck/tck-graphql/pom.xml +++ b/microprofile/tests/tck/tck-graphql/pom.xml @@ -59,6 +59,51 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + generate-sources + + unpack + + + + + org.eclipse.microprofile.graphql + microprofile-graphql-tck + jar + true + ${project.build.directory}/test-classes + + + **/tests/,**/dynamic/,**/*Test.class,**/beans.xml + + + + + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + jandex + + + process-classes + + + + ${project.build.directory}/test-classes + + + + + + org.apache.maven.plugins maven-surefire-plugin From 317911802e5628cb5e84e145dc7938e783de197d Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 20 Mar 2020 15:45:47 +0800 Subject: [PATCH 040/178] initial work on formatting --- .../graphql/server/DataFetcherUtils.java | 3 +- .../graphql/server/FormattingHelper.java | 120 ++++++++++++++++++ .../graphql/server/SchemaArgument.java | 38 +++++- .../graphql/server/SchemaFieldDefinition.java | 24 ++++ .../graphql/server/SchemaGenerator.java | 78 ++++++++++-- .../graphql/server/SchemaGeneratorHelper.java | 92 -------------- .../graphql/server/SchemaArgumentTest.java | 9 ++ .../server/SchemaFieldDefinitionTest.java | 8 ++ .../graphql/server/SchemaGeneratorIT.java | 15 ++- .../graphql/server/SchemaGeneratorTest.java | 20 ++- .../test/queries/NumberFormatQueries.java | 44 +++++++ .../types/SimpleContactWithNumberFormats.java | 69 ++++------ 12 files changed, 360 insertions(+), 160 deletions(-) create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueries.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index dba3337d8cc..c1513970800 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -19,7 +19,6 @@ import java.lang.reflect.Method; import java.text.NumberFormat; import java.util.ArrayList; -import java.util.Locale; import java.util.Map; import java.util.UUID; @@ -29,7 +28,7 @@ import graphql.schema.PropertyDataFetcherHelper; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getCorrectFormat; +import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectFormat; /** * Utilities for working with {@link DataFetcher}s. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java new file mode 100644 index 00000000000..0b1f846dd8c --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +import javax.json.bind.annotation.JsonbNumberFormat; + +/** + * Helper class for number formatting. + */ +public class FormattingHelper { + /** + * Returna {@link NumberFormat} for the given type, locale and format. + * + * @param type the GraphQL type or scalar + * @param locale the locale, either "" or the correct locale + * @param format the format to use, may be null + * @return The correct {@link NumberFormat} for the given type and locale + */ + protected static NumberFormat getCorrectFormat(String type, String locale, String format) { + Locale actualLocale = SchemaGeneratorHelper.DEFAULT_LOCALE.equals(locale) + ? Locale.getDefault() + : Locale.forLanguageTag(locale); + NumberFormat numberFormat; + if (SchemaGeneratorHelper.FLOAT.equals(type) || SchemaGeneratorHelper.BIG_DECIMAL.equals(type)) { + numberFormat = NumberFormat.getNumberInstance(actualLocale); + } else if (SchemaGeneratorHelper.INT.equals(type) || SchemaGeneratorHelper.BIG_INTEGER.equals(type)) { + numberFormat = NumberFormat.getIntegerInstance(actualLocale); + } else { + return null; + } + if (format != null && !format.trim().equals("")) { + ((DecimalFormat) numberFormat).applyPattern(format); + } + return numberFormat; + } + + /** + * Returna {@link NumberFormat} for the given type and locale. + * + * @param type the GraphQL type or scalar + * @param locale the locale, either "" or the correct locale + * @return The correct {@link NumberFormat} for the given type and locale + */ + protected static NumberFormat getCorrectFormat(String type, String locale) { + return getCorrectFormat(type, locale, null); + } + + /** + * Return the format and locale for a field if they exist in a {@link String} array. + * + * @param field the {@link Field} to check + * @return the format ([0]) and locale ([1]) for a field in a {@link String} array or an empty array if not + */ + protected static String[] getFormatAnnotation(Field field) { + return getFormatAnnotationInternal(field.getAnnotation(JsonbNumberFormat.class), field + .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class)); + } + + /** + * Return the format and locale for a parameter if they exist in a {@link String} array. + * + * @param parameter the {@link Field} to check + * @return the format ([0]) and locale ([1]) for a parameter in a {@link String} array or an empty array if not + */ + protected static String[] getFormatAnnotation(Parameter parameter) { + return getFormatAnnotationInternal(parameter.getAnnotation(JsonbNumberFormat.class), parameter + .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class)); + } + + /** + * Return the format and locale for a method if they exist in a {@link String} array. + * + * @param method the {@link Method} to check + * @return the format ([0]) and locale ([1]) for a method in a {@link String} array or an empty array if not + */ + protected static String[] getFormatAnnotation(Method method) { + return getFormatAnnotationInternal(method.getAnnotation(JsonbNumberFormat.class), + method.getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class)); + } + + /** + * Return the format and locate for the given annotations. + * + * @param jsonbNumberFormat {@link JsonbNumberFormat} annotation, may be null + * @param numberFormat {@Link org.eclipse.microprofile.graphql.NumberFormat} annotation, may be none + * @return the format ([0]) and locale ([1]) for a method in a {@link String} array or an empty array if not + */ + private static String[] getFormatAnnotationInternal(JsonbNumberFormat jsonbNumberFormat, + org.eclipse.microprofile.graphql.NumberFormat numberFormat) { + // check @NumberFormat first as this takes precedence + if (numberFormat != null) { + return new String[] { numberFormat.value(), numberFormat.locale() }; + } + if (jsonbNumberFormat != null) { + return new String[] { jsonbNumberFormat.value(), jsonbNumberFormat.locale() }; + } + return new String[0]; + } +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index 90a3e79c3f6..d85a67d03a1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -50,19 +50,23 @@ public class SchemaArgument private final Class originalType; /** - * Indicates if this argument is a source argument, which should be excluded from the - * query parameters. + * Indicates if this argument is a source argument, which should be excluded from the query parameters. */ private boolean sourceArgument; + /** + * Defines the format for a number or date. + */ + private String[] format; + /** * Construct a {@link SchemaArgument} instance. * * @param argumentName name of the argument * @param argumentType type of the argument * @param isMandatory indicates if the argument is mandatory - * @param defaultValue default value for the argument - * @param originalType original argument type before it was converted to a GraphQL representation. + * @param defaultValue default value for the argument + * @param originalType original argument type before it was converted to a GraphQL representation. */ public SchemaArgument(String argumentName, String argumentType, boolean isMandatory, Object defaultValue, Class originalType) { @@ -163,7 +167,8 @@ public Class getOriginalType() { /** * Indicates if the argument is a source argument. - * @return if the argument is a source argument. + * + * @return if the argument is a source argument. */ public boolean isSourceArgument() { return sourceArgument; @@ -171,12 +176,31 @@ public boolean isSourceArgument() { /** * Set if the argument is a source argument. + * * @param sourceArgument if the argument is a source argument. */ public void setSourceArgument(boolean sourceArgument) { this.sourceArgument = sourceArgument; } + /** + * Return the format for a number or date. + * + * @return the format for a number or date + */ + public String[] getFormat() { + return format; + } + + /** + * Set the format for a number or date. + * + * @param format the format for a number or date + */ + public void setFormat(String[] format) { + this.format = format; + } + @Override public String toString() { return "Argument{" @@ -186,6 +210,7 @@ public String toString() { + ", defaultValue=" + defaultValue + ", originalType=" + originalType + ", sourceArgument=" + sourceArgument + + ", format=" + format + ", description='" + getDescription() + '\'' + '}'; } @@ -205,6 +230,7 @@ public boolean equals(Object o) { && Objects.equals(argumentName, schemaArgument.argumentName) && Objects.equals(argumentType, schemaArgument.argumentType) && Objects.equals(originalType, schemaArgument.originalType) + && Objects.equals(format, schemaArgument.format) && Objects.equals(sourceArgument, schemaArgument.sourceArgument) && Objects.equals(getDescription(), schemaArgument.getDescription()) && Objects.equals(defaultValue, schemaArgument.defaultValue); @@ -213,6 +239,6 @@ public boolean equals(Object o) { @Override public int hashCode() { return Objects.hash(super.hashCode(), argumentName, argumentType, sourceArgument, - isMandatory, defaultValue, getDescription(), originalType); + isMandatory, defaultValue, getDescription(), originalType, format); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index 619d60a5b60..6fc2f07245e 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -63,6 +63,11 @@ public class SchemaFieldDefinition */ private DataFetcher dataFetcher; + /** + * Defines the format for a number or date. + */ + private String[] format; + /** * Construct a {@link SchemaFieldDefinition}. * @@ -219,6 +224,24 @@ public int getArrayLevels() { return arrayLevels; } + /** + * Return the format for a number or date. + * + * @return the format for a number or date + */ + public String[] getFormat() { + return format; + } + + /** + * Set the format for a number or date. + * + * @param format the format for a number or date + */ + public void setFormat(String[] format) { + this.format = format; + } + @Override public String toString() { return "FieldDefinition{" @@ -228,6 +251,7 @@ public String toString() { + ", isReturnTypeMandatory=" + isReturnTypeMandatory + ", listArguments=" + listSchemaArguments + ", arrayLevels=" + arrayLevels + + ", format=" + format + ", description='" + getDescription() + '\'' + '}'; } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index d71eed7b324..5b508fe9bdd 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -61,7 +61,7 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.checkScalars; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getArrayLevels; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldName; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFormatAnnotation; +import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormatAnnotation; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getGraphQLType; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getMethodName; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootArrayClass; @@ -181,7 +181,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec if (typeAnnotation != null && inputAnnotation != null) { throw new RuntimeException("Class " + clazz.getName() + " has been annotated with" - + "Type and Input"); + + " both Type and Input"); } if (typeAnnotation != null || interfaceAnnotation != null) { @@ -251,7 +251,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec }); }); - // process any additional methods requires via the @Source annotation + // process any additional methods require via the @Source annotation for (DiscoveredMethod dm : setAdditionalMethods) { // add the discovered method to the type SchemaType type = schema.getTypeByClass(dm.source); @@ -340,7 +340,7 @@ private void processUnresolvedTypes(Schema schema) { * Generate a {@link SchemaType} from a given class. * * @param realReturnType the class to generate type from - * @param isInputType indicates if the type is an input type and if not the Input annotation will be ignored + * @param isInputType indicates if the type is an input type and if not the Input annotation will be ignored * @return a {@link SchemaType} * @throws IntrospectionException * @throws ClassNotFoundException @@ -539,7 +539,6 @@ private void addTypeToSchema(Schema schema, SchemaType type) String valueClassName = type.getValueClassName(); retrieveGetterBeanMethods(Class.forName(valueClassName)).forEach((k, v) -> { - SchemaFieldDefinition fd = newFieldDefinition(v, null); type.addFieldDefinition(fd); @@ -590,7 +589,8 @@ private SchemaEnum generateEnum(Class clazz) { */ @SuppressWarnings("rawTypes") private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String optionalName) { - String sValueClassName = method.getReturnType(); + String valueClassName = method.getReturnType(); + String graphQLType = getGraphQLType(valueClassName); DataFetcher dataFetcher = null; boolean isArrayReturnType = method.isArrayReturnType || method.isCollectionType() || method.isMap(); @@ -602,14 +602,27 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String } } + // check for format on the return type of methods + String[] format = method.getFormat(); + if (method.getPropertyName() != null && format != null && format.length == 2) { + if (!isGraphQLType(valueClassName)) { + + dataFetcher = DataFetcherUtils + .newNumberFormatDataFetcher(method.getPropertyName(), graphQLType, format[0], format[1]); + // we must change the type of this to a String but keep the above type for the above data fetcher + graphQLType = SchemaGeneratorHelper.STRING; + } + } + SchemaFieldDefinition fd = new SchemaFieldDefinition(optionalName != null ? optionalName : method.name, - getGraphQLType(sValueClassName), + graphQLType, isArrayReturnType, false, method.getArrayLevels()); fd.setDataFetcher(dataFetcher); + fd.setFormat(method.getFormat()); return fd; } @@ -735,7 +748,7 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class String name = method.getName(); String varName; - String numberFormat[]; + String[] numberFormat = new String[0]; if (name.startsWith(IS) || name.startsWith(GET) || name.startsWith(SET)) { // this is a getter method @@ -807,6 +820,8 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class DiscoveredMethod discoveredMethod = new DiscoveredMethod(); discoveredMethod.setName(varName); discoveredMethod.setMethod(method); + discoveredMethod.setFormat(numberFormat); + discoveredMethod.setPropertyName(pd != null ? pd.getName() : null); Parameter[] parameters = method.getParameters(); if (parameters != null && parameters.length > 0) { @@ -832,6 +847,7 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class } SchemaArgument argument = new SchemaArgument(parameterName, returnType.getReturnClass(), false, null, paramType); + argument.setFormat(getFormatAnnotation(parameter)); Source sourceAnnotation = parameter.getAnnotation(Source.class); if (sourceAnnotation != null) { @@ -975,12 +991,22 @@ public static class DiscoveredMethod { */ private String source; + /** + * The property name if the method is a getter. + */ + private String propertyName; + /** * Indicates if the method containing the {@link Source} annotation was also annotated with the {@link Query} annotation. * If true, then this indicates that a top level query shoudl also be created as well as the field in the type. */ private boolean isQueryAnnotated = false; + /** + * Defines the format for a number or date. + */ + private String[] format; + /** * Default constructor. */ @@ -1194,6 +1220,42 @@ public void setQueryAnnotated(boolean queryAnnotated) { isQueryAnnotated = queryAnnotated; } + /** + * Return the format for a number or date. + * + * @return the format for a number or date + */ + public String[] getFormat() { + return format; + } + + /** + * Set the format for a number or date. + * + * @param format the format for a number or date + */ + public void setFormat(String[] format) { + this.format = format; + } + + /** + * Return the property name if the method is a getter. + * + * @return property name if the method is a getter + */ + public String getPropertyName() { + return propertyName; + } + + /** + * Set the property name if the method is a getter. + * + * @param propertyName property name + */ + public void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + @Override public String toString() { return "DiscoveredMethod{" diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 31beef4bfd9..3e1825b030a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -18,12 +18,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; import java.math.BigInteger; -import java.text.DecimalFormat; -import java.text.NumberFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -34,10 +31,8 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import javax.json.bind.annotation.JsonbNumberFormat; import javax.json.bind.annotation.JsonbProperty; import graphql.Scalars; @@ -509,93 +504,6 @@ protected static String getRootArrayClass(String clazz) { return clazz.replaceAll("\\[", "").replaceAll(";", "").replaceAll("^L", ""); } - /** - * Returna {@link NumberFormat} for the given type, locale and format. - * - * @param type the GraphQL type or scalar - * @param locale the locale, either "" or the correct locale - * @param format the format to use, may be null - * @return The correct {@link NumberFormat} for the given type and locale - */ - protected static NumberFormat getCorrectFormat(String type, String locale, String format) { - Locale actualLocale = DEFAULT_LOCALE.equals(locale) ? Locale.getDefault() : Locale.forLanguageTag(locale); - NumberFormat numberFormat; - if (FLOAT.equals(type) || BIG_DECIMAL.equals(type)) { - numberFormat = NumberFormat.getNumberInstance(actualLocale); - } else if (INT.equals(type) || BIG_INTEGER.equals(type)) { - numberFormat = NumberFormat.getIntegerInstance(actualLocale); - } else { - return null; - } - if (format != null && !format.trim().equals("")) { - ((DecimalFormat) numberFormat).applyPattern(format); - } - return numberFormat; - } - - /** - * Returna {@link NumberFormat} for the given type and locale. - * - * @param type the GraphQL type or scalar - * @param locale the locale, either "" or the correct locale - * @return The correct {@link NumberFormat} for the given type and locale - */ - protected static NumberFormat getCorrectFormat(String type, String locale) { - return getCorrectFormat(type, locale, null); - } - - /** - * Return the format and locale for a field if they exist in a {@link String} array. - * - * @param field the {@link Field} to check - * @return the format ([0]) and locale ([1]) for a field in a {@link String} array or an empty array if not - */ - protected static String[] getFormatAnnotation(Field field) { - return getFormatAnnotationInternal(field.getAnnotation(JsonbNumberFormat.class), field - .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class)); - } - - /** - * Return the format and locale for a parameter if they exist in a {@link String} array. - * - * @param parameter the {@link Field} to check - * @return the format ([0]) and locale ([1]) for a parameter in a {@link String} array or an empty array if not - */ - protected static String[] getFormatAnnotation(Parameter parameter) { - return getFormatAnnotationInternal(parameter.getAnnotation(JsonbNumberFormat.class), parameter - .getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class)); - } - - /** - * Return the format and locale for a method if they exist in a {@link String} array. - * - * @param method the {@link Method} to check - * @return the format ([0]) and locale ([1]) for a method in a {@link String} array or an empty array if not - */ - protected static String[] getFormatAnnotation(Method method) { - return getFormatAnnotationInternal(method.getAnnotation(JsonbNumberFormat.class), - method.getAnnotation(org.eclipse.microprofile.graphql.NumberFormat.class)); - } - - /** - * Return the format and locate for the given annotations. - * - * @param jsonbNumberFormat {@link JsonbNumberFormat} annotation, may be null - * @param numberFormat {@Link org.eclipse.microprofile.graphql.NumberFormat} annotation, may be none - * @return the format ([0]) and locale ([1]) for a method in a {@link String} array or an empty array if not - */ - private static String[] getFormatAnnotationInternal(JsonbNumberFormat jsonbNumberFormat, - org.eclipse.microprofile.graphql.NumberFormat numberFormat) { - // check @NumberFormat first as this takes precedence - if (numberFormat != null) { - return new String[] { numberFormat.value(), numberFormat.locale() }; - } - if (jsonbNumberFormat != null) { - return new String[] { jsonbNumberFormat.value(), jsonbNumberFormat.locale() }; - } - return new String[0]; - } - /** * Return the inner most root type such as {@link String} for a List of List of String. * diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index 2745ed20f77..4583e06acc0 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -56,6 +57,14 @@ public void testConstructors() { assertThat(schemaArgument.isSourceArgument(), is(false)); schemaArgument.setSourceArgument(true); assertThat(schemaArgument.isSourceArgument(), is(true)); + + assertThat(schemaArgument.getFormat(), is(nullValue())); + schemaArgument.setFormat(new String[] { "value-1", "value-2"}); + String[] format = schemaArgument.getFormat(); + assertThat(format, is(notNullValue())); + assertThat(format.length, is(2)); + assertThat(format[0], is("value-1")); + assertThat(format[1], is("value-2")); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java index a8f97cbf4b2..87695812914 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java @@ -63,6 +63,14 @@ public void testConstructors() { schemaFieldDefinition.setReturnType("BLAH"); assertThat(schemaFieldDefinition.getReturnType(), is("BLAH")); + + assertThat(schemaFieldDefinition.getFormat(), is(nullValue())); + schemaFieldDefinition.setFormat(new String[] {"a", "b"}); + String[] format = schemaFieldDefinition.getFormat(); + assertThat(format, is(notNullValue())); + assertThat(format.length, is(2)); + assertThat(format[0], is("a")); + assertThat(format[1], is("b")); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index 5a5ecd6105e..5c7caedbb57 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -37,6 +37,7 @@ import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations; import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations; import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; +import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueries; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; @@ -77,6 +78,7 @@ /** * Integration tests for {@link SchemaGeneratorTest}. */ +@SuppressWarnings("unchecked") public class SchemaGeneratorIT extends AbstractGraphQLTest { private String indexFileName = null; @@ -234,9 +236,18 @@ public void testSimpleContactWithSelf() throws IOException { @Test public void testNumberFormats() throws IOException { - setupIndex(indexFileName, SimpleContactWithNumberFormats.class); + setupIndex(indexFileName, SimpleContactWithNumberFormats.class, NumberFormatQueries.class); ExecutionContext executionContext = new ExecutionContext<>(dummyContext); - System.out.println("schema=" + executionContext.getSchema()); + ExecutionResult result = executionContext.execute("query { simpleFormattingQuery { id name age bankBalance value longValue } }"); + Map mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + + Map mapResults2 = (Map) mapResults.get("simpleFormattingQuery"); + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("age"), is("50 years old")); + assertThat(mapResults2.get("bankBalance"), is("$ 1200.00")); + assertThat(mapResults2.get("value"), is("10 value")); + assertThat(mapResults2.get("longValue"), is("Long-123456789")); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index bcf74a40e75..4df6a9b4b5d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -19,7 +19,6 @@ import java.beans.IntrospectionException; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; @@ -63,7 +62,7 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DEFAULT_LOCALE; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FLOAT; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFormatAnnotation; +import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormatAnnotation; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootTypeName; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; @@ -347,19 +346,19 @@ public void testGetRootType() throws NoSuchFieldException { @Test public void testGetCorrectFormat() { - NumberFormat numberFormat = SchemaGeneratorHelper.getCorrectFormat(INT, ""); + NumberFormat numberFormat = FormattingHelper.getCorrectFormat(INT, ""); assertThat(numberFormat, is(notNullValue())); assertThat(numberFormat.getMaximumFractionDigits(), is(0)); - numberFormat = SchemaGeneratorHelper.getCorrectFormat(BIG_INTEGER, ""); + numberFormat = FormattingHelper.getCorrectFormat(BIG_INTEGER, ""); assertThat(numberFormat, is(notNullValue())); assertThat(numberFormat.getMaximumFractionDigits(), is(0)); - numberFormat = SchemaGeneratorHelper.getCorrectFormat(FLOAT, ""); + numberFormat = FormattingHelper.getCorrectFormat(FLOAT, ""); assertThat(numberFormat, is(notNullValue())); assertThat(numberFormat.getMaximumFractionDigits() > 0, is(true)); - numberFormat = SchemaGeneratorHelper.getCorrectFormat(BIG_DECIMAL, ""); + numberFormat = FormattingHelper.getCorrectFormat(BIG_DECIMAL, ""); assertThat(numberFormat, is(notNullValue())); assertThat(numberFormat.getMaximumFractionDigits() > 0, is(true)); } @@ -391,6 +390,13 @@ public void testGetFormatAnnotation() throws NoSuchFieldException, NoSuchMethodE assertAnnotation(SimpleContactWithNumberFormats.class, "method", "getName", 0, null, null); } + @Test + public void testFormatAnnotationFromSchema() throws IntrospectionException, ClassNotFoundException { + SchemaGenerator schemaGenerator = new SchemaGenerator(); + Schema schema = schemaGenerator.generateSchemaFromClasses(SimpleContactWithNumberFormats.class); + assertThat(schema, is(notNullValue())); + } + /** * Assert that a {@link org.eclipse.microprofile.graphql.NumberFormat} or {@link javax.json.bind.annotation.JsonbNumberFormat} * annotation is correclty applied. @@ -453,7 +459,7 @@ private void assertAnnotation(Class clazz, * @param expectedResult expected result */ private void assertFormat(String type, String locale, String format, Object value, String expectedResult) { - NumberFormat numberFormat = SchemaGeneratorHelper.getCorrectFormat(type, locale, format); + NumberFormat numberFormat = FormattingHelper.getCorrectFormat(type, locale, format); assertThat(numberFormat, is(notNullValue())); String formatted = numberFormat.format(value); assertThat(formatted, is(notNullValue())); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueries.java new file mode 100644 index 00000000000..12403064260 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueries.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds queries that have various formatting types. + */ +@GraphQLApi +@ApplicationScoped +public class NumberFormatQueries { + + @Inject + private TestDB testDB; + + public NumberFormatQueries() { + } + + @Query("simpleFormattingQuery") + public SimpleContactWithNumberFormats retrieveFormattedObject() { + return new SimpleContactWithNumberFormats(1, "Tim", 50, 1200.0f, 10, 123456789L); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java index 2e98543f4b2..37b3a917359 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java @@ -16,10 +16,6 @@ package io.helidon.microprofile.graphql.server.test.types; -import java.math.BigDecimal; -import java.text.DecimalFormat; -import java.util.Objects; - import javax.json.bind.annotation.JsonbNumberFormat; import org.eclipse.microprofile.graphql.NumberFormat; @@ -30,39 +26,44 @@ */ @Type public class SimpleContactWithNumberFormats { - private String id; + private Integer id; private String name; @NumberFormat("0 'years old'") - private int age; + private Integer age; @JsonbNumberFormat(value = "¤ 000.00", locale = "en-AU") - private float bankBalance; + private Float bankBalance; @JsonbNumberFormat(value = "000") // this should be ignored @NumberFormat("0 'value'") // this should be applied - private int value; + private Integer value; + + private Long longValue; - public int getFormatMethod(@NumberFormat("0 'years old'") int age) { + public Integer getFormatMethod(@NumberFormat("0 'years old'") int age) { return age; } - public SimpleContactWithNumberFormats(String id, - String name, - int age, float bankBalance, int value) { + public SimpleContactWithNumberFormats(Integer id, + String name, Integer age, + Float bankBalance, + int value, Long longValue) { this.id = id; this.name = name; this.age = age; this.bankBalance = bankBalance; this.value = value; + this.longValue = longValue; } @NumberFormat("0 'id'") - public String getId() { + + public Integer getId() { return id; } - public void setId(String id) { + public void setId(Integer id) { this.id = id; } @@ -74,19 +75,19 @@ public void setName(String name) { this.name = name; } - public int getAge() { + public Integer getAge() { return age; } - public void setAge(int age) { + public void setAge(Integer age) { this.age = age; } - public float getBankBalance() { + public Float getBankBalance() { return bankBalance; } - public void setBankBalance(float bankBalance) { + public void setBankBalance(Float bankBalance) { this.bankBalance = bankBalance; } @@ -98,34 +99,16 @@ public void setValue(int value) { this.value = value; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SimpleContactWithNumberFormats that = (SimpleContactWithNumberFormats) o; - return age == that.age - && Float.compare(that.bankBalance, bankBalance) == 0 - && Objects.equals(id, that.id) - && Objects.equals(value, that.value) - && Objects.equals(name, that.name); + public void setValue(Integer value) { + this.value = value; } - @Override - public int hashCode() { - return Objects.hash(id, name, age, bankBalance, value); + @NumberFormat("Long-#############") + public Long getLongValue() { + return longValue; } - @Override - public String toString() { - return "SimpleContactWithNumberFormats{" - + "id='" + id + '\'' - + ", name='" + name + '\'' - + ", age=" + age - + ", value" + value - + ", bankBalance=" + bankBalance + '}'; + public void setLongValue(Long longValue) { + this.longValue = longValue; } } From a53d003aa5de2b97265fe305055527e837e532a8 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 24 Mar 2020 15:36:36 +0800 Subject: [PATCH 041/178] more work on number formatting --- .../graphql/server/DataFetcherUtils.java | 2 +- .../graphql/server/SchemaGenerator.java | 42 ++++++++++++++++--- .../graphql/server/SchemaGeneratorIT.java | 12 ++++-- ...a => NumberFormatQueriesAndMutations.java} | 18 ++++++-- .../types/SimpleContactWithNumberFormats.java | 4 +- 5 files changed, 61 insertions(+), 17 deletions(-) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/{NumberFormatQueries.java => NumberFormatQueriesAndMutations.java} (72%) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index c1513970800..51966b1a70c 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -105,7 +105,7 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met * @param type of the source * @return a new {@link DataFetcher} */ - public static DataFetcher newNumberFormatDataFetcher(String propertyName, String type, String valueFormat, String locale) { + public static DataFetcher newNumberFormatPropertyDataFetcher(String propertyName, String type, String valueFormat, String locale) { NumberFormat numberFormat = getCorrectFormat(type, locale, valueFormat); return environment -> { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 5b508fe9bdd..33b035f6aed 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -24,6 +24,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; +import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -42,6 +43,7 @@ import javax.json.bind.annotation.JsonbTransient; import graphql.schema.DataFetcher; +import graphql.schema.DataFetcherFactories; import org.eclipse.microprofile.graphql.DefaultValue; import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.GraphQLApi; @@ -55,9 +57,11 @@ import org.eclipse.microprofile.graphql.Source; import org.eclipse.microprofile.graphql.Type; +import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectFormat; import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.MUTATION_TYPE; import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.QUERY_TYPE; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.checkScalars; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getArrayLevels; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldName; @@ -263,6 +267,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec .forEach(fd::addArgument); } + // check for existing DataFetcher fd.setDataFetcher(DataFetcherUtils.newMethodDataFetcher( dm.method.getDeclaringClass(), dm.method, dm.getSource(), fd.getArguments().toArray(new SchemaArgument[0]))); @@ -454,8 +459,23 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, } if (fd != null) { - DataFetcher dataFetcher = DataFetcherUtils - .newMethodDataFetcher(clazz, method, null, fd.getArguments().toArray(new SchemaArgument[0])); + DataFetcher dataFetcher = null; + String[] format = discoveredMethod.format; + if (format.length == 2) { + // a format exists on the method return type so format it after returning the value + final String graphQLType = getGraphQLType(fd.getReturnType()); + dataFetcher = DataFetcherFactories.wrapDataFetcher(DataFetcherUtils + .newMethodDataFetcher(clazz, method, null, fd.getArguments().toArray(new SchemaArgument[0])), + (d, v) -> { + NumberFormat numberFormat = getCorrectFormat(graphQLType, format[1], format[0]); + return v != null ? numberFormat.format(v) : null; + }); + fd.setReturnType(STRING); + } else { + // no formatting, just call the method + dataFetcher = DataFetcherUtils + .newMethodDataFetcher(clazz, method, null, fd.getArguments().toArray(new SchemaArgument[0])); + } fd.setDataFetcher(dataFetcher); schemaType.addFieldDefinition(fd); @@ -602,13 +622,13 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String } } - // check for format on the return type of methods + // check for format on the property String[] format = method.getFormat(); if (method.getPropertyName() != null && format != null && format.length == 2) { if (!isGraphQLType(valueClassName)) { dataFetcher = DataFetcherUtils - .newNumberFormatDataFetcher(method.getPropertyName(), graphQLType, format[0], format[1]); + .newNumberFormatPropertyDataFetcher(method.getPropertyName(), graphQLType, format[0], format[1]); // we must change the type of this to a String but keep the above type for the above data fetcher graphQLType = SchemaGeneratorHelper.STRING; } @@ -817,6 +837,16 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class } } + // check for method return type number format + String[] methodNumberFormat = getFormatAnnotation(method); + if (methodNumberFormat.length == 2) { + numberFormat = methodNumberFormat; + } + + if (numberFormat.length == 2 && ID.equals(returnClazzName)) { + throw new RuntimeException("Unable to format an ID type"); + } + DiscoveredMethod discoveredMethod = new DiscoveredMethod(); discoveredMethod.setName(varName); discoveredMethod.setMethod(method); @@ -998,14 +1028,14 @@ public static class DiscoveredMethod { /** * Indicates if the method containing the {@link Source} annotation was also annotated with the {@link Query} annotation. - * If true, then this indicates that a top level query shoudl also be created as well as the field in the type. + * If true, then this indicates that a top level query should also be created as well as the field in the type. */ private boolean isQueryAnnotated = false; /** * Defines the format for a number or date. */ - private String[] format; + private String[] format = new String[0]; /** * Default constructor. diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index 5c7caedbb57..26f514b058a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -37,7 +37,7 @@ import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations; import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations; import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; -import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueries; +import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; @@ -236,7 +236,7 @@ public void testSimpleContactWithSelf() throws IOException { @Test public void testNumberFormats() throws IOException { - setupIndex(indexFileName, SimpleContactWithNumberFormats.class, NumberFormatQueries.class); + setupIndex(indexFileName, SimpleContactWithNumberFormats.class, NumberFormatQueriesAndMutations.class); ExecutionContext executionContext = new ExecutionContext<>(dummyContext); ExecutionResult result = executionContext.execute("query { simpleFormattingQuery { id name age bankBalance value longValue } }"); Map mapResults = getAndAssertResult(result); @@ -247,7 +247,13 @@ public void testNumberFormats() throws IOException { assertThat(mapResults2.get("age"), is("50 years old")); assertThat(mapResults2.get("bankBalance"), is("$ 1200.00")); assertThat(mapResults2.get("value"), is("10 value")); - assertThat(mapResults2.get("longValue"), is("Long-123456789")); + assertThat(mapResults2.get("longValue"), is(BigInteger.valueOf(Long.MAX_VALUE))); + + result = executionContext.execute("mutation { generateDoubleValue }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults, is(notNullValue())); + assertThat(mapResults.get("generateDoubleValue"), is("Double-123456789")); + } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java similarity index 72% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueries.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java index 12403064260..96e2526f88f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueries.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java @@ -16,29 +16,39 @@ package io.helidon.microprofile.graphql.server.test.queries; +import java.math.BigDecimal; + import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Mutation; +import org.eclipse.microprofile.graphql.NumberFormat; import org.eclipse.microprofile.graphql.Query; /** - * Class that holds queries that have various formatting types. + * Class that holds queries and mutations that have various formatting types. */ @GraphQLApi @ApplicationScoped -public class NumberFormatQueries { +public class NumberFormatQueriesAndMutations { @Inject private TestDB testDB; - public NumberFormatQueries() { + public NumberFormatQueriesAndMutations() { } @Query("simpleFormattingQuery") public SimpleContactWithNumberFormats retrieveFormattedObject() { - return new SimpleContactWithNumberFormats(1, "Tim", 50, 1200.0f, 10, 123456789L); + return new SimpleContactWithNumberFormats(1, "Tim", 50, 1200.0f, 10, Long.MAX_VALUE); + } + + @Mutation("generateDoubleValue") + @NumberFormat("Double-###########") + public Double generateDouble() { + return 123456789d; } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java index 37b3a917359..cc7cf2dbb9e 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java @@ -58,7 +58,6 @@ public SimpleContactWithNumberFormats(Integer id, } @NumberFormat("0 'id'") - public Integer getId() { return id; } @@ -102,8 +101,7 @@ public void setValue(int value) { public void setValue(Integer value) { this.value = value; } - - @NumberFormat("Long-#############") + public Long getLongValue() { return longValue; } From 5880b9d23e095c264f1caa732080fa98a4b8c414 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 24 Mar 2020 16:29:28 +0800 Subject: [PATCH 042/178] working on number format --- .../graphql/server/DataFetcherUtils.java | 4 ++ .../graphql/server/FormattingHelper.java | 16 ++++++-- .../graphql/server/SchemaGeneratorHelper.java | 39 ++++++++++--------- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 51966b1a70c..c6411ede040 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -107,6 +107,10 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met */ public static DataFetcher newNumberFormatPropertyDataFetcher(String propertyName, String type, String valueFormat, String locale) { NumberFormat numberFormat = getCorrectFormat(type, locale, valueFormat); + if (numberFormat == null) { + throw new RuntimeException("Unable to find number format for type=" + + type + ", locale=" + locale + ", valueFormat=" + valueFormat); + } return environment -> { S source = environment.getSource(); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java index 0b1f846dd8c..a8cbce864f3 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java @@ -25,10 +25,14 @@ import javax.json.bind.annotation.JsonbNumberFormat; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.*; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INTEGER_LIST; + /** * Helper class for number formatting. */ public class FormattingHelper { + /** * Returna {@link NumberFormat} for the given type, locale and format. * @@ -38,13 +42,19 @@ public class FormattingHelper { * @return The correct {@link NumberFormat} for the given type and locale */ protected static NumberFormat getCorrectFormat(String type, String locale, String format) { - Locale actualLocale = SchemaGeneratorHelper.DEFAULT_LOCALE.equals(locale) + Locale actualLocale = DEFAULT_LOCALE.equals(locale) ? Locale.getDefault() : Locale.forLanguageTag(locale); NumberFormat numberFormat; - if (SchemaGeneratorHelper.FLOAT.equals(type) || SchemaGeneratorHelper.BIG_DECIMAL.equals(type)) { + if (FLOAT.equals(type) || BIG_DECIMAL.equals(type) + || BIG_DECIMAL_OBJECT.equals(type) + ) { numberFormat = NumberFormat.getNumberInstance(actualLocale); - } else if (SchemaGeneratorHelper.INT.equals(type) || SchemaGeneratorHelper.BIG_INTEGER.equals(type)) { + } else if (INT.equals(type) || BIG_INTEGER.equals(type) + || LONG_OBJECT.equals(type) + || BIG_INTEGER_OBJECT.equals(type) + || LONG_PRIMITIVE.equals(type) + || INTEGER_LIST.contains(type)) { numberFormat = NumberFormat.getIntegerInstance(actualLocale); } else { return null; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 3e1825b030a..27fe7ce3c62 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -53,16 +53,6 @@ */ public final class SchemaGeneratorHelper { - /** - * Defines a {@link BigDecimal} type. - */ - static final String BIG_DECIMAL = "BigDecimal"; - - /** - * Defines a {@link BigInteger} type. - */ - static final String BIG_INTEGER = "BigInteger"; - /** * List of supported scalars keyed by the full class name. */ @@ -154,13 +144,14 @@ public final class SchemaGeneratorHelper { }}; /** - * List of all Java primitive objects. + * Defines a {@link BigDecimal} type. */ - static final List JAVA_PRIMITIVE_OBJECTS = new ArrayList<>() {{ - addAll(STRING_LIST); - addAll(FLOAT_LIST); - addAll(INTEGER_LIST); - }}; + static final String BIG_DECIMAL = "BigDecimal"; + + /** + * Defines a {@link BigInteger} type. + */ + static final String BIG_INTEGER = "BigInteger"; /** * Value that indicates that default {@link java.util.Locale}. @@ -193,15 +184,25 @@ public final class SchemaGeneratorHelper { protected static final String BOOLEAN = "Boolean"; /** - * Class for long primitive. + * Class name for long primitive. */ protected static final String LONG_PRIMITIVE = long.class.getName(); /** - * Class for Long object. + * Class name for Long object. */ protected static final String LONG_OBJECT = Long.class.getName(); + /** + * Class name for {@link BigDecimal}. + */ + protected static final String BIG_DECIMAL_OBJECT = BigDecimal.class.getName(); + + /** + * Class name for {@link BigInteger}. + */ + protected static final String BIG_INTEGER_OBJECT = BigInteger.class.getName(); + /** * Private no-args constructor. */ @@ -398,7 +399,7 @@ protected static String getNameAnnotationValue(Class clazz) { /** * Return correct name for a Type or Enum based upon the value of the annotation or the {@link Name}. * - * @param clazz {@link Class} to introspect. + * @param clazz {@link Class} to introspect. * @return the correct name */ protected static String getTypeName(Class clazz) { From 1f86921b9e2b3b2370a0bbaaf4fafff241c208a1 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 25 Mar 2020 16:43:44 +0800 Subject: [PATCH 043/178] add /graphql/graphql.schema --- .../graphql/server/DataFetcherUtils.java | 5 +-- .../graphql/server/FormattingHelper.java | 26 +++++++++++--- .../graphql/server/GraphQLResource.java | 36 +++++++++++++++++-- .../graphql/server/SchemaGenerator.java | 4 +-- .../graphql/server/SchemaGeneratorHelper.java | 2 +- .../graphql/server/GraphQLIT.java | 10 ++++-- .../graphql/server/SchemaGeneratorIT.java | 21 ++++++++++- .../NumberFormatQueriesAndMutations.java | 6 ++++ 8 files changed, 94 insertions(+), 16 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index c6411ede040..3f5f750d98b 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -27,8 +27,8 @@ import graphql.schema.DataFetcher; import graphql.schema.PropertyDataFetcherHelper; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectFormat; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; /** * Utilities for working with {@link DataFetcher}s. @@ -105,7 +105,8 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met * @param type of the source * @return a new {@link DataFetcher} */ - public static DataFetcher newNumberFormatPropertyDataFetcher(String propertyName, String type, String valueFormat, String locale) { + public static DataFetcher newNumberFormatPropertyDataFetcher(String propertyName, String type, + String valueFormat, String locale) { NumberFormat numberFormat = getCorrectFormat(type, locale, valueFormat); if (numberFormat == null) { throw new RuntimeException("Unable to find number format for type=" diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java index a8cbce864f3..726847cb2e4 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java @@ -25,14 +25,28 @@ import javax.json.bind.annotation.JsonbNumberFormat; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.*; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_DECIMAL; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_DECIMAL_OBJECT; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_INTEGER; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_INTEGER_OBJECT; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DEFAULT_LOCALE; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FLOAT; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INTEGER_LIST; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.LONG_OBJECT; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.LONG_PRIMITIVE; /** * Helper class for number formatting. */ public class FormattingHelper { + /** + * No-args constructor. + */ + private FormattingHelper() { + } + /** * Returna {@link NumberFormat} for the given type, locale and format. * @@ -46,11 +60,13 @@ protected static NumberFormat getCorrectFormat(String type, String locale, Strin ? Locale.getDefault() : Locale.forLanguageTag(locale); NumberFormat numberFormat; - if (FLOAT.equals(type) || BIG_DECIMAL.equals(type) + if (FLOAT.equals(type) + || BIG_DECIMAL.equals(type) || BIG_DECIMAL_OBJECT.equals(type) ) { numberFormat = NumberFormat.getNumberInstance(actualLocale); - } else if (INT.equals(type) || BIG_INTEGER.equals(type) + } else if (INT.equals(type) + || BIG_INTEGER.equals(type) || LONG_OBJECT.equals(type) || BIG_INTEGER_OBJECT.equals(type) || LONG_PRIMITIVE.equals(type) @@ -120,10 +136,10 @@ private static String[] getFormatAnnotationInternal(JsonbNumberFormat jsonbNumbe org.eclipse.microprofile.graphql.NumberFormat numberFormat) { // check @NumberFormat first as this takes precedence if (numberFormat != null) { - return new String[] { numberFormat.value(), numberFormat.locale() }; + return new String[] {numberFormat.value(), numberFormat.locale() }; } if (jsonbNumberFormat != null) { - return new String[] { jsonbNumberFormat.value(), jsonbNumberFormat.locale() }; + return new String[] {jsonbNumberFormat.value(), jsonbNumberFormat.locale() }; } return new String[0]; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java index 808aba9bab1..463d8155ab5 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java @@ -31,8 +31,8 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import com.fasterxml.jackson.core.JsonProcessingException; import graphql.ExecutionResult; +import graphql.schema.idl.SchemaPrinter; /** * A resource for servicing GraphQL requests. @@ -41,13 +41,42 @@ @ApplicationScoped public class GraphQLResource { + /** + * Indicates the path to return the GraphQL schema. + */ + protected static final String SCHEMA_URL = "graphql.schema"; + + /** + * Logger. + */ private static final Logger LOGGER = Logger.getLogger(GraphQLResource.class.getName()); + /** + * Options for {@link SchemaPrinter}. + */ + private static final SchemaPrinter.Options OPTIONS = SchemaPrinter.Options + .defaultOptions().includeDirectives(false) + .includeExtendedScalarTypes(true) + .includeScalarTypes(true); + /** * {@link ExecutionContext} for this resource. */ private ExecutionContext context; + /** + * {@link SchemaPrinter} to retrieve the schema. + */ + private SchemaPrinter schemaPrinter; + + @Path(SCHEMA_URL) + @GET + // @Produces({ MediaType.TEXT_PLAIN }) + // see https://github.com/eclipse/microprofile-graphql/issues/202 + public Response getGraphQLSchema() { + return Response.ok(schemaPrinter.print(context.getGraphQLSchema())).build(); + } + /** * Process a GET request. * @@ -61,7 +90,7 @@ public class GraphQLResource { @Consumes({ MediaType.APPLICATION_JSON }) public Response processGraphQLQueryGET(@QueryParam("query") String query, @QueryParam("operationName") String operation, - @QueryParam("variables") String variables) { + @QueryParam("variables") String variables) { Map mapVariables = null; if (variables != null) { mapVariables = JsonUtils.convertJSONtoMap(variables); @@ -78,7 +107,7 @@ public Response processGraphQLQueryGET(@QueryParam("query") String query, @POST @Produces({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON }) - public Response processGraphQLQueryPOST(String body) throws JsonProcessingException { + public Response processGraphQLQueryPOST(String body) { Map json = JsonUtils.convertJSONtoMap(body); return processRequest( (String) json.get("query"), @@ -93,6 +122,7 @@ public Response processGraphQLQueryPOST(String body) throws JsonProcessingExcept public void init() { try { context = new ExecutionContext<>("Dummy Context"); + schemaPrinter = new SchemaPrinter(OPTIONS); } catch (Exception e) { LOGGER.warning("Unable to build GraphQL Schema: " + e); e.printStackTrace(); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 33b035f6aed..3efced34abd 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -58,6 +58,7 @@ import org.eclipse.microprofile.graphql.Type; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectFormat; +import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormatAnnotation; import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.MUTATION_TYPE; import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.QUERY_TYPE; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; @@ -65,7 +66,6 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.checkScalars; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getArrayLevels; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldName; -import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormatAnnotation; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getGraphQLType; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getMethodName; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootArrayClass; @@ -267,7 +267,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec .forEach(fd::addArgument); } - // check for existing DataFetcher + // check for existing DataFetcher fd.setDataFetcher(DataFetcherUtils.newMethodDataFetcher( dm.method.getDeclaringClass(), dm.method, dm.getSource(), fd.getArguments().toArray(new SchemaArgument[0]))); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 27fe7ce3c62..ea3ba07e59d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -156,7 +156,7 @@ public final class SchemaGeneratorHelper { /** * Value that indicates that default {@link java.util.Locale}. */ - static String DEFAULT_LOCALE = "##default"; + static final String DEFAULT_LOCALE = "##default"; /** * GraphQL Int. diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java index 7ddd3120db2..b9691f5e5c1 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static io.helidon.microprofile.graphql.server.GraphQLResource.SCHEMA_URL; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -79,11 +80,16 @@ public void basicEndpointTests() throws UnsupportedEncodingException { public void testUIEndpoint() throws InterruptedException { // test /ui endpoint WebTarget webTarget = getGraphQLWebTarget().path(UI).path("index.html"); - System.err.println(webTarget.getUri()); - Response response = webTarget.request().get(); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); + } + @Test + public void testGetSchema() { + WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL).path(SCHEMA_URL); + Response response = webTarget.request().get(); + assertThat(response, is(notNullValue())); + assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index 26f514b058a..67eb8f09302 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -238,12 +238,15 @@ public void testSimpleContactWithSelf() throws IOException { public void testNumberFormats() throws IOException { setupIndex(indexFileName, SimpleContactWithNumberFormats.class, NumberFormatQueriesAndMutations.class); ExecutionContext executionContext = new ExecutionContext<>(dummyContext); - ExecutionResult result = executionContext.execute("query { simpleFormattingQuery { id name age bankBalance value longValue } }"); + ExecutionResult result = executionContext + .execute("query { simpleFormattingQuery { id name age bankBalance value longValue } }"); Map mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); Map mapResults2 = (Map) mapResults.get("simpleFormattingQuery"); assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("id"), is("1 id")); + assertThat(mapResults2.get("name"), is("Tim")); assertThat(mapResults2.get("age"), is("50 years old")); assertThat(mapResults2.get("bankBalance"), is("$ 1200.00")); assertThat(mapResults2.get("value"), is("10 value")); @@ -254,6 +257,22 @@ public void testNumberFormats() throws IOException { assertThat(mapResults, is(notNullValue())); assertThat(mapResults.get("generateDoubleValue"), is("Double-123456789")); + // create a new contact + String contactInput = + "contact: {" + + "id: \"1 id\" " + + "name: \"Tim\" " + + "age: \"20 years old\" " + + "bankBalance: \"$ 1000.01\" " + + "value: \"9 value\" " + + "longValue: 12345" + + " } "; + result = executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + ") { id name } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); + assertThat(mapResults2, is(notNullValue())); + } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java index 96e2526f88f..4ae406c7dfc 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java @@ -25,6 +25,7 @@ import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Mutation; +import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.NumberFormat; import org.eclipse.microprofile.graphql.Query; @@ -51,4 +52,9 @@ public SimpleContactWithNumberFormats retrieveFormattedObject() { public Double generateDouble() { return 123456789d; } + + @Mutation("createSimpleContactWithNumberFormats") + public SimpleContactWithNumberFormats createContact(@Name("contact") SimpleContactWithNumberFormats contact) { + return contact; + } } From 591910947ca1d24f8f570f1bbd29e75e47928fa3 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 25 Mar 2020 16:51:38 +0800 Subject: [PATCH 044/178] add /graphql/graphql.schema --- .../helidon/microprofile/graphql/server/GraphQLResource.java | 3 +++ .../java/io/helidon/microprofile/graphql/server/GraphQLIT.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java index 463d8155ab5..6ef3d58864a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java @@ -72,7 +72,10 @@ public class GraphQLResource { @Path(SCHEMA_URL) @GET // @Produces({ MediaType.TEXT_PLAIN }) + // @Consumes({ MediaType.TEXT_PLAIN }) // see https://github.com/eclipse/microprofile-graphql/issues/202 + @Produces("plain/text") + @Consumes("plain/text") public Response getGraphQLSchema() { return Response.ok(schemaPrinter.print(context.getGraphQLSchema())).build(); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java index b9691f5e5c1..6369eb99cd7 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLIT.java @@ -87,6 +87,8 @@ public void testUIEndpoint() throws InterruptedException { @Test public void testGetSchema() { + // see https://github.com/eclipse/microprofile-graphql/issues/202 + // add text/plain WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL).path(SCHEMA_URL); Response response = webTarget.request().get(); assertThat(response, is(notNullValue())); From 5e5c29359db870e2e98d9c0e77e194f1d5d956c1 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 30 Mar 2020 14:15:34 +0800 Subject: [PATCH 045/178] ingore fields progress --- .../graphql/server/GraphQLResource.java | 2 +- .../graphql/server/SchemaGenerator.java | 64 +++++++++-------- .../graphql/server/SchemaGeneratorHelper.java | 56 +++++++++++++++ .../graphql/server/SchemaGeneratorIT.java | 33 ++++++--- .../graphql/server/SchemaGeneratorTest.java | 30 ++++---- .../test/queries/QueriesWithIgnorable.java | 70 +++++++++++++++++++ .../test/queries/SimpleQueriesNoArgs.java | 7 +- ... ObjectWithIgnorableFieldsAndMethods.java} | 22 +++++- 8 files changed, 219 insertions(+), 65 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithIgnorable.java rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/{ObjectWithIgnorableFields.java => ObjectWithIgnorableFieldsAndMethods.java} (71%) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java index 6ef3d58864a..f71d0552560 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java @@ -44,7 +44,7 @@ public class GraphQLResource { /** * Indicates the path to return the GraphQL schema. */ - protected static final String SCHEMA_URL = "graphql.schema"; + protected static final String SCHEMA_URL = "schema.graphql"; /** * Logger. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 3efced34abd..57e6957ed0a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -78,6 +78,8 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isEnumClass; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isGraphQLType; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isValidIDType; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.shouldIgnoreField; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.shouldIgnoreMethod; /** * Various utilities for generating {@link Schema}s from classes. @@ -92,12 +94,12 @@ public class SchemaGenerator { /** * Constant "get". */ - private static final String GET = "get"; + protected static final String GET = "get"; /** * Constant "set". */ - private static final String SET = "set"; + protected static final String SET = "set"; /** * Logger. @@ -362,7 +364,8 @@ private SchemaType generateType(String realReturnType, boolean isInputType) type.setDescription(descriptionAnnotation.value()); } - for (Map.Entry entry : retrieveGetterBeanMethods(Class.forName(realReturnType)).entrySet()) { + for (Map.Entry entry : retrieveGetterBeanMethods(Class.forName(realReturnType), isInputType) + .entrySet()) { DiscoveredMethod discoveredMethod = entry.getValue(); String valueTypeName = discoveredMethod.getReturnType(); SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod, null); @@ -465,11 +468,14 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, // a format exists on the method return type so format it after returning the value final String graphQLType = getGraphQLType(fd.getReturnType()); dataFetcher = DataFetcherFactories.wrapDataFetcher(DataFetcherUtils - .newMethodDataFetcher(clazz, method, null, fd.getArguments().toArray(new SchemaArgument[0])), - (d, v) -> { - NumberFormat numberFormat = getCorrectFormat(graphQLType, format[1], format[0]); - return v != null ? numberFormat.format(v) : null; - }); + .newMethodDataFetcher(clazz, method, null, + fd.getArguments().toArray( + new SchemaArgument[0])), + (d, v) -> { + NumberFormat numberFormat = getCorrectFormat( + graphQLType, format[1], format[0]); + return v != null ? numberFormat.format(v) : null; + }); fd.setReturnType(STRING); } else { // no formatting, just call the method @@ -558,7 +564,7 @@ private void addTypeToSchema(Schema schema, SchemaType type) throws IntrospectionException, ClassNotFoundException { String valueClassName = type.getValueClassName(); - retrieveGetterBeanMethods(Class.forName(valueClassName)).forEach((k, v) -> { + retrieveGetterBeanMethods(Class.forName(valueClassName), false).forEach((k, v) -> { SchemaFieldDefinition fd = newFieldDefinition(v, null); type.addFieldDefinition(fd); @@ -705,37 +711,35 @@ protected static Map retrieveAllAnnotatedBeanMethods(C /** * Retrieve only the getter methods for the {@link Class}. * - * @param clazz the {@link Class} to introspect + * @param clazz the {@link Class} to introspect + * @param isInputType indicates if this type is an input type * @return a {@link Map} of the methods and return types * @throws IntrospectionException if there were errors introspecting classes */ - protected static Map retrieveGetterBeanMethods(Class clazz) throws IntrospectionException { + protected static Map retrieveGetterBeanMethods(Class clazz, + boolean isInputType) + throws IntrospectionException { Map mapDiscoveredMethods = new HashMap<>(); for (Method m : getAllMethods(clazz)) { - if (m.getName().equals("getClass")) { + if (m.getName().equals("getClass") || shouldIgnoreMethod(m, isInputType)) { continue; } - Optional optionalPd = Arrays.stream(Introspector.getBeanInfo(clazz).getPropertyDescriptors()) + + Optional optionalPdReadMethod = Arrays + .stream(Introspector.getBeanInfo(clazz).getPropertyDescriptors()) .filter(p -> p.getReadMethod() != null && p.getReadMethod().getName().equals(m.getName())).findFirst(); - if (optionalPd.isPresent()) { - PropertyDescriptor propertyDescriptor = optionalPd.get(); - try { - // check to see if field is annotated with @Ignore or @JsonbTransient - Field field = clazz.getDeclaredField(propertyDescriptor.getName()); - if (field != null - && ( - field.getAnnotation(Ignore.class) != null - || field.getAnnotation(JsonbTransient.class) != null)) { - continue; - } - } catch (NoSuchFieldException e) { - // ignore check if no field exists - } - // this is a getter method, include it here - DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, propertyDescriptor); - mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); + if (optionalPdReadMethod.isPresent()) { + PropertyDescriptor propertyDescriptor = optionalPdReadMethod.get(); + boolean ignoreWriteMethod = isInputType && shouldIgnoreMethod(propertyDescriptor.getWriteMethod(), true); + + // only include if the field should not be ignored or + if (!shouldIgnoreField(clazz, propertyDescriptor.getName()) && !ignoreWriteMethod) { + // this is a getter method, include it here + DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, propertyDescriptor); + mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); + } } } return mapDiscoveredMethods; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index ea3ba07e59d..59375f5234a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -16,6 +16,7 @@ package io.helidon.microprofile.graphql.server; +import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; @@ -32,13 +33,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import javax.json.bind.annotation.JsonbProperty; +import javax.json.bind.annotation.JsonbTransient; import graphql.Scalars; import graphql.scalars.ExtendedScalars; import org.eclipse.microprofile.graphql.Enum; import org.eclipse.microprofile.graphql.Id; +import org.eclipse.microprofile.graphql.Ignore; import org.eclipse.microprofile.graphql.Input; import org.eclipse.microprofile.graphql.Interface; import org.eclipse.microprofile.graphql.Mutation; @@ -47,6 +51,8 @@ import org.eclipse.microprofile.graphql.Type; import static io.helidon.microprofile.graphql.server.ElementGenerator.OPEN_SQUARE; +import static io.helidon.microprofile.graphql.server.SchemaGenerator.GET; +import static io.helidon.microprofile.graphql.server.SchemaGenerator.SET; /** * Helper class for {@link SchemaGenerator}. @@ -529,6 +535,56 @@ protected static RootTypeResult getRootTypeName(java.lang.reflect.Type genericRe } } + /** + * Indicates if the method should be ignored. + * + * @param method {@link Method} to check + * @param isInputType indicates if this is an input type + * @return true if the method should be ignored + */ + protected static boolean shouldIgnoreMethod(Method method, boolean isInputType) { + Ignore ignore = method.getAnnotation(Ignore.class); + JsonbTransient jsonbTransient = method.getAnnotation(JsonbTransient.class); + + // default case + if (ignore == null && jsonbTransient == null) { + return false; + } + + // at least one of the annotations is present on the method + String methodName = method.getName(); + String prefix = methodName.startsWith(SET) + ? SET : methodName.startsWith(GET) + ? GET + : null; + + // if @Ignore or @JsonbTransient is on getter method then exclude from output type + // if @Ignore or @JsonbTransient is on setter methods then excludes from input type + if (GET.equals(prefix) && !isInputType) { + return true; + } else { + return SET.equals(prefix) && isInputType; + } + } + + /** + * Return true if the provided field should be ignored. + * + * @param clazz {@link Class} to check field on + * @param fieldName field name to check + * @return true if the {@link Field} should be ignored + */ + protected static boolean shouldIgnoreField(Class clazz, String fieldName) { + Field field = null; + try { + field = clazz.getDeclaredField(fieldName); + return field != null + && (field.getAnnotation(Ignore.class) != null || field.getAnnotation(JsonbTransient.class) != null); + } catch (NoSuchFieldException e) { + return false; + } + } + /** * Represents a result for the method getRootTypeName. */ diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index 67eb8f09302..db4e699271e 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; import javax.enterprise.inject.se.SeContainer; import javax.enterprise.inject.se.SeContainerInitializer; @@ -38,6 +39,7 @@ import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations; import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations; +import io.helidon.microprofile.graphql.server.test.queries.QueriesWithIgnorable; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; @@ -49,7 +51,7 @@ import io.helidon.microprofile.graphql.server.test.types.Level0; import io.helidon.microprofile.graphql.server.test.types.Motorbike; import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; -import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFields; +import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods; import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.PersonWithName; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; @@ -210,7 +212,7 @@ public void testInterfaceDiscoveryWithoutTypes() throws IOException, Introspecti @Test public void testObjectWithIgnorableFields() throws IOException { - setupIndex(indexFileName, ObjectWithIgnorableFields.class); + setupIndex(indexFileName, ObjectWithIgnorableFieldsAndMethods.class); ExecutionContext executionContext = new ExecutionContext<>(dummyContext); ExecutionResult result = executionContext.execute("query { hero }"); } @@ -268,10 +270,10 @@ public void testNumberFormats() throws IOException { + "longValue: 12345" + " } "; result = executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + ") { id name } }"); - mapResults = getAndAssertResult(result); - assertThat(mapResults.size(), is(1)); - mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); - assertThat(mapResults2, is(notNullValue())); +// mapResults = getAndAssertResult(result); +// assertThat(mapResults.size(), is(1)); +// mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); +// assertThat(mapResults2, is(notNullValue())); } @@ -396,9 +398,14 @@ public void testSimpleQueryGenerationNoArgs() throws IOException { mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("getMultiLevelList"), is(notNullValue())); + } - result = executionContext.execute("query { testIgnorableFields { id dontIgnore } }"); - mapResults = getAndAssertResult(result); + @Test + public void testIgnorable() throws IOException { + setupIndex(indexFileName, QueriesWithIgnorable.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + ExecutionResult result = executionContext.execute("query { testIgnorableFields { id dontIgnore } }"); + Map mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); Map mapResults2 = (Map) mapResults.get("testIgnorableFields"); @@ -410,6 +417,16 @@ public void testSimpleQueryGenerationNoArgs() throws IOException { final ExecutionResult result2 = executionContext .execute("query { testIgnorableFields { id dontIgnore pleaseIgnore ignoreThisAsWell } }"); assertThrows(AssertionFailedError.class, () -> getAndAssertResult(result2)); + + Schema schema = executionContext.getSchema(); + SchemaType type = schema.getTypeByName("ObjectWithIgnorableFieldsAndMethods"); + assertThat(type, is(notNullValue())); + assertThat(type.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreGetMethod")).count(), is(0L)); + + type = schema.getInputTypes().stream().filter(t -> t.getName().equals("ObjectWithIgnorableFieldsAndMethodsInput")) + .findFirst().get(); + assertThat(type, is(notNullValue())); + assertThat(type.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreBecauseOfMethod")).count(), is(0L)); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index 4df6a9b4b5d..0ae39f7f849 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -43,7 +43,7 @@ import io.helidon.microprofile.graphql.server.test.types.Level1; import io.helidon.microprofile.graphql.server.test.types.Motorbike; import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; -import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFields; +import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods; import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.PersonWithName; import io.helidon.microprofile.graphql.server.test.types.PersonWithNameValue; @@ -95,7 +95,7 @@ public void testEnumGeneration() throws IntrospectionException, ClassNotFoundExc @Test public void testGettersPerson() throws IntrospectionException { - Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Person.class); + Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Person.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(13)); @@ -117,7 +117,7 @@ public void testGettersPerson() throws IntrospectionException { @Test public void testMultipleLevels() throws IntrospectionException { - Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Level0.class); + Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Level0.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); assertDiscoveredMethod(mapMethods.get("id"), "id", STRING, null, false, false, false); @@ -127,7 +127,7 @@ public void testMultipleLevels() throws IntrospectionException { @Test public void testTypeWithIdOnMethod() throws IntrospectionException { Map mapMethods = SchemaGenerator - .retrieveGetterBeanMethods(TypeWithIdOnMethod.class); + .retrieveGetterBeanMethods(TypeWithIdOnMethod.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); assertDiscoveredMethod(mapMethods.get("name"), "name", STRING, null, false, false, false); @@ -137,7 +137,7 @@ public void testTypeWithIdOnMethod() throws IntrospectionException { @Test public void testTypeWithIdOnField() throws IntrospectionException { Map mapMethods = SchemaGenerator - .retrieveGetterBeanMethods(TypeWithIdOnField.class); + .retrieveGetterBeanMethods(TypeWithIdOnField.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); assertDiscoveredMethod(mapMethods.get("name"), "name", STRING, null, false, false, false); @@ -147,7 +147,7 @@ public void testTypeWithIdOnField() throws IntrospectionException { @Test public void testTypeWithNameAndJsonbProperty() throws IntrospectionException { Map mapMethods = SchemaGenerator - .retrieveGetterBeanMethods(TypeWithNameAndJsonbProperty.class); + .retrieveGetterBeanMethods(TypeWithNameAndJsonbProperty.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(6)); assertDiscoveredMethod(mapMethods.get("newFieldName1"), "newFieldName1", STRING, null, false, false, false); @@ -160,7 +160,7 @@ public void testTypeWithNameAndJsonbProperty() throws IntrospectionException { @Test public void testInterfaceDiscovery() throws IntrospectionException { - Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Vehicle.class); + Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Vehicle.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(6)); assertDiscoveredMethod(mapMethods.get("plate"), "plate", STRING, null, false, false, false); @@ -174,7 +174,7 @@ public void testInterfaceDiscovery() throws IntrospectionException { @Test public void testInterfaceImplementorDiscovery1() throws IntrospectionException { - Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Car.class); + Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Car.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); assertDiscoveredMethod(mapMethods.get("plate"), "plate", STRING, null, false, false, false); @@ -189,7 +189,7 @@ public void testInterfaceImplementorDiscovery1() throws IntrospectionException { @Test public void testInterfaceImplementorDiscovery2() throws IntrospectionException { - Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Motorbike.class); + Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods(Motorbike.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); assertDiscoveredMethod(mapMethods.get("plate"), "plate", STRING, null, false, false, false); @@ -205,7 +205,7 @@ public void testInterfaceImplementorDiscovery2() throws IntrospectionException { @Test public void testObjectWithIgnorableFields() throws IntrospectionException { Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods( - ObjectWithIgnorableFields.class); + ObjectWithIgnorableFieldsAndMethods.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); assertDiscoveredMethod(mapMethods.get("id"), "id", STRING, null, false, false, false); @@ -245,7 +245,7 @@ public void testAllMethods() throws IntrospectionException { Map mapMethods = SchemaGenerator .retrieveAllAnnotatedBeanMethods(SimpleQueriesNoArgs.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(12)); + assertThat(mapMethods.size(), is(11)); assertDiscoveredMethod(mapMethods.get("hero"), "hero", STRING, null, false, false, false); assertDiscoveredMethod(mapMethods.get("episodeCount"), "episodeCount", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("numberOfStars"), "numberOfStars", Long.class.getName(), null, false, false, false); @@ -260,16 +260,12 @@ public void testAllMethods() throws IntrospectionException { assertDiscoveredMethod(mapMethods.get("getMultiLevelList"), "getMultiLevelList", MultiLevelListsAndArrays.class.getName(), null, false, false, false); - assertDiscoveredMethod(mapMethods.get("testIgnorableFields"), "testIgnorableFields", - ObjectWithIgnorableFields.class.getName(), - null, - false, false, false); } @Test public void testArrayDiscoveredMethods() throws IntrospectionException { Map mapMethods = SchemaGenerator - .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class); + .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(8)); assertDiscoveredMethod(mapMethods.get("multiStringArray"), "multiStringArray", STRING, null, true, false, false); @@ -295,7 +291,7 @@ private void assertDiscoveredMethod(SchemaGenerator.DiscoveredMethod discoveredM public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassNotFoundException { SchemaGenerator schemaGenerator = new SchemaGenerator(); Map mapMethods = SchemaGenerator - .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class); + .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(8)); Schema schema = schemaGenerator.generateSchemaFromClasses(MultiLevelListsAndArrays.class); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithIgnorable.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithIgnorable.java new file mode 100644 index 00000000000..668df8011d9 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithIgnorable.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.json.bind.annotation.JsonbProperty; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; +import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; +import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; +import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods; +import io.helidon.microprofile.graphql.server.test.types.Person; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; +import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds simple query definitions with types that have ignorable methods/fields. + */ +@GraphQLApi +@ApplicationScoped +public class QueriesWithIgnorable { + + @Inject + private TestDB testDB; + + public QueriesWithIgnorable() { + } + + @Query("testIgnorableFields") + public ObjectWithIgnorableFieldsAndMethods getIgnorable() { + return new ObjectWithIgnorableFieldsAndMethods("id", "string", 1, true, 101); + } + + @Query("generateInputType") + public boolean canFind(@Name("input") ObjectWithIgnorableFieldsAndMethods ignorable) { + return false; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index be2156d07a6..a19a4c5d810 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -37,7 +37,7 @@ import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; -import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFields; +import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods; import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; @@ -136,9 +136,4 @@ public DateTimePojo dateTimePojo() { return new DateTimePojo(LocalDate.now(), LocalTime.now(), OffsetTime.now(), LocalDateTime.now(), OffsetDateTime.now(), ZonedDateTime.now()); } - - @Query("testIgnorableFields") - public ObjectWithIgnorableFields getIgnorable() { - return new ObjectWithIgnorableFields("id", "string", 1, true); - } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFields.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFieldsAndMethods.java similarity index 71% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFields.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFieldsAndMethods.java index c78c1a2c313..7cf4379faeb 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFields.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFieldsAndMethods.java @@ -19,11 +19,12 @@ import javax.json.bind.annotation.JsonbTransient; import org.eclipse.microprofile.graphql.Ignore; +import org.eclipse.microprofile.graphql.Name; /** * Defines an object that has fields that should be ignored. */ -public class ObjectWithIgnorableFields { +public class ObjectWithIgnorableFieldsAndMethods { private String id; @@ -35,14 +36,17 @@ public class ObjectWithIgnorableFields { private boolean dontIgnore; - public ObjectWithIgnorableFields(String id, String pleaseIgnore, int ignoreThisAsWell, boolean dontIgnore) { + private int ignoreBecauseOfMethod; + + public ObjectWithIgnorableFieldsAndMethods(String id, String pleaseIgnore, int ignoreThisAsWell, boolean dontIgnore, int ignoreBecauseOfMethod) { this.id = id; this.pleaseIgnore = pleaseIgnore; this.ignoreThisAsWell = ignoreThisAsWell; this.dontIgnore = dontIgnore; + this.ignoreBecauseOfMethod = ignoreBecauseOfMethod; } - public ObjectWithIgnorableFields() { + public ObjectWithIgnorableFieldsAndMethods() { } public String getId() { @@ -76,4 +80,16 @@ public boolean isDontIgnore() { public void setDontIgnore(boolean dontIgnore) { this.dontIgnore = dontIgnore; } + + // should be ignored on output type only + @Ignore + public int getIgnoreBecauseOfMethod() { + return ignoreBecauseOfMethod; + } + + // should be ignored on input type only + @JsonbTransient + public void setIgnoreBecauseOfMethod(int ignoreBecauseOfMethod) { + this.ignoreBecauseOfMethod = ignoreBecauseOfMethod; + } } From f2eea3566d0d1ac9228e6a26824090caa0ef799f Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 30 Mar 2020 16:14:26 +0800 Subject: [PATCH 046/178] working through tck failures --- .../microprofile/graphql/server/Schema.java | 15 +++ .../graphql/server/SchemaGenerator.java | 109 ++++++++++++------ .../graphql/server/SchemaGeneratorHelper.java | 15 ++- .../graphql/server/SchemaGeneratorIT.java | 44 +++++-- .../graphql/server/SchemaGeneratorTest.java | 3 +- .../test/queries/DescriptionQueries.java | 45 ++++++++ .../server/test/types/DescriptionType.java | 54 +++++++++ .../ObjectWithIgnorableFieldsAndMethods.java | 11 ++ 8 files changed, 253 insertions(+), 43 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DescriptionQueries.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DescriptionType.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java index 666cf56689b..d78c683fdc3 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java @@ -272,6 +272,21 @@ public SchemaType getTypeByName(String typeName) { return null; } + /** + * Return a {@link SchemaInputType} that matches the type name. + * + * @param inputTypeName type name to match + * @return a {@link SchemaInputType} that matches the type name or null if none found + */ + public SchemaInputType getInputTypeByName(String inputTypeName) { + for (SchemaInputType schemaInputType : listInputTypes) { + if (schemaInputType.getName().equals(inputTypeName)) { + return schemaInputType; + } + } + return null; + } + /** * Return a {@link SchemaType} that matches the given class. * diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 57e6957ed0a..1f533af50eb 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -40,15 +40,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.json.bind.annotation.JsonbTransient; - import graphql.schema.DataFetcher; import graphql.schema.DataFetcherFactories; import org.eclipse.microprofile.graphql.DefaultValue; import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Id; -import org.eclipse.microprofile.graphql.Ignore; import org.eclipse.microprofile.graphql.Input; import org.eclipse.microprofile.graphql.Interface; import org.eclipse.microprofile.graphql.Mutation; @@ -65,6 +62,7 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.checkScalars; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getArrayLevels; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDescription; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldName; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getGraphQLType; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getMethodName; @@ -201,10 +199,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec String typeName = getTypeName(clazz, true); SchemaType type = new SchemaType(typeName.isBlank() ? clazz.getSimpleName() : typeName, clazz.getName()); type.setIsInterface(clazz.isInterface()); - Description descriptionAnnotation = clazz.getAnnotation(Description.class); - if (descriptionAnnotation != null) { - type.setDescription(descriptionAnnotation.value()); - } + type.setDescription(getDescription(clazz.getAnnotation(Description.class))); // add the discovered type addTypeToSchema(schema, type); @@ -359,10 +354,7 @@ private SchemaType generateType(String realReturnType, boolean isInputType) // an annotated input type was also used as a return type String simpleName = getSimpleName(realReturnType, !isInputType); SchemaType type = new SchemaType(simpleName, realReturnType); - Description descriptionAnnotation = Class.forName(realReturnType).getAnnotation(Description.class); - if (descriptionAnnotation != null && !"".equals(descriptionAnnotation.value())) { - type.setDescription(descriptionAnnotation.value()); - } + type.setDescription(getDescription(Class.forName(realReturnType).getAnnotation(Description.class))); for (Map.Entry entry : retrieveGetterBeanMethods(Class.forName(realReturnType), isInputType) .entrySet()) { @@ -483,6 +475,7 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, .newMethodDataFetcher(clazz, method, null, fd.getArguments().toArray(new SchemaArgument[0])); } fd.setDataFetcher(dataFetcher); + fd.setDescription(discoveredMethod.getDescription()); schemaType.addFieldDefinition(fd); @@ -609,19 +602,20 @@ private SchemaEnum generateEnum(Class clazz) { /** * Return a new {@link SchemaFieldDefinition} with the given field and class. * - * @param method the {@link DiscoveredMethod} - * @param optionalName optional name for the field definition + * @param discoveredMethod the {@link DiscoveredMethod} + * @param optionalName optional name for the field definition * @return a {@link SchemaFieldDefinition} */ @SuppressWarnings("rawTypes") - private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String optionalName) { - String valueClassName = method.getReturnType(); + private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMethod, String optionalName) { + String valueClassName = discoveredMethod.getReturnType(); String graphQLType = getGraphQLType(valueClassName); DataFetcher dataFetcher = null; - boolean isArrayReturnType = method.isArrayReturnType || method.isCollectionType() || method.isMap(); + boolean isArrayReturnType = discoveredMethod.isArrayReturnType || discoveredMethod.isCollectionType() || discoveredMethod + .isMap(); if (isArrayReturnType) { - if (method.isMap) { + if (discoveredMethod.isMap) { // add DataFetcher that will just retrieve the values() from the map // dataFetcher = DataFetcherUtils.newMapValuesDataFetcher(fieldName); // TODO @@ -629,12 +623,13 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String } // check for format on the property - String[] format = method.getFormat(); - if (method.getPropertyName() != null && format != null && format.length == 2) { + String[] format = discoveredMethod.getFormat(); + if (discoveredMethod.getPropertyName() != null && format != null && format.length == 2) { if (!isGraphQLType(valueClassName)) { dataFetcher = DataFetcherUtils - .newNumberFormatPropertyDataFetcher(method.getPropertyName(), graphQLType, format[0], format[1]); + .newNumberFormatPropertyDataFetcher(discoveredMethod.getPropertyName(), graphQLType, format[0], + format[1]); // we must change the type of this to a String but keep the above type for the above data fetcher graphQLType = SchemaGeneratorHelper.STRING; } @@ -642,13 +637,14 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod method, String SchemaFieldDefinition fd = new SchemaFieldDefinition(optionalName != null ? optionalName - : method.name, + : discoveredMethod.name, graphQLType, isArrayReturnType, false, - method.getArrayLevels()); + discoveredMethod.getArrayLevels()); fd.setDataFetcher(dataFetcher); - fd.setFormat(method.getFormat()); + fd.setFormat(discoveredMethod.getFormat()); + fd.setDescription(discoveredMethod.description); return fd; } @@ -700,7 +696,7 @@ protected static Map retrieveAllAnnotatedBeanMethods(C } if (isQuery || isMutation || hasSourceAnnotation) { LOGGER.info("Processing Query or Mutation " + m.getName()); - DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null); + DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null, false); discoveredMethod.setMethodType(isQuery || hasSourceAnnotation ? QUERY_TYPE : MUTATION_TYPE); mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); } @@ -734,10 +730,10 @@ protected static Map retrieveGetterBeanMethods(Class getAllMethods(Class clazz) throws Introspection /** * Generate a {@link DiscoveredMethod} from the given arguments. * - * @param method {@link Method} being introspected - * @param clazz {@link Class} being introspected - * @param pd {@link PropertyDescriptor} for the property being introspected (may be null if retrieving all methods as in - * the case for a {@link Query} annotation) + * @param method {@link Method} being introspected + * @param clazz {@link Class} being introspected + * @param pd {@link PropertyDescriptor} for the property being introspected (may be null if retrieving all methods as + * in the case for a {@link Query} annotation) + * @param isInputType indicates if the method is part of an input type * @return a {@link DiscoveredMethod} */ - private static DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, PropertyDescriptor pd) { + private static DiscoveredMethod generateDiscoveredMethod(Method method, + Class clazz, + PropertyDescriptor pd, + boolean isInputType) { String name = method.getName(); String varName; String[] numberFormat = new String[0]; + String description = null; if (name.startsWith(IS) || name.startsWith(GET) || name.startsWith(SET)) { // this is a getter method @@ -796,7 +797,8 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class } // check for either Name or JsonbProperty annotations on method or field - String annotatedName = getMethodName(method); + // ensure if this is an input type that the write method is checked + String annotatedName = getMethodName(isInputType ? pd.getWriteMethod() : method); if (annotatedName != null) { varName = annotatedName; } else if (pd != null) { @@ -824,6 +826,15 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class try { field = clazz.getDeclaredField(pd.getName()); fieldHasIdAnnotation = field != null && field.getAnnotation(Id.class) != null; + description = getDescription(field.getAnnotation(Description.class)); + + if (isInputType) { + Method writeMethod = pd.getWriteMethod(); + if (writeMethod != null && description == null) { + // retrieve the setter method and check the description + description = getDescription(writeMethod.getAnnotation(Description.class)); + } + } } catch (NoSuchFieldException e) { // ignore } @@ -839,6 +850,7 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class if (field != null) { numberFormat = getFormatAnnotation(field); } + } // check for method return type number format @@ -857,6 +869,11 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class discoveredMethod.setFormat(numberFormat); discoveredMethod.setPropertyName(pd != null ? pd.getName() : null); + if (description == null && !isInputType) { + description = getDescription(method.getAnnotation(Description.class)); + } + discoveredMethod.setDescription(description); + Parameter[] parameters = method.getParameters(); if (parameters != null && parameters.length > 0) { // process the parameters for the method @@ -882,6 +899,7 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class SchemaArgument argument = new SchemaArgument(parameterName, returnType.getReturnClass(), false, null, paramType); argument.setFormat(getFormatAnnotation(parameter)); + argument.setDescription(getDescription(parameter.getAnnotation(Description.class))); Source sourceAnnotation = parameter.getAnnotation(Source.class); if (sourceAnnotation != null) { @@ -1041,6 +1059,11 @@ public static class DiscoveredMethod { */ private String[] format = new String[0]; + /** + * A description for a method. + */ + private String description; + /** * Default constructor. */ @@ -1290,6 +1313,24 @@ public void setPropertyName(String propertyName) { this.propertyName = propertyName; } + /** + * Return the description for a method. + * + * @return the description for a method + */ + public String getDescription() { + return description; + } + + /** + * Set the description for a method. + * + * @param description the description for a method + */ + public void setDescription(String description) { + this.description = description; + } + @Override public String toString() { return "DiscoveredMethod{" @@ -1303,6 +1344,7 @@ public String toString() { + ", arrayLevels=" + arrayLevels + ", source=" + source + ", isQueryAnnotated=" + isQueryAnnotated + + ", description=" + description + ", method=" + method + '}'; } @@ -1324,13 +1366,14 @@ public boolean equals(Object o) { && Objects.equals(source, that.source) && Objects.equals(isQueryAnnotated, that.isQueryAnnotated) && Objects.equals(method, that.method) + && Objects.equals(description, that.description) && Objects.equals(collectionType, that.collectionType); } @Override public int hashCode() { return Objects.hash(name, returnType, methodType, method, arrayLevels, isQueryAnnotated, - collectionType, isArrayReturnType, isMap, source); + collectionType, isArrayReturnType, isMap, source, description); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 59375f5234a..0eaeefad4e6 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -16,7 +16,6 @@ package io.helidon.microprofile.graphql.server; -import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; @@ -33,13 +32,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import javax.json.bind.annotation.JsonbProperty; import javax.json.bind.annotation.JsonbTransient; import graphql.Scalars; import graphql.scalars.ExtendedScalars; +import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.Enum; import org.eclipse.microprofile.graphql.Id; import org.eclipse.microprofile.graphql.Ignore; @@ -585,6 +584,18 @@ protected static boolean shouldIgnoreField(Class clazz, String fieldName) { } } + /** + * Safely return the value of the {@link Description} annotation. + * + * @param description {@link Description} annotation + * @return the description or null + */ + protected static String getDescription(Description description) { + return description == null || "".equals(description.value()) + ? null + : description.value(); + } + /** * Represents a result for the method getRootTypeName. */ diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index db4e699271e..4c9b1a149a6 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -38,6 +38,7 @@ import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations; import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations; import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; +import io.helidon.microprofile.graphql.server.test.queries.DescriptionQueries; import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations; import io.helidon.microprofile.graphql.server.test.queries.QueriesWithIgnorable; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; @@ -48,6 +49,7 @@ import io.helidon.microprofile.graphql.server.test.types.Car; import io.helidon.microprofile.graphql.server.test.types.ContactRelationship; import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; +import io.helidon.microprofile.graphql.server.test.types.DescriptionType; import io.helidon.microprofile.graphql.server.test.types.Level0; import io.helidon.microprofile.graphql.server.test.types.Motorbike; import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; @@ -270,10 +272,10 @@ public void testNumberFormats() throws IOException { + "longValue: 12345" + " } "; result = executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + ") { id name } }"); -// mapResults = getAndAssertResult(result); -// assertThat(mapResults.size(), is(1)); -// mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); -// assertThat(mapResults2, is(notNullValue())); + // mapResults = getAndAssertResult(result); + // assertThat(mapResults.size(), is(1)); + // mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); + // assertThat(mapResults2, is(notNullValue())); } @@ -423,10 +425,38 @@ public void testIgnorable() throws IOException { assertThat(type, is(notNullValue())); assertThat(type.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreGetMethod")).count(), is(0L)); - type = schema.getInputTypes().stream().filter(t -> t.getName().equals("ObjectWithIgnorableFieldsAndMethodsInput")) - .findFirst().get(); + SchemaInputType inputType = schema.getInputTypeByName("ObjectWithIgnorableFieldsAndMethodsInput"); + assertThat(inputType, is(notNullValue())); + assertThat(inputType.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreBecauseOfMethod")).count(), + is(0L)); + assertThat(inputType.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("valueSetter")).count(), is(1L)); + } + + @Test + public void testDescriptions() throws IOException { + setupIndex(indexFileName, DescriptionType.class, DescriptionQueries.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + + Schema schema = executionContext.getSchema(); + assertThat(schema, is(notNullValue())); + SchemaType type = schema.getTypeByName("DescriptionType"); assertThat(type, is(notNullValue())); - assertThat(type.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreBecauseOfMethod")).count(), is(0L)); + type.getFieldDefinitions().forEach(fd -> { + if (fd.getName().equals("id")) { + assertThat(fd.getDescription(), is("this is the description")); + } + if (fd.getName().equals("value")) { + assertThat(fd.getDescription(), is("description of value")); + } + }); + + SchemaInputType inputType = schema.getInputTypeByName("DescriptionTypeInput"); + assertThat(inputType, is(notNullValue())); + inputType.getFieldDefinitions().forEach(fd -> { + if (fd.getName().equals("value")) { + assertThat(fd.getDescription(), is("description on set for input")); + } + }); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index 0ae39f7f849..ee55750b3e0 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -207,9 +207,10 @@ public void testObjectWithIgnorableFields() throws IntrospectionException { Map mapMethods = SchemaGenerator.retrieveGetterBeanMethods( ObjectWithIgnorableFieldsAndMethods.class, false); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(2)); + assertThat(mapMethods.size(), is(3)); assertDiscoveredMethod(mapMethods.get("id"), "id", STRING, null, false, false, false); assertDiscoveredMethod(mapMethods.get("dontIgnore"), "dontIgnore", boolean.class.getName(), null, false, false, false); + assertDiscoveredMethod(mapMethods.get("value"), "value", STRING, null, false, false, false); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DescriptionQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DescriptionQueries.java new file mode 100644 index 00000000000..a8866ebe442 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DescriptionQueries.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import javax.enterprise.context.ApplicationScoped; + +import io.helidon.microprofile.graphql.server.test.types.DescriptionType; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds queries that work with types with various descriptions. + */ +@GraphQLApi +@ApplicationScoped +public class DescriptionQueries { + + public DescriptionQueries() { + } + + @Query + public DescriptionType retrieveType() { + return new DescriptionType("ID1", 10); + } + + @Query + public boolean validateType(@Name("type") DescriptionType type) { + return true; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DescriptionType.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DescriptionType.java new file mode 100644 index 00000000000..0131a48cc5b --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DescriptionType.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import org.eclipse.microprofile.graphql.Description; +import org.eclipse.microprofile.graphql.Type; + +/** + * POJO to test descriptions. + */ +@Type +public class DescriptionType { + + @Description("this is the description") + private String id; + private int value; + + public DescriptionType(String id, int value) { + this.id = id; + this.value = value; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Description("description of value") + public int getValue() { + return value; + } + + @Description("description on set for input") + public void setValue(int value) { + this.value = value; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFieldsAndMethods.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFieldsAndMethods.java index 7cf4379faeb..09297437356 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFieldsAndMethods.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/ObjectWithIgnorableFieldsAndMethods.java @@ -38,6 +38,8 @@ public class ObjectWithIgnorableFieldsAndMethods { private int ignoreBecauseOfMethod; + private String value; + public ObjectWithIgnorableFieldsAndMethods(String id, String pleaseIgnore, int ignoreThisAsWell, boolean dontIgnore, int ignoreBecauseOfMethod) { this.id = id; this.pleaseIgnore = pleaseIgnore; @@ -92,4 +94,13 @@ public int getIgnoreBecauseOfMethod() { public void setIgnoreBecauseOfMethod(int ignoreBecauseOfMethod) { this.ignoreBecauseOfMethod = ignoreBecauseOfMethod; } + + public String getValue() { + return value; + } + + @Name("valueSetter") + public void setValue(String value) { + this.value = value; + } } From c94841556caf4289b214a49213a755d582e4d256 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 31 Mar 2020 12:51:21 +0800 Subject: [PATCH 047/178] progress --- .../graphql/server/SchemaArgument.java | 29 ++++++- .../graphql/server/SchemaGenerator.java | 56 ++++++------- .../graphql/server/SchemaGeneratorHelper.java | 34 ++++++++ .../graphql/server/SchemaArgumentTest.java | 3 + .../graphql/server/SchemaGeneratorIT.java | 78 ++++++++++++++++++- .../graphql/server/SchemaGeneratorTest.java | 10 +++ .../graphql/server/test/db/TestDB.java | 5 ++ .../test/queries/DefaultValueQueries.java | 62 +++++++++++++++ .../server/test/queries/InvalidQueries.java | 39 ++++++++++ .../NumberFormatQueriesAndMutations.java | 6 -- .../queries/OddNamedQueriesAndMutations.java | 44 +++++++++++ .../server/test/types/DefaultValuePOJO.java | 53 +++++++++++++ .../server/test/types/DescriptionType.java | 24 ++++++ 13 files changed, 404 insertions(+), 39 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/InvalidQueries.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/OddNamedQueriesAndMutations.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DefaultValuePOJO.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index d85a67d03a1..d3a2928d877 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -18,6 +18,9 @@ import java.util.Objects; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isGraphQLType; + /** * The representation of a GraphQL Argument or Parameter. */ @@ -42,7 +45,7 @@ public class SchemaArgument /** * The default value for this argument. */ - private final Object defaultValue; + private Object defaultValue; /** * Original argument type before it was converted to a GraphQL representation. @@ -98,13 +101,22 @@ public String getSchemaAsString() { sb.append(SPACER) .append(EQUALS) .append(SPACER); - boolean isString = defaultValue instanceof String; - if (isString) { + + // determine how the default value should be rendered + String argumentType = getArgumentType(); + + if (isGraphQLType(argumentType) && STRING.equals(argumentType)) { sb.append(QUOTE) .append(defaultValue) .append(QUOTE); } else { - sb.append(defaultValue); + // Workaround for graphql profile TCK bug + if (defaultValue.toString().contains("\":")) { + sb.append("{}"); + } + else { + sb.append(defaultValue); + } } } @@ -156,6 +168,15 @@ public Object getDefaultValue() { return defaultValue; } + /** + * Set the default value for this argument. + * + * @param defaultValue the default value for this argument + */ + public void setDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + } + /** * Retrieve the original argument type. * diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 1f533af50eb..9c555d1176f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -42,7 +42,6 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetcherFactories; -import org.eclipse.microprofile.graphql.DefaultValue; import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Id; @@ -62,6 +61,8 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.checkScalars; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getArrayLevels; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDefaultDescription; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDefaultValueAnnotationValue; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDescription; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldName; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getGraphQLType; @@ -459,15 +460,14 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, if (format.length == 2) { // a format exists on the method return type so format it after returning the value final String graphQLType = getGraphQLType(fd.getReturnType()); - dataFetcher = DataFetcherFactories.wrapDataFetcher(DataFetcherUtils - .newMethodDataFetcher(clazz, method, null, - fd.getArguments().toArray( - new SchemaArgument[0])), - (d, v) -> { - NumberFormat numberFormat = getCorrectFormat( - graphQLType, format[1], format[0]); - return v != null ? numberFormat.format(v) : null; - }); + dataFetcher = DataFetcherFactories.wrapDataFetcher( + DataFetcherUtils.newMethodDataFetcher(clazz, method, null, + fd.getArguments().toArray(new SchemaArgument[0])), + (d, v) -> { + NumberFormat numberFormat = getCorrectFormat( + graphQLType, format[1], format[0]); + return v != null ? numberFormat.format(v) : null; + }); fd.setReturnType(STRING); } else { // no formatting, just call the method @@ -696,7 +696,7 @@ protected static Map retrieveAllAnnotatedBeanMethods(C } if (isQuery || isMutation || hasSourceAnnotation) { LOGGER.info("Processing Query or Mutation " + m.getName()); - DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null, false); + DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null, false, true); discoveredMethod.setMethodType(isQuery || hasSourceAnnotation ? QUERY_TYPE : MUTATION_TYPE); mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); } @@ -730,10 +730,10 @@ protected static Map retrieveGetterBeanMethods(Class getAllMethods(Class clazz) throws Introspection /** * Generate a {@link DiscoveredMethod} from the given arguments. * - * @param method {@link Method} being introspected - * @param clazz {@link Class} being introspected - * @param pd {@link PropertyDescriptor} for the property being introspected (may be null if retrieving all methods as - * in the case for a {@link Query} annotation) - * @param isInputType indicates if the method is part of an input type + * @param method {@link Method} being introspected + * @param clazz {@link Class} being introspected + * @param pd {@link PropertyDescriptor} for the property being introspected (may be null if retrieving all + * methods as in the case for a {@link Query} annotation) + * @param isInputType indicates if the method is part of an input type + * @param isQueryOrMutation indicates if this is for a query or mutation * @return a {@link DiscoveredMethod} */ private static DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, PropertyDescriptor pd, - boolean isInputType) { + boolean isInputType, + boolean isQueryOrMutation) { String name = method.getName(); String varName; String[] numberFormat = new String[0]; String description = null; - if (name.startsWith(IS) || name.startsWith(GET) || name.startsWith(SET)) { + if (!isQueryOrMutation && (name.startsWith(IS) || name.startsWith(GET) || name.startsWith(SET))) { // this is a getter method String prefix = null; if (name.startsWith(IS)) { @@ -850,7 +852,6 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, if (field != null) { numberFormat = getFormatAnnotation(field); } - } // check for method return type number format @@ -872,7 +873,7 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, if (description == null && !isInputType) { description = getDescription(method.getAnnotation(Description.class)); } - discoveredMethod.setDescription(description); + discoveredMethod.setDescription(getDefaultDescription(numberFormat, description)); Parameter[] parameters = method.getParameters(); if (parameters != null && parameters.length > 0) { @@ -886,7 +887,7 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, && !paramNameAnnotation.value().isBlank() ? paramNameAnnotation.value() : parameter.getName(); - DefaultValue defaultValueAnnotations = parameter.getAnnotation(DefaultValue.class); + Class paramType = parameter.getType(); ReturnType returnType = getReturnType(paramType, genericParameterTypes[i++]); @@ -897,9 +898,12 @@ private static DiscoveredMethod generateDiscoveredMethod(Method method, returnType.setReturnClass(ID); } - SchemaArgument argument = new SchemaArgument(parameterName, returnType.getReturnClass(), false, null, paramType); - argument.setFormat(getFormatAnnotation(parameter)); - argument.setDescription(getDescription(parameter.getAnnotation(Description.class))); + SchemaArgument argument = new SchemaArgument(parameterName, returnType.getReturnClass(), false, + getDefaultValueAnnotationValue(parameter), paramType); + String[] argumentFormat = getFormatAnnotation(parameter); + String argumentDescription = getDescription(parameter.getAnnotation(Description.class)); + argument.setFormat(argumentFormat); + argument.setDescription(getDefaultDescription(argumentFormat, argumentDescription)); Source sourceAnnotation = parameter.getAnnotation(Source.class); if (sourceAnnotation != null) { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 0eaeefad4e6..5a3a167868b 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -18,6 +18,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; import java.math.BigInteger; @@ -38,6 +39,7 @@ import graphql.Scalars; import graphql.scalars.ExtendedScalars; +import org.eclipse.microprofile.graphql.DefaultValue; import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.Enum; import org.eclipse.microprofile.graphql.Id; @@ -401,6 +403,38 @@ protected static String getNameAnnotationValue(Class clazz) { return null; } + /** + * Return the {@link DefaultValue} annotation value if it exists or null. + * + * @param parameter {@link Parameter} to check + * @return the {@link DefaultValue} annotation value if it exists or null + */ + protected static String getDefaultValueAnnotationValue(Parameter parameter) { + DefaultValue defaultValueAnnotation = parameter.getAnnotation(DefaultValue.class); + if (defaultValueAnnotation != null && !"".equals(defaultValueAnnotation.value())) { + return defaultValueAnnotation.value(); + } + return null; + } + + /** + * Return the default description based upon the format and description. + * @param format format + * @param description description + * @return the default description + */ + protected static String getDefaultDescription(String[] format, String description) { + String fmt = format == null || format.length == 0 + ? null : format[0] + " " + format[1]; + if (description == null && fmt == null) { + return null; + } + + return description == null + ? fmt : fmt == null + ? description : description + " (" + fmt.trim() + ")"; + } + /** * Return correct name for a Type or Enum based upon the value of the annotation or the {@link Name}. * diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index 4583e06acc0..82bd52c0e8f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -65,6 +65,9 @@ public void testConstructors() { assertThat(format.length, is(2)); assertThat(format[0], is("value-1")); assertThat(format[1], is("value-2")); + + schemaArgument.setDefaultValue("hello"); + assertThat(schemaArgument.getDefaultValue(), is("hello")); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index 4c9b1a149a6..9830eb00e97 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -38,8 +38,11 @@ import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations; import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations; import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; +import io.helidon.microprofile.graphql.server.test.queries.DefaultValueQueries; import io.helidon.microprofile.graphql.server.test.queries.DescriptionQueries; +import io.helidon.microprofile.graphql.server.test.queries.InvalidQueries; import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations; +import io.helidon.microprofile.graphql.server.test.queries.OddNamedQueriesAndMutations; import io.helidon.microprofile.graphql.server.test.queries.QueriesWithIgnorable; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; @@ -49,6 +52,7 @@ import io.helidon.microprofile.graphql.server.test.types.Car; import io.helidon.microprofile.graphql.server.test.types.ContactRelationship; import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; +import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO; import io.helidon.microprofile.graphql.server.test.types.DescriptionType; import io.helidon.microprofile.graphql.server.test.types.Level0; import io.helidon.microprofile.graphql.server.test.types.Motorbike; @@ -231,6 +235,12 @@ public void testVoidQueries() throws IOException { assertThrows(RuntimeException.class, () -> new ExecutionContext<>(dummyContext)); } + @Test + public void testInvalidQueries() throws IOException { + setupIndex(indexFileName, InvalidQueries.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext<>(dummyContext)); + } + @Test public void testSimpleContactWithSelf() throws IOException { setupIndex(indexFileName, SimpleContactWithSelf.class); @@ -334,13 +344,12 @@ public void testSimpleMutations() throws IOException { assertThat(mapResults2, is(notNullValue())); assertThat(mapResults2.get("id"), is(notNullValue())); assertThat(mapResults2.get("name"), is("tim")); - ; assertThat(mapResults2.get("age"), is(notNullValue())); - result = executionContext.execute("mutation { echoStringValue(value: \"echo\") }"); + result = executionContext.execute("mutation { setEchoStringValue(value: \"echo\") }"); mapResults = getAndAssertResult(result); assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("echoStringValue"), is("echo")); + assertThat(mapResults.get("setEchoStringValue"), is("echo")); } @Test @@ -448,6 +457,14 @@ public void testDescriptions() throws IOException { if (fd.getName().equals("value")) { assertThat(fd.getDescription(), is("description of value")); } + if (fd.getName().equals("longValue1")) { + // no description so include the format + assertThat(fd.getDescription(), is("L-######## ##default")); + } + if (fd.getName().equals("longValue2")) { + // both description and formatting + assertThat(fd.getDescription(), is("Description (###,### en-AU)")); + } }); SchemaInputType inputType = schema.getInputTypeByName("DescriptionTypeInput"); @@ -459,6 +476,61 @@ public void testDescriptions() throws IOException { }); } + @Test + public void testDefaultValues() throws IOException { + setupIndex(indexFileName, DefaultValuePOJO.class, DefaultValueQueries.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + + // test with both fields as default + ExecutionResult result = executionContext.execute("mutation { generateDefaultValuePOJO { id value } }"); + Map mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + Map results = (Map) mapResults.get("generateDefaultValuePOJO"); + assertThat(results, is(notNullValue())); + assertThat(results.get("id"), is("ID-1")); + assertThat(results.get("value"), is(1000)); + + // test with a field overridden + result = executionContext.execute("mutation { generateDefaultValuePOJO(id: \"ID-123\") { id value } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + results = (Map) mapResults.get("generateDefaultValuePOJO"); + assertThat(results, is(notNullValue())); + assertThat(results.get("id"), is("ID-123")); + assertThat(results.get("value"), is(1000)); + + result = executionContext.execute("query { echoDefaultValuePOJO { id value } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + results = (Map) mapResults.get("echoDefaultValuePOJO"); + assertThat(results, is(notNullValue())); + assertThat(results.get("id"), is("ID-1")); + assertThat(results.get("value"), is(1000)); + + result = executionContext.execute("query { echoDefaultValuePOJO(input: {id: \"X123\" value: 1}) { id value } }"); + mapResults = getAndAssertResult(result); + assertThat(mapResults.size(), is(1)); + results = (Map) mapResults.get("echoDefaultValuePOJO"); + assertThat(results, is(notNullValue())); + assertThat(results.get("id"), is("X123")); + assertThat(results.get("value"), is(1)); + } + + @Test + public void setOddNamedQueriesAndMutations() throws IOException { + setupIndex(indexFileName, DefaultValuePOJO.class, OddNamedQueriesAndMutations.class); + ExecutionContext executionContext = new ExecutionContext<>(dummyContext); + + Schema schema = executionContext.getSchema(); + assertThat(schema, is(notNullValue())); + SchemaType query = schema.getTypeByName("Query"); + SchemaType mutation = schema.getTypeByName("Mutation"); + assertThat(query, is(notNullValue())); + assertThat(query.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("settlement")).count(), is(1L)); + assertThat(mutation.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("getaway")).count(), is(1L)); + + } + @Test @SuppressWarnings("unchecked") public void testSimpleQueriesWithSource() throws IOException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index ee55750b3e0..57f81673ec0 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -63,9 +63,11 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FLOAT; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT; import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormatAnnotation; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDefaultDescription; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootTypeName; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; /** @@ -263,6 +265,14 @@ public void testAllMethods() throws IntrospectionException { false, false, false); } + @Test + public void testDefaultDescription() { + assertThat(getDefaultDescription(null, null), is(nullValue())); + assertThat(getDefaultDescription(new String[] {"format","locale"} , null), is("format locale")); + assertThat(getDefaultDescription(null, "desc"), is("desc")); + assertThat(getDefaultDescription(new String[] {"format","locale"}, "desc"), is("desc (format locale)")); + } + @Test public void testArrayDiscoveredMethods() throws IntrospectionException { Map mapMethods = SchemaGenerator diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java index bc978f4a4d5..0467ba97ac4 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java @@ -30,6 +30,7 @@ import javax.enterprise.context.ApplicationScoped; import io.helidon.microprofile.graphql.server.test.types.Address; +import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO; import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; @@ -175,4 +176,8 @@ public Address generateWorkAddress() { return new Address("8 Yawkey Way", null, getRandomName(), getRandomState(), getRandomZip(), "US"); } + + public DefaultValuePOJO generatePOJO(String id, int value) { + return new DefaultValuePOJO(id, value); + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java new file mode 100644 index 00000000000..436180f38bc --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO; +import io.helidon.microprofile.graphql.server.test.types.DescriptionType; +import org.eclipse.microprofile.graphql.DefaultValue; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Mutation; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds queries that work with {@link DefaultValue} annotation. + */ +@GraphQLApi +@ApplicationScoped +public class DefaultValueQueries { + + private static final String DEFAULT_INPUT = "{ " + + " id: \"ID-1\" " + + " value: 1000 " + + "}"; + + @Inject + private TestDB testDB; + + public DefaultValueQueries() { + } + + @Mutation + @Name("generateDefaultValuePOJO") + public DefaultValuePOJO createNewPJO(@Name("id") @DefaultValue("ID-1") String id, + @Name("value") @DefaultValue("1000") int value) { + return testDB.generatePOJO(id, value); + } + + @Query + @Name("echoDefaultValuePOJO") + public DefaultValuePOJO echo(@Name("input") @DefaultValue(DEFAULT_INPUT) DefaultValuePOJO input) { + return input; + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/InvalidQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/InvalidQueries.java new file mode 100644 index 00000000000..d2b4bf77d45 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/InvalidQueries.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import javax.enterprise.context.ApplicationScoped; + +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Mutation; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds queries that also have Mutation annotation. + */ +@GraphQLApi +@ApplicationScoped +public class InvalidQueries { + + public InvalidQueries() { + } + + @Query + @Mutation + public void badQuery() { + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java index 4ae406c7dfc..607d6d8ddf1 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java @@ -16,12 +16,9 @@ package io.helidon.microprofile.graphql.server.test.queries; -import java.math.BigDecimal; import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Mutation; @@ -36,9 +33,6 @@ @ApplicationScoped public class NumberFormatQueriesAndMutations { - @Inject - private TestDB testDB; - public NumberFormatQueriesAndMutations() { } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/OddNamedQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/OddNamedQueriesAndMutations.java new file mode 100644 index 00000000000..8bd56193a49 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/OddNamedQueriesAndMutations.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import javax.enterprise.context.ApplicationScoped; + +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Mutation; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds queries and mutation with odd names. + */ +@GraphQLApi +@ApplicationScoped +public class OddNamedQueriesAndMutations { + + public OddNamedQueriesAndMutations() { + } + + @Query + public String settlement() { + return "this should be ok"; + } + + @Mutation + public String getaway() { + return "let's getaway"; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DefaultValuePOJO.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DefaultValuePOJO.java new file mode 100644 index 00000000000..be25cd3fcc2 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DefaultValuePOJO.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import org.eclipse.microprofile.graphql.Type; + +/** + * POJO to test default values. + */ +@Type +public class DefaultValuePOJO { + + private String id; + private int value; + + public DefaultValuePOJO() { + } + + public DefaultValuePOJO(String id, int value) { + this.id = id; + this.value = value; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DescriptionType.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DescriptionType.java index 0131a48cc5b..302a0624bb4 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DescriptionType.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DescriptionType.java @@ -17,6 +17,7 @@ package io.helidon.microprofile.graphql.server.test.types; import org.eclipse.microprofile.graphql.Description; +import org.eclipse.microprofile.graphql.NumberFormat; import org.eclipse.microprofile.graphql.Type; /** @@ -29,6 +30,13 @@ public class DescriptionType { private String id; private int value; + @NumberFormat("L-########") + private Long longValue1; + + @NumberFormat(value = "###,###", locale = "en-AU") + @Description("Description") + private Long longValue2; + public DescriptionType(String id, int value) { this.id = id; this.value = value; @@ -51,4 +59,20 @@ public int getValue() { public void setValue(int value) { this.value = value; } + + public Long getLongValue1() { + return longValue1; + } + + public void setLongValue1(Long longValue1) { + this.longValue1 = longValue1; + } + + public Long getLongValue2() { + return longValue2; + } + + public void setLongValue2(Long longValue2) { + this.longValue2 = longValue2; + } } From a6a8777f5f2d4975a6477650a8e58598177f135f Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 31 Mar 2020 13:59:34 +0800 Subject: [PATCH 048/178] further schema fixes --- microprofile/graphql/server/pom.xml | 9 +++- .../graphql/server/SchemaGenerator.java | 47 +++++++++---------- .../graphql/server/SchemaGeneratorHelper.java | 39 ++++++++++++++- .../graphql/server/SchemaGeneratorIT.java | 1 - .../test/queries/SimpleQueriesWithSource.java | 2 +- 5 files changed, 66 insertions(+), 32 deletions(-) diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 8f58ada1a8d..6895241d694 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -1,5 +1,4 @@ - - - io.helidon.microprofile.bundles - internal-test-libs + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all test diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index c7c2fcad064..e4ac7836d85 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -33,7 +33,6 @@ import graphql.schema.DataFetchingEnvironment; import graphql.schema.PropertyDataFetcher; - import graphql.GraphQLException; import graphql.schema.DataFetcher; import graphql.schema.PropertyDataFetcherHelper; @@ -162,9 +161,6 @@ public Object get(DataFetchingEnvironment environment) { return originalResult != null ? numberFormat.format(originalResult) : null; } - /** - * {@inheritDoc} - */ @Override public NumberFormat getNumberFormat() { return numberFormat; @@ -174,7 +170,8 @@ public NumberFormat getNumberFormat() { /** * An implementation of a {@link PropertyDataFetcher} which returns a formatted date. */ - public static class DateFormattingDataFetcher extends PropertyDataFetcher { + public static class DateFormattingDataFetcher + extends PropertyDataFetcher implements DateFormattingProvider { /** * {@link DateTimeFormatter} to format with. @@ -199,6 +196,11 @@ public Object get(DataFetchingEnvironment environment) { return originalResult instanceof TemporalAccessor ? dateTimeFormatter.format((TemporalAccessor) originalResult) : null; } + + @Override + public DateTimeFormatter getDateTimeFormat() { + return dateTimeFormatter; + } } /** diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DateFormattingProvider.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DateFormattingProvider.java new file mode 100644 index 00000000000..1a7732d6e29 --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DateFormattingProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019, 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; + +import java.time.format.DateTimeFormatter; + +/** + * An interface indicating the {@link DateTimeFormatter} used to format a field. + */ +public interface DateFormattingProvider extends FormattingProvider { + /** + * Return the {@link DateTimeFormatter} used. + * @return he {@link DateTimeFormatter} used + */ + DateTimeFormatter getDateTimeFormat(); +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java index 40855fab82f..e6501816384 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java @@ -32,6 +32,7 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_DECIMAL_CLASS; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_INTEGER; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_INTEGER_CLASS; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BYTE_CLASS; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DEFAULT_LOCALE; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DOUBLE_CLASS; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DOUBLE_PRIMITIVE_CLASS; @@ -48,6 +49,7 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.LONG_PRIMITIVE_CLASS; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.OFFSET_DATE_TIME_CLASS; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.OFFSET_TIME_CLASS; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.SHORT_CLASS; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.SUPPORTED_SCALARS; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ZONED_DATE_TIME_CLASS; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException; @@ -128,6 +130,8 @@ protected static NumberFormat getCorrectNumberFormat(String type, String locale, || INTEGER_CLASS.equals(type) || INTEGER_PRIMITIVE_CLASS.equals(type) || BIG_INTEGER_CLASS.equals(type) + || BYTE_CLASS.equals(type) + || SHORT_CLASS.equals(type) || LONG_CLASS.equals(type) || DOUBLE_CLASS.equals(type) || DOUBLE_PRIMITIVE_CLASS.equals(type) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java index 3249cc7f028..ca1448b31f8 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java @@ -72,11 +72,8 @@ public class GraphQLResource { @Path(SCHEMA_URL) @GET - // @Produces({ MediaType.TEXT_PLAIN }) - // @Consumes({ MediaType.TEXT_PLAIN }) - // see https://github.com/eclipse/microprofile-graphql/issues/202 - @Produces("plain/text") - @Consumes("plain/text") + @Produces({ MediaType.TEXT_PLAIN }) + @Consumes({ MediaType.TEXT_PLAIN }) public Response getGraphQLSchema() { return Response.ok(schemaPrinter.print(context.getGraphQLSchema())).build(); } diff --git a/microprofile/graphql/server/src/main/java/module-info.java b/microprofile/graphql/server/src/main/java/module-info.java index 067a87b0ab0..e10feea8d19 100644 --- a/microprofile/graphql/server/src/main/java/module-info.java +++ b/microprofile/graphql/server/src/main/java/module-info.java @@ -33,6 +33,8 @@ requires graphql.java.extended.scalars; requires microprofile.graphql.api; + requires transitive microprofile.config.api; + exports io.helidon.microprofile.graphql.server; requires io.helidon.config; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java index 3d230854ff5..c28c243d729 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java @@ -17,14 +17,14 @@ package io.helidon.microprofile.graphql.server; import java.io.IOException; + import java.util.List; import java.util.Map; -import io.helidon.config.Config; -import io.helidon.config.ConfigSources; - +import io.helidon.config.mp.MpConfigSources; import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; +import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.hamcrest.Matchers; @@ -57,7 +57,7 @@ public void testAllDefaultsForConfig() throws IOException { @Test public void testDifferentMessage() throws IOException { Config config = setupConfig("config/config1.properties"); - assertThat(config.get("mp").get("graphql").get("defaultErrorMessage").asString().get(), is("new message")); + assertThat(config.getValue("mp.graphql.defaultErrorMessage", String.class), is("new message")); setupIndex(indexFileName); @@ -235,9 +235,15 @@ private void assertPayload(Map errorMap) { } protected Config setupConfig(String propertiesFile) { - Config config = propertiesFile == null ? Config.create() : Config.create(ConfigSources.classpath(propertiesFile)); + Config config = propertiesFile == null + ? ConfigProviderResolver.instance().getBuilder().build() + : ConfigProviderResolver.instance() + .getBuilder() + .withSources(MpConfigSources.create(ExceptionHandlingIT.class.getClassLoader().getResource(propertiesFile))) + .build(); + ConfigProviderResolver.instance() - .registerConfig((org.eclipse.microprofile.config.Config) config, Thread.currentThread().getContextClassLoader()); + .registerConfig(config, Thread.currentThread().getContextClassLoader()); return config; } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java index d9d3066db01..58e71244fbf 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java @@ -91,7 +91,7 @@ public void testGetSchema() { // see https://github.com/eclipse/microprofile-graphql/issues/202 // add text/plain WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL).path(SCHEMA_URL); - Response response = webTarget.request().get(); + Response response = webTarget.request(MediaType.TEXT_PLAIN).get(); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index d3c2af96d6a..fa4657af01b 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -339,8 +339,6 @@ public void testNumberFormats() throws IOException { assertThat(mapResults.size(), is(1)); mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); assertThat(mapResults2, is(notNullValue())); - - } @Test From 05f8075c98597e34722f07038684b92e062ed43a Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 31 Aug 2020 08:05:01 +0800 Subject: [PATCH 073/178] work on dates --- .../graphql/server/DataFetcherUtils.java | 31 +++++++++++-- .../graphql/server/FormattedNumberImpl.java | 3 +- .../graphql/server/SchemaGenerator.java | 4 +- .../graphql/server/SchemaGeneratorIT.java | 46 +++++++++---------- 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index e4ac7836d85..958ac3d4586 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -16,6 +16,7 @@ package io.helidon.microprofile.graphql.server; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.DateFormat; @@ -31,16 +32,18 @@ import javax.enterprise.inject.spi.CDI; +import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.PropertyDataFetcher; -import graphql.GraphQLException; -import graphql.schema.DataFetcher; import graphql.schema.PropertyDataFetcherHelper; +import graphql.GraphQLException; + import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectDateFormatter; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectNumberFormat; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isDateTimeScalar; /** * Utilities for working with {@link DataFetcher}s. @@ -104,7 +107,29 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met // convert back to original data type listArgumentValues.add(getOriginalValue(originalType, (String) key)); } else { - listArgumentValues.add(key); + // check the format and convert it from a string to the original format + String[] format = argument.getFormat(); + if (format != null && format.length == 2) { + if (isDateTimeScalar(argument.getArgumentType())) { + DateTimeFormatter dateFormatter = getCorrectDateFormatter(originalType.getName(), + format[1], format[0]); + listArgumentValues.add(dateFormatter.parse(key.toString())); + } else { + NumberFormat numberFormat = getCorrectNumberFormat(originalType.getName(), + format[1], format[0]); + if (numberFormat != null) { + // convert to the original type + Number parsedValue = numberFormat.parse(key.toString()); + Constructor constructor = argument.getOriginalType() + .getDeclaredConstructor(String.class); + listArgumentValues.add(constructor.newInstance(parsedValue.toString())); + } else { + listArgumentValues.add(key); + } + } + } else { + listArgumentValues.add(key); + } } } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattedNumberImpl.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattedNumberImpl.java index e0e74c524cf..d1eb47140b1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattedNumberImpl.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattedNumberImpl.java @@ -21,7 +21,8 @@ /** * An implementation of a {@link FormattedNumber}. */ -public class FormattedNumberImpl implements FormattedNumber { +public class FormattedNumberImpl + implements FormattedNumber { /** * The {@link NumberFormat} used to format the value. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 0512cf7f572..442d57f110e 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -1080,12 +1080,12 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM argument.setDescription(argumentDescription); if (argumentFormat[0] != null) { argument.setFormat(new String[] {argumentFormat[1], argumentFormat[2] }); - // TODO: Need to set return type + argument.setArgumentType(String.class.getName()); } Source sourceAnnotation = parameter.getAnnotation(Source.class); if (sourceAnnotation != null) { - // set the method name to the correct property name as it will be currently be incorrect + // set the method name to the correct property name as it will currently be incorrect discoveredMethod.setName(annotatedName != null ? annotatedName : stripMethodName(method, false)); discoveredMethod.setSource(returnType.getReturnClass()); discoveredMethod.setQueryAnnotated(method.getAnnotation(Query.class) != null); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index fa4657af01b..e120170efe7 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -18,6 +18,7 @@ import java.beans.IntrospectionException; import java.io.IOException; +import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; import java.util.ArrayList; @@ -319,30 +320,30 @@ public void testNumberFormats() throws IOException { mapResults = getAndAssertResult(executionContext.execute("query { echoBigDecimalUsingFormat(param1: \"BD-123\") }")); assertThat(mapResults, is(notNullValue())); - assertThat(mapResults.get("echoBigDecimalUsingFormat"), is(123)); + assertThat(mapResults.get("echoBigDecimalUsingFormat"), is(BigDecimal.valueOf(123))); // create a new contact - String contactInput = - "contact: {" - + "id: \"1 id\" " - + "name: \"Tim\" " - + "age: \"20 years old\" " - + "bankBalance: \"$ 1000.01\" " - + "value: \"9 value\" " - + "longValue: 12345" - + "bigDecimal: \"BigDecimal-12345\"" - + " } "; - - mapResults = getAndAssertResult( - executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + - ") { id name } }")); - assertThat(mapResults.size(), is(1)); - mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); - assertThat(mapResults2, is(notNullValue())); - } - - @Test - @Disabled +// String contactInput = +// "contact: {" +// + "id: \"1 id\" " +// + "name: \"Tim\" " +// + "age: \"20 years old\" " +// + "bankBalance: \"$ 1000.01\" " +// + "value: \"9 value\" " +// + "longValue: 12345" +// + "bigDecimal: \"BigDecimal-12345\"" +// + " } "; +// +// mapResults = getAndAssertResult( +// executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + +// ") { id name } }")); +// assertThat(mapResults.size(), is(1)); +// mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); +// assertThat(mapResults2, is(notNullValue())); + } + + @Test + // @Disabled public void testDateAndTime() throws IOException { setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesNoArgs.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); @@ -390,7 +391,6 @@ public void testDateAndTime() throws IOException { assertThat(mapResults2.get("localDate2"), is("08/04/1970")); assertThat(mapResults2.get("localTime"), is("10:10:20")); assertThat(mapResults2.get("offsetTime"), is("08:10:01+0000")); - } @Test From 6efcd6e4b845d6fa741684e0195271ecbe201093 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 3 Sep 2020 11:01:58 +0800 Subject: [PATCH 074/178] fixup date formatting --- dependencies/pom.xml | 5 +- .../graphql/server/CustomDateTimeScalar.java | 144 +++++++++------- .../graphql/server/CustomScalars.java | 95 +++++++++- .../graphql/server/DataFetcherUtils.java | 20 ++- .../graphql/server/ExecutionContext.java | 3 +- .../graphql/server/FormattingHelper.java | 163 +++++++++++++++++- .../graphql/server/GraphQLResource.java | 1 - .../graphql/server/JandexUtils.java | 106 +++++++++--- .../graphql/server/SchemaFieldDefinition.java | 26 ++- .../graphql/server/SchemaGenerator.java | 162 +++++++++++++---- .../graphql/server/SchemaGeneratorHelper.java | 15 +- .../graphql/server/AbstractGraphQLTest.java | 1 - .../graphql/server/SchemaGeneratorIT.java | 39 ++++- .../graphql/server/SchemaGeneratorTest.java | 24 +-- .../server/test/queries/QueriesWithNulls.java | 2 +- .../test/queries/SimpleQueriesNoArgs.java | 10 +- .../test/queries/SimpleQueryDateTest.java | 65 +++++++ .../server/test/types/DateTimePojo.java | 17 +- .../types/SimpleContactWithNumberFormats.java | 5 + 19 files changed, 737 insertions(+), 166 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueryDateTest.java diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 6b53205e308..3a488a99570 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -52,8 +52,9 @@ 1.30.11 2.3.3 20.1.0 - 14.1 - 1.0.1 + 15.0 + 1.1.0 + 1.27.1 1.32.1 28.1-jre 1.4.199 diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomDateTimeScalar.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomDateTimeScalar.java index 664349a2cc9..2057badcc58 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomDateTimeScalar.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomDateTimeScalar.java @@ -26,6 +26,8 @@ import java.util.function.Function; import graphql.language.StringValue; +import graphql.language.Value; +import graphql.scalars.datetime.DateTimeScalar; import graphql.schema.Coercing; import graphql.schema.CoercingParseLiteralException; import graphql.schema.CoercingParseValueException; @@ -38,78 +40,102 @@ * Custom Time scalar. Copied initially from graphql.scalars.datetime.DateTimeScalar from extend scalars package. * */ -public class CustomDateTimeScalar extends GraphQLScalarType { +public class CustomDateTimeScalar { - /** - * Instance of the scalar. - */ - public static final GraphQLScalarType INSTANCE = new CustomDateTimeScalar(); + private GraphQLScalarType dateTimeScalar = new DateTimeScalar(); - /** - * Construct a new scalar. - */ - public CustomDateTimeScalar() { - super("DateTime", "An RFC-3339 compliant DateTime Scalar", new Coercing() { - @Override - public String serialize(Object input) throws CoercingSerializeException { - OffsetDateTime offsetDateTime; - if (input instanceof LocalDateTime) { - return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format((TemporalAccessor) input); - } - if (input instanceof OffsetDateTime) { - offsetDateTime = (OffsetDateTime) input; - } else if (input instanceof ZonedDateTime) { - offsetDateTime = ((ZonedDateTime) input).toOffsetDateTime(); - } else if (input instanceof String) { - offsetDateTime = parseOffsetDateTime(input.toString(), CoercingSerializeException::new); + public static GraphQLScalarType getInstance() { + return GraphQLScalarType.newScalar().coercing(new Coercing() { + public String serialize(Object dataFetcherResult) throws CoercingSerializeException { + if (dataFetcherResult instanceof String) { + return (String) dataFetcherResult; } else { - throw new CoercingSerializeException( - "Expected something we can convert to 'java.time.OffsetDateTime' but was '" + typeName(input) + "'." - ); - } - try { - return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(offsetDateTime); - } catch (DateTimeException e) { - throw new CoercingSerializeException( - "Unable to turn TemporalAccessor into OffsetDateTime because of : '" + e.getMessage() + "'." - ); + throw new RuntimeException("Shouldn't Happen"); } } @Override public OffsetDateTime parseValue(Object input) throws CoercingParseValueException { - OffsetDateTime offsetDateTime; - if (input instanceof OffsetDateTime) { - offsetDateTime = (OffsetDateTime) input; - } else if (input instanceof ZonedDateTime) { - offsetDateTime = ((ZonedDateTime) input).toOffsetDateTime(); - } else if (input instanceof String) { - offsetDateTime = parseOffsetDateTime(input.toString(), CoercingParseValueException::new); - } else { - throw new CoercingParseValueException( - "Expected a 'String' but was '" + typeName(input) + "'." - ); - } - return offsetDateTime; + return null; } + @Override public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralException { - if (!(input instanceof StringValue)) { - throw new CoercingParseLiteralException( - "Expected AST type 'StringValue' but was '" + typeName(input) + "'." - ); - } - return parseOffsetDateTime(((StringValue) input).getValue(), CoercingParseLiteralException::new); + return null; } + }) + .name("DateTime") + .description("An Custom RFC-3339 compliant DateTime Scalar") + .build(); + } + /** + * Construct a new scalar. + */ + private CustomDateTimeScalar() { - private OffsetDateTime parseOffsetDateTime(String s, Function exceptionMaker) { - try { - return OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME); - } catch (DateTimeParseException e) { - throw exceptionMaker.apply("Invalid RFC3339 value : '" + s + "'. because of : '" + e.getMessage() + "'"); - } - } - }); +// +// super("DateTime", "An RFC-3339 compliant DateTime Scalar", new Coercing() { +// @Override +// public String serialize(Object input) throws CoercingSerializeException { +// OffsetDateTime offsetDateTime; +// if (input instanceof LocalDateTime) { +// return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format((TemporalAccessor) input); +// } +// if (input instanceof OffsetDateTime) { +// offsetDateTime = (OffsetDateTime) input; +// } else if (input instanceof ZonedDateTime) { +// offsetDateTime = ((ZonedDateTime) input).toOffsetDateTime(); +// } else if (input instanceof String) { +// offsetDateTime = parseOffsetDateTime(input.toString(), CoercingSerializeException::new); +// } else { +// throw new CoercingSerializeException( +// "Expected something we can convert to 'java.time.OffsetDateTime' but was '" + typeName(input) + "'." +// ); +// } +// try { +// return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(offsetDateTime); +// } catch (DateTimeException e) { +// throw new CoercingSerializeException( +// "Unable to turn TemporalAccessor into OffsetDateTime because of : '" + e.getMessage() + "'." +// ); +// } +// } +// +// @Override +// public OffsetDateTime parseValue(Object input) throws CoercingParseValueException { +// OffsetDateTime offsetDateTime; +// if (input instanceof OffsetDateTime) { +// offsetDateTime = (OffsetDateTime) input; +// } else if (input instanceof ZonedDateTime) { +// offsetDateTime = ((ZonedDateTime) input).toOffsetDateTime(); +// } else if (input instanceof String) { +// offsetDateTime = parseOffsetDateTime(input.toString(), CoercingParseValueException::new); +// } else { +// throw new CoercingParseValueException( +// "Expected a 'String' but was '" + typeName(input) + "'." +// ); +// } +// return offsetDateTime; +// } +// +// @Override +// public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralException { +// if (!(input instanceof StringValue)) { +// throw new CoercingParseLiteralException( +// "Expected AST type 'StringValue' but was '" + typeName(input) + "'." +// ); +// } +// return parseOffsetDateTime(((StringValue) input).getValue(), CoercingParseLiteralException::new); +// } +// +// private OffsetDateTime parseOffsetDateTime(String s, Function exceptionMaker) { +// try { +// return OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME); +// } catch (DateTimeParseException e) { +// throw exceptionMaker.apply("Invalid RFC3339 value : '" + s + "'. because of : '" + e.getMessage() + "'"); +// } +// } +// }); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java index 2ecbab5a3a1..cfec09b19f5 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java @@ -20,12 +20,16 @@ import java.math.BigInteger; import java.text.NumberFormat; import java.text.ParseException; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.OffsetTime; import static graphql.Scalars.GraphQLBigInteger; import static graphql.Scalars.GraphQLFloat; import static graphql.Scalars.GraphQLInt; import graphql.Scalars; +import graphql.scalars.ExtendedScalars; import graphql.schema.Coercing; import graphql.schema.CoercingParseLiteralException; import graphql.schema.CoercingParseValueException; @@ -47,6 +51,89 @@ private CustomScalars() { public static final GraphQLScalarType CUSTOM_INT_SCALAR = newCustomGraphQLInt(); public static final GraphQLScalarType CUSTOM_FLOAT_SCALAR = newCustomGraphQLFloat(); public static final GraphQLScalarType CUSTOM_BIGINTEGER_SCALAR = newCustomGraphQLBigInteger(); + public static final GraphQLScalarType CUSTOM_DATE_TIME_SCALAR = newDateTimeScalar(); + public static final GraphQLScalarType CUSTOM_TIME_SCALAR = newTimeScalar(); + public static final GraphQLScalarType CUSTOM_DATE_SCALAR = newDateScalar(); + + public static GraphQLScalarType newDateTimeScalar() { + GraphQLScalarType originalScalar = ExtendedScalars.DateTime; + Coercing originalCoercing = originalScalar.getCoercing(); + return GraphQLScalarType.newScalar().coercing(new Coercing() { + public String serialize(Object dataFetcherResult) throws CoercingSerializeException { + if (dataFetcherResult instanceof String) { + return (String) dataFetcherResult; + } else { + return originalCoercing.serialize(dataFetcherResult); + } + } + + @Override + public OffsetDateTime parseValue(Object input) throws CoercingParseValueException { + return null; + } + + + @Override + public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralException { + return null; + } + }) + .name("DateTime") + .description("A Custom RFC-3339 compliant DateTime Scalar") + .build(); + } + + public static GraphQLScalarType newTimeScalar() { + GraphQLScalarType originalScalar = ExtendedScalars.Time; + Coercing originalCoercing = originalScalar.getCoercing(); + return GraphQLScalarType.newScalar().coercing(new Coercing() { + public String serialize(Object dataFetcherResult) throws CoercingSerializeException { + return dataFetcherResult instanceof String + ? (String) dataFetcherResult + : originalCoercing.serialize(dataFetcherResult); + } + + @Override + public OffsetTime parseValue(Object input) throws CoercingParseValueException { + return null; + } + + + @Override + public OffsetTime parseLiteral(Object input) throws CoercingParseLiteralException { + return null; + } + }) + .name("Time") + .description("A Custom RFC-3339 compliant Time Scalar") + .build(); + } + + public static GraphQLScalarType newDateScalar() { + GraphQLScalarType originalScalar = ExtendedScalars.Date; + Coercing originalCoercing = originalScalar.getCoercing(); + return GraphQLScalarType.newScalar().coercing(new Coercing() { + public String serialize(Object dataFetcherResult) throws CoercingSerializeException { + return dataFetcherResult instanceof String + ? (String) dataFetcherResult + : originalCoercing.serialize(dataFetcherResult); + } + + @Override + public LocalDate parseValue(Object input) throws CoercingParseValueException { + return null; + } + + + @Override + public LocalDate parseLiteral(Object input) throws CoercingParseLiteralException { + return null; + } + }) + .name("Date") + .description("A Custom RFC-3339 compliant Date Scalar") + .build(); + } /** * Creates a custom BigDecimalScalar which will parse a formatted value which was originally formatted using a {@link @@ -62,11 +149,9 @@ private static GraphQLScalarType newCustomBigDecimalScalar() { Coercing formattingCoercing = new Coercing<>() { @Override public Object serialize(Object dataFetcherResult) throws CoercingSerializeException { - Object finalDataFetcherResult = dataFetcherResult; - if (dataFetcherResult instanceof FormattedNumber) { - return ((FormattedNumber) finalDataFetcherResult).getFormattedValue(); - } - return originalCoercing.serialize(finalDataFetcherResult); + return dataFetcherResult instanceof String + ? (String) dataFetcherResult + : originalCoercing.serialize(dataFetcherResult); } @Override diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 958ac3d4586..459af7b15e1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -25,6 +25,7 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.ArrayList; +import java.util.Collection; import java.util.Locale; import java.util.Map; import java.util.UUID; @@ -39,6 +40,8 @@ import graphql.GraphQLException; +import static io.helidon.microprofile.graphql.server.FormattingHelper.formatDate; +import static io.helidon.microprofile.graphql.server.FormattingHelper.formatNumber; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectDateFormatter; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectNumberFormat; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; @@ -110,6 +113,7 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met // check the format and convert it from a string to the original format String[] format = argument.getFormat(); if (format != null && format.length == 2) { + // TODO: This always returns false, need to fix if (isDateTimeScalar(argument.getArgumentType())) { DateTimeFormatter dateFormatter = getCorrectDateFormatter(originalType.getName(), format[1], format[0]); @@ -180,10 +184,7 @@ public NumberFormattingDataFetcher(String propertyName, String type, String valu @Override public Object get(DataFetchingEnvironment environment) { Object originalResult = super.get(environment); - if (isScalar) { - return new FormattedNumberImpl(numberFormat, originalResult != null ? numberFormat.format(originalResult) : null); - } - return originalResult != null ? numberFormat.format(originalResult) : null; + return formatNumber(super.get(environment), isScalar, numberFormat); } @Override @@ -195,8 +196,10 @@ public NumberFormat getNumberFormat() { /** * An implementation of a {@link PropertyDataFetcher} which returns a formatted date. */ + @SuppressWarnings({"unchecked", "rawtypes"}) public static class DateFormattingDataFetcher - extends PropertyDataFetcher implements DateFormattingProvider { + extends PropertyDataFetcher + implements DateFormattingProvider { /** * {@link DateTimeFormatter} to format with. @@ -217,10 +220,8 @@ public DateFormattingDataFetcher(String propertyName, String type, String valueF @Override public Object get(DataFetchingEnvironment environment) { - Object originalResult = super.get(environment); - return originalResult instanceof TemporalAccessor - ? dateTimeFormatter.format((TemporalAccessor) originalResult) : null; - } + return formatDate(super.get(environment), dateTimeFormatter); + } @Override public DateTimeFormatter getDateTimeFormat() { @@ -228,6 +229,7 @@ public DateTimeFormatter getDateTimeFormat() { } } + /** * Create a new {@link DataFetcher} which formats a date/time. * diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index 5e1f8183727..6c4f1afabd4 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -191,8 +191,7 @@ public ExecutionContext(Context context) { SchemaPrinter.Options options = SchemaPrinter.Options .defaultOptions() .includeDirectives(false) - .includeScalarTypes(true) - .includeExtendedScalarTypes(true); + .includeScalarTypes(true); SchemaPrinter schemaPrinter = new SchemaPrinter(options); GraphQL.Builder builder = GraphQL.newGraphQL(this.graphQLSchema) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java index e6501816384..7fffe363590 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java @@ -20,6 +20,9 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.util.ArrayList; +import java.util.Collection; import java.util.Locale; import java.util.logging.Logger; @@ -27,6 +30,9 @@ import javax.json.bind.annotation.JsonbNumberFormat; import org.eclipse.microprofile.graphql.DateFormat; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationValue; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_DECIMAL; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_DECIMAL_CLASS; @@ -82,8 +88,28 @@ public class FormattingHelper { /** * Indicates no formatting applied. */ - private static final String[] NO_FORMATTING = new String[] {null, null, null }; + protected static final String[] NO_FORMATTING = new String[] {null, null, null }; + /** + * JsonbDateFormat class name. + */ + private static final String JSONB_DATE_FORMAT = JsonbDateFormat.class.getName(); + + /** + * DateFormat class name. + */ + private static final String DATE_FORMAT = org.eclipse.microprofile.graphql.DateFormat.class.getName(); + + /** + * JsonBNumberFormat class name. + */ + private static final String JSONB_NUMBER_FORMAT = JsonbNumberFormat.class.getName(); + + /** + * NumberFormat class name. + */ + private static final String NUMBER_FORMAT = org.eclipse.microprofile.graphql.NumberFormat.class.getName(); + /** * No-args constructor. */ @@ -198,7 +224,7 @@ protected static NumberFormat getCorrectNumberFormat(String type, String locale) /** * Return formatting annotation details for the {@link AnnotatedElement}. The array returned contains three elements: [0] = - * either DATE,NUMBER or null if no formatting applied: [1][2] = if formatting applied then the format and locale. + * either DATE, NUMBER or null if no formatting applied: [1][2] = if formatting applied then the format and locale. * * @param annotatedElement the {@link AnnotatedElement} to check * @return formatting annotation details or array with three nulls if no formatting @@ -220,6 +246,86 @@ protected static String[] getFormattingAnnotation(AnnotatedElement annotatedElem : new String[] {NUMBER, numberFormat[0], numberFormat[1] }; } + /** + * Return the method parameter format using Jandex. + * + * @param jandexUtils {@link JandexUtils} to use + * @param clazz {@link Class} to check for annotation + * @param methodName method name to check + * @param paramNumber parameter number to check + * @return + */ + protected static String[] getMethodParameterFormat(JandexUtils jandexUtils, String clazz, String methodName, + int paramNumber) { + + if (jandexUtils.hasIndex()) { + AnnotationInstance dateFormat1 = + jandexUtils.getMethodParameterAnnotation(clazz, methodName, paramNumber, JSONB_DATE_FORMAT); + AnnotationInstance dateFormat2 = + jandexUtils.getMethodParameterAnnotation(clazz, methodName, paramNumber, DATE_FORMAT); + AnnotationInstance numberFormat1 = + jandexUtils.getMethodParameterAnnotation(clazz, methodName, paramNumber, JSONB_NUMBER_FORMAT); + AnnotationInstance numberFormat2 = + jandexUtils.getMethodParameterAnnotation(clazz, methodName, paramNumber, NUMBER_FORMAT); + + return getFormatFromAnnotationInstance(dateFormat1, dateFormat2, numberFormat1, numberFormat2); + } + return NO_FORMATTING; + } + + /** + * Return a format given a {@link AnnotationInstance}. + * @param dateFormat1 {@link AnnotationInstance} date format from {@link JsonbDateFormat} + * @param dateFormat2 {@link AnnotationInstance} date format from {@link DateFormat} + * @param numberFormat1 {@link AnnotationInstance} number format from {@link JsonbNumberFormat} + * @param numberFormat2 {@link AnnotationInstance} number format from {@link org.eclipse.microprofile.graphql.NumberFormat} + * @return a String[] representing the format and locale + */ + private static String[] getFormatFromAnnotationInstance(AnnotationInstance dateFormat1, + AnnotationInstance dateFormat2, + AnnotationInstance numberFormat1, + AnnotationInstance numberFormat2) { + // ensure that both date and number formatting are not present + if (dateFormat1 != null || dateFormat2 != null && + numberFormat1 != null || numberFormat2 != null) { + ensureRuntimeException(LOGGER, "Cannot have date and number formatting on the same method"); + } + AnnotationInstance formatInstance = dateFormat1 != null ? dateFormat1 + : dateFormat2 != null ? dateFormat2 + : numberFormat1 != null ? numberFormat1 + : numberFormat2; + if (formatInstance == null) { + return NO_FORMATTING; + } + + String type = dateFormat1 != null || dateFormat2 != null ? DATE + : NUMBER; + + AnnotationValue format = formatInstance.value("value"); + AnnotationValue locale = formatInstance.value("locale"); + return new String[] {type, format.asString(), locale == null ? DEFAULT_LOCALE : locale.asString() }; + } + + /** + * Return the method format using Jandex. + * + * @param jandexUtils {@link JandexUtils} to use + * @param clazz {@link Class} to check for annotation + * @param methodName method name to check + * @return + */ + protected static String[] getMethodFormat(JandexUtils jandexUtils, String clazz, String methodName) { + if (jandexUtils.hasIndex()) { + AnnotationInstance dateFormat1 = jandexUtils.getMethodAnnotation(clazz, methodName, JSONB_DATE_FORMAT); + AnnotationInstance dateFormat2 = jandexUtils.getMethodAnnotation(clazz, methodName, DATE_FORMAT); + AnnotationInstance numberFormat1 = jandexUtils.getMethodAnnotation(clazz, methodName, JSONB_NUMBER_FORMAT); + AnnotationInstance numberFormat2 = jandexUtils.getMethodAnnotation(clazz, methodName, NUMBER_FORMAT); + + return getFormatFromAnnotationInstance(dateFormat1, dateFormat2, numberFormat1, numberFormat2); + } + return NO_FORMATTING; + } + /** * Return the number format and locale for an {@link AnnotatedElement} if they exist in a {@link String} array. * @@ -282,4 +388,57 @@ private static String[] getDateFormatAnnotationInternal(JsonbDateFormat jsonbDat return new String[0]; } + /** + * Format a date value given a {@link DateTimeFormatter}. + * @param originalResult original result + * @param dateTimeFormatter {@link DateTimeFormatter} to format with + * @return formatted value + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Object formatDate(Object originalResult, DateTimeFormatter dateTimeFormatter) { + if (originalResult == null) { + return null; + } + if (originalResult instanceof Collection) { + Collection formattedResult = new ArrayList(); + Collection originalCollection = (Collection) originalResult; + originalCollection.forEach(e -> formattedResult.add(e instanceof TemporalAccessor ? dateTimeFormatter + .format((TemporalAccessor) e) : e) + ); + return formattedResult; + + } else { + return originalResult instanceof TemporalAccessor + ? dateTimeFormatter.format((TemporalAccessor) originalResult) : originalResult; + } + } + + /** + * Format a number value with a a given {@link NumberFormat}. + * @param originalResult original result + * @param isScalar indicates if it is a scalar value + * @param numberFormat {@link NumberFormat} to format with + * @return formatted value + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Object formatNumber(Object originalResult, boolean isScalar, NumberFormat numberFormat) { + if (originalResult == null) { + return null; + } + if (originalResult instanceof Collection) { + Collection formattedResult = new ArrayList(); + Collection originalCollection = (Collection) originalResult; + originalCollection.forEach(e -> { + if (isScalar) { + formattedResult.add(new FormattedNumberImpl(numberFormat, numberFormat.format(originalResult))); + } else { + formattedResult.add(numberFormat.format(originalResult)); + } + }); + return formattedResult; + } + return isScalar ? new FormattedNumberImpl(numberFormat, numberFormat.format(originalResult)) + : numberFormat.format(originalResult); + } + } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java index ca1448b31f8..e28b61025b3 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java @@ -57,7 +57,6 @@ public class GraphQLResource { */ private static final SchemaPrinter.Options OPTIONS = SchemaPrinter.Options .defaultOptions().includeDirectives(false) - .includeExtendedScalarTypes(true) .includeScalarTypes(true); /** diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java index ad89b1544ef..29981923514 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java @@ -23,9 +23,12 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; @@ -132,15 +135,33 @@ public Collection> getKnownImplementors(String clazz, boolean includeAb * @return true if the given class, method and parameter has the specified annotation class name */ protected boolean methodParameterHasAnnotation(String clazz, String methodName, int paramNumber, String annotationClazz) { - ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz)); - if (classByName != null) { - MethodInfo methodInfo = classByName.firstMethod(methodName); - if (methodInfo != null) { - ClassType classType = retrieveInnerMostType(methodInfo.parameters().get(paramNumber)); - return classType != null && classType.hasAnnotation(DotName.createSimple(annotationClazz)); + return getMethodParameterAnnotation(clazz, methodName, paramNumber, annotationClazz) != null; + } + + /** + * Return the {@link AnnotationInstance} for the given class, method and parameter has the specified annotation class name for a generic type. + * + * @param clazz {@link Class} to check for annotation + * @param methodName method name to check + * @param paramNumber parameter number to check + * @param annotationClazz the annotation {@link Class} to check + * @return {@link AnnotationInstance} or null if none present + */ + protected AnnotationInstance getMethodParameterAnnotation(String clazz, String methodName, + int paramNumber, String annotationClazz) { + if (hasIndex()) { + ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz)); + if (classByName != null) { + MethodInfo methodInfo = classByName.firstMethod(methodName); + if (methodInfo != null) { + ClassType classType = retrieveInnerMostType(methodInfo.parameters().get(paramNumber)); + return classType.annotations().stream() + .filter(a -> a.name().equals(DotName.createSimple(annotationClazz))) + .findFirst().orElse(null); + } } } - return false; + return null; } /** @@ -152,18 +173,33 @@ protected boolean methodParameterHasAnnotation(String clazz, String methodName, * @return true if the given class, method and parameter has the specified annotation class name */ protected boolean fieldHasAnnotation(String clazz, String fieldName, String annotationClazz) { - if (!hasIndex()) { - return false; - } - ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz)); - if (classByName != null) { - FieldInfo field = classByName.field(fieldName); - if (field != null) { - ClassType classType = retrieveInnerMostType(field.type()); - return classType != null && classType.hasAnnotation(DotName.createSimple(annotationClazz)); + return getFieldAnnotation(clazz, fieldName, annotationClazz) != null; + } + + /** + * Return the {@link AnnotationInstance} for the given field in a class has the specified annotation class name for a generic type. + * + * @param clazz {@link Class} to check for annotation + * @param fieldName field name to check + * @param annotationClazz the annotation {@link Class} to check + * @return {@link AnnotationInstance} or null if none present + */ + protected AnnotationInstance getFieldAnnotation(String clazz, String fieldName, String annotationClazz) { + if (hasIndex()) { + ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz)); + if (classByName != null) { + FieldInfo field = classByName.field(fieldName); + if (field != null) { + ClassType classType = retrieveInnerMostType(field.type()); + if (classType != null) { + return classType.annotations().stream() + .filter(a -> a.name().equals(DotName.createSimple(annotationClazz))) + .findFirst().orElse(null); + } + } } } - return false; + return null; } /** @@ -175,18 +211,34 @@ protected boolean fieldHasAnnotation(String clazz, String fieldName, String anno * @return true if the given class, method and parameter has the specified annotation class name */ protected boolean methodHasAnnotation(String clazz, String methodName, String annotationClazz) { - if (!hasIndex()) { - return false; - } - ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz)); - if (classByName != null) { - MethodInfo method = classByName.firstMethod(methodName); - if (method != null) { - ClassType classType = retrieveInnerMostType(method.returnType()); - return classType != null && classType.hasAnnotation(DotName.createSimple(annotationClazz)); + return getMethodAnnotation(clazz, methodName, annotationClazz) != null; + } + + /** + * Return the {@link AnnotationInstance} for the given method in a class has the specified annotation class name or a generic. + * + * @param clazz {@link Class} to check for annotation + * @param methodName method name to check + * @param annotationClazz the annotation {@link Class} to check + * @return {@link AnnotationInstance} or null if none present + */ + protected AnnotationInstance getMethodAnnotation(String clazz, String methodName, String annotationClazz) { + if (hasIndex()) { + ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz)); + if (classByName != null) { + MethodInfo method = classByName.firstMethod(methodName); + if (method != null) { + ClassType classType = retrieveInnerMostType(method.returnType()); + if (classType == null) { + return null; + } + return classType.annotations().stream() + .filter(a -> a.name().equals(DotName.createSimple(annotationClazz))) + .findFirst().orElse(null); + } } } - return false; + return null; } /** diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index 7affd808c59..d87e49d8737 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -71,10 +71,15 @@ public class SchemaFieldDefinition private DataFetcher dataFetcher; /** - * Original argument type before it was converted to a GraphQL representation. + * Original type before it was converted to a GraphQL representation. */ private Class originalType; + /** + * Original array inner type if it is array type. + */ + private Class originalArrayType; + /** * Defines the format for a number or date. */ @@ -300,6 +305,24 @@ public Class getOriginalType() { return originalType; } + /** + * Sets the original array type. + * + * @param originalArrayType the original array type + */ + public void setOriginalArrayType(Class originalArrayType) { + this.originalArrayType = originalArrayType; + } + + /** + * Returns the original array type. + * + * @return the original array type + */ + public Class getOriginalArrayType() { + return originalArrayType; + } + /** * Return if the array return type is mandatory. * @return if the array return type is mandatory @@ -327,6 +350,7 @@ public String toString() { + ", listArguments=" + listSchemaArguments + ", arrayLevels=" + arrayLevels + ", originalType=" + originalType + + ", originalArrayType=" + originalArrayType + ", format=" + Arrays.toString(format) + ", description='" + getDescription() + '\'' + '}'; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 442d57f110e..4c85f8486ca 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -45,6 +45,7 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetcherFactories; import graphql.schema.PropertyDataFetcher; + import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Id; @@ -56,9 +57,13 @@ import org.eclipse.microprofile.graphql.Query; import org.eclipse.microprofile.graphql.Source; import org.eclipse.microprofile.graphql.Type; +import org.jboss.jandex.AnnotationInstance; import static io.helidon.microprofile.graphql.server.FormattingHelper.DATE; +import static io.helidon.microprofile.graphql.server.FormattingHelper.NO_FORMATTING; import static io.helidon.microprofile.graphql.server.FormattingHelper.NUMBER; +import static io.helidon.microprofile.graphql.server.FormattingHelper.formatDate; +import static io.helidon.microprofile.graphql.server.FormattingHelper.formatNumber; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectDateFormatter; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectNumberFormat; import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormattingAnnotation; @@ -143,6 +148,8 @@ public class SchemaGenerator { /** * Construct a {@link SchemaGenerator} instance. + * + * @param context the {@link Context} to use */ public SchemaGenerator(Context context) { this.context = context; @@ -236,7 +243,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) addTypeToSchema(schema, type); if (type.isInterface()) { - // is an interface so check for any implementors and add them to + // is an interface so check for any implementors and add them too jandexUtils.getKnownImplementors(clazz.getName()).forEach(c -> setUnresolvedTypes.add(c.getName())); } } else if (inputAnnotation != null) { @@ -338,17 +345,32 @@ private void processDefaultDateTimeValues(Schema schema) { // as default formatting has already been dealt with if (isDateTimeScalar(returnType) && fd.getDataFetcher() == null) { String[] existingFormat = fd.getFormat(); - String[] newFormat = ensureFormat(returnType, fd.getOriginalType().getName(), existingFormat); + // check if this type is an array type and if so then get the actual original type + Class clazzOriginalType = fd.getOriginalArrayType() != null + ? fd.getOriginalArrayType() : fd.getOriginalType(); + String[] newFormat = ensureFormat(returnType, clazzOriginalType.getName(), existingFormat); if (!Arrays.equals(newFormat, existingFormat) && newFormat.length == 2) { // formats differ so set the new format and DataFetcher fd.setFormat(newFormat); // create the raw array to pass to the retrieveFormattingDataFetcher method DataFetcher dataFetcher = retrieveFormattingDataFetcher(new String[] {DATE, newFormat[0], newFormat[1] }, - fd.getName(), fd.getOriginalType().getName()); + fd.getName(), clazzOriginalType.getName()); fd.setDataFetcher(dataFetcher); // context.addFormatter(null, fd.getName(), (FormattingProvider) dataFetcher); } } + + // check the SchemaArguments + fd.getArguments().forEach(a -> { + String argumentType = a.getArgumentType(); + if (isDateTimeScalar(argumentType)) { + String[] existingArgFormat = a.getFormat(); + String[] newArgFormat = ensureFormat(argumentType, a.getOriginalType().getName(), existingArgFormat); + if (!Arrays.equals(newArgFormat, existingArgFormat) && newArgFormat.length == 2) { + a.setFormat(newArgFormat); + } + } + }); }); }); } @@ -517,39 +539,37 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, // if the type is a Date/Time/DateTime scalar and there is currently no format, // then use the default format if there is one - if (format.length != 3 && isDateTimeScalar(discoveredMethod.getReturnType())) { + if (format != null && format.length != 3 && isDateTimeScalar(discoveredMethod.getReturnType())) { String[] newFormat = ensureFormat(discoveredMethod.getReturnType(), fd.getOriginalType().getName(), new String[0]); if (newFormat.length == 2) { - format = new String[] { DATE, newFormat[0], newFormat[1] }; + format = new String[] {DATE, newFormat[0], newFormat[1] }; } } - if (format.length == 3) { + if (format != null && format.length == 3) { // a format exists on the method return type so format it after returning the value final String graphQLType = getGraphQLType(fd.getReturnType()); final DataFetcher methodDataFetcher = DataFetcherUtils.newMethodDataFetcher(clazz, method, null, fd.getArguments().toArray( new SchemaArgument[0])); final String[] newFormat = new String[] {format[0], format[1], format[2] }; - if (isDateTimeScalar(discoveredMethod.getReturnType())) { + SchemaScalar dateScalar = getScalar(discoveredMethod.getReturnType()); + if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) { dataFetcher = DataFetcherFactories.wrapDataFetcher(methodDataFetcher, - (e, v) -> { - DateTimeFormatter dateTimeFormatter = - getCorrectDateFormatter( - graphQLType, newFormat[2], newFormat[1]); - return v instanceof TemporalAccessor - ? dateTimeFormatter - .format((TemporalAccessor) v) : null; - }); + (e, v) -> { + DateTimeFormatter dateTimeFormatter = + getCorrectDateFormatter( + graphQLType, newFormat[2], newFormat[1]); + return formatDate(v, dateTimeFormatter); + }); } else { dataFetcher = DataFetcherFactories.wrapDataFetcher(methodDataFetcher, - (e, v) -> { - NumberFormat numberFormat = getCorrectNumberFormat( - graphQLType, newFormat[2], newFormat[1]); - return v != null && numberFormat != null - ? numberFormat.format(v) - : null; - }); + (e, v) -> { + NumberFormat numberFormat = getCorrectNumberFormat( + graphQLType, newFormat[2], newFormat[1]); + boolean isScalar = SchemaGeneratorHelper.getScalar(discoveredMethod.getReturnType()) != null; + return formatNumber(v, isScalar, numberFormat); + }); fd.setReturnType(STRING); } } else { @@ -728,6 +748,7 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth fd.setDataFetcher(dataFetcher); fd.setOriginalType(discoveredMethod.getMethod().getReturnType()); fd.setArrayReturnTypeMandatory(discoveredMethod.isArrayReturnTypeMandatory()); + fd.setOriginalArrayType(isArrayReturnType ? discoveredMethod.getOriginalArrayType() : null); if (format != null && format.length == 3) { fd.setFormat(new String[] { format[1], format[2] }); @@ -798,7 +819,7 @@ private void updateLongTypes(Schema schema, String longReturnType, String shortR * @throws IntrospectionException if there were errors introspecting classes */ protected Map retrieveAllAnnotatedBeanMethods(Class clazz) - throws IntrospectionException { + throws IntrospectionException, ClassNotFoundException { Map mapDiscoveredMethods = new HashMap<>(); for (Method m : getAllMethods(clazz)) { boolean isQuery = m.getAnnotation(Query.class) != null; @@ -833,7 +854,7 @@ protected Map retrieveAllAnnotatedBeanMethods(Class */ protected Map retrieveGetterBeanMethods(Class clazz, boolean isInputType) - throws IntrospectionException { + throws IntrospectionException, ClassNotFoundException { Map mapDiscoveredMethods = new HashMap<>(); for (Method m : getAllMethods(clazz)) { @@ -888,7 +909,7 @@ protected List getAllMethods(Class clazz) throws IntrospectionExcepti */ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, PropertyDescriptor pd, boolean isInputType, - boolean isQueryOrMutation) { + boolean isQueryOrMutation) throws ClassNotFoundException { String[] format = new String[0]; String description = null; boolean isReturnTypeMandatory = false; @@ -1023,7 +1044,15 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, discoveredMethod.setArrayReturnType(realReturnType.isArrayType()); discoveredMethod.setCollectionType(realReturnType.getCollectionType()); discoveredMethod.setMap(realReturnType.isMap()); + SchemaScalar dateScalar = getScalar(realReturnType.getReturnClass()); + if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) { + // only set the original array type if it's a date/time + discoveredMethod.setOriginalArrayType(Class.forName(realReturnType.returnClass)); + } discoveredMethod.setReturnType(realReturnType.getReturnClass()); + if (realReturnType.getFormat() != null) { + discoveredMethod.setFormat(realReturnType.format); + } } else { discoveredMethod.setName(varName); discoveredMethod.setReturnType(returnClazzName); @@ -1159,6 +1188,7 @@ private ReturnType getReturnType(Class returnClazz, java.lang.reflect.Type ge } actualReturnType.setArrayLevels(arrayLevels); actualReturnType.setReturnTypeMandatory(rootTypeResult.isArrayReturnTypeMandatory()); + actualReturnType.setFormat(rootTypeResult.format); } else if (!returnClazzName.isEmpty() && returnClazzName.startsWith("[")) { // return type is array of either primitives or Objects/Interface/Enum. actualReturnType.setArrayType(true); @@ -1186,6 +1216,8 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp int level = 1; boolean isParameter = parameterNumber != -1; String nonNullClazz = NonNull.class.getName(); + String[] format = NO_FORMATTING; + boolean isReturnTypeMandatory; if (genericReturnType instanceof ParameterizedType) { ParameterizedType paramReturnType = (ParameterizedType) genericReturnType; @@ -1196,26 +1228,32 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp ParameterizedType parameterizedType2 = (ParameterizedType) actualTypeArgument; actualTypeArgument = parameterizedType2.getActualTypeArguments()[index]; } + Class clazz = actualTypeArgument.getClass(); boolean hasAnnotation = false; + AnnotationInstance formatInstance = null; + String clazzName = method.getDeclaringClass().getName(); + String methodName = method.getName(); if (jandexUtils.hasIndex()) { if (isParameter) { hasAnnotation = jandexUtils - .methodParameterHasAnnotation(method.getDeclaringClass().getName(), method.getName(), - parameterNumber, nonNullClazz); + .methodParameterHasAnnotation(clazzName, methodName, parameterNumber, nonNullClazz); + format = FormattingHelper.getMethodParameterFormat(jandexUtils, clazzName, methodName, + parameterNumber); } else { - hasAnnotation = jandexUtils.methodHasAnnotation(method.getDeclaringClass().getName(), - method.getName(), nonNullClazz); + hasAnnotation = jandexUtils.methodHasAnnotation(clazzName, methodName, nonNullClazz); + format = FormattingHelper.getMethodFormat(jandexUtils, clazzName, methodName); } } isReturnTypeMandatory = hasAnnotation || isPrimitive(clazz.getName()); - return new RootTypeResult(((Class) actualTypeArgument).getName(), level, isReturnTypeMandatory); + return new RootTypeResult(((Class) actualTypeArgument).getName(), level, isReturnTypeMandatory, format); } else { Class clazz = genericReturnType.getClass(); isReturnTypeMandatory = clazz.getAnnotation(NonNull.class) != null || isPrimitive(clazz.getName()); - return new RootTypeResult(((Class) genericReturnType).getName(), level, isReturnTypeMandatory); +// format = FormattingHelper.getMethodFormat(jandexUtils, clazzName, methodName); + return new RootTypeResult(((Class) genericReturnType).getName(), level, isReturnTypeMandatory, format); } } @@ -1322,6 +1360,11 @@ public static class DiscoveredMethod { */ private boolean isArrayReturnTypeMandatory; + /** + * Original array inner type if it is array type. + */ + private Class originalArrayType; + /** * Default constructor. */ @@ -1643,6 +1686,24 @@ public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { isArrayReturnTypeMandatory = arrayReturnTypeMandatory; } + /** + * Sets the original array type. + * + * @param originalArrayType the original array type + */ + public void setOriginalArrayType(Class originalArrayType) { + this.originalArrayType = originalArrayType; + } + + /** + * Returns the original array type. + * + * @return the original array type + */ + public Class getOriginalArrayType() { + return originalArrayType; + } + @Override public String toString() { return "DiscoveredMethod{" @@ -1659,6 +1720,7 @@ public String toString() { + ", isReturnTypeMandatory=" + isReturnTypeMandatory + ", isArrayReturnTypeMandatory=" + isArrayReturnTypeMandatory + ", description=" + description + + ", originalArrayType=" + originalArrayType + ", defaultValue=" + defaultValue + ", method=" + method + '}'; } @@ -1731,6 +1793,11 @@ public static class ReturnType { */ private boolean isReturnTypeMandatory; + /** + * The format of the return type if any. + */ + private String[] format; + /** * Default constructor. */ @@ -1844,6 +1911,22 @@ public boolean isReturnTypeMandatory() { public void setReturnTypeMandatory(boolean returnTypeMandatory) { isReturnTypeMandatory = returnTypeMandatory; } + + /** + * Return the format of the result class. + * @return the format of the result class + */ + public String[] getFormat() { + return format; + } + + /** + * Set the format of the result class. + * @param format the format of the result class + */ + public void setFormat(String[] format) { + this.format = format; + } } /** @@ -1866,17 +1949,24 @@ public static class RootTypeResult { */ private boolean isArrayReturnTypeMandatory; + /** + * The format of the result class. + */ + private final String[] format; + /** * Construct a root type result. * * @param rootTypeName root type of the {@link Collection} or {@link Map} * @param isArrayReturnTypeMandatory indicates if the return type is mandatory * @param levels number of levels in total + * @param format format of the result class */ - public RootTypeResult(String rootTypeName, int levels, boolean isArrayReturnTypeMandatory) { + public RootTypeResult(String rootTypeName, int levels, boolean isArrayReturnTypeMandatory, String[] format) { this.rootTypeName = rootTypeName; this.levels = levels; this.isArrayReturnTypeMandatory = isArrayReturnTypeMandatory; + this.format = format; } /** @@ -1914,5 +2004,13 @@ public boolean isArrayReturnTypeMandatory() { public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { isArrayReturnTypeMandatory = arrayReturnTypeMandatory; } + + /** + * Return the format of the result class. + * @return the format of the result class + */ + public String[] getFormat() { + return format; + } } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index f70391d1b78..f4e84a9f5ce 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -54,8 +54,11 @@ import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_BIGDECIMAL_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_BIGINTEGER_SCALAR; +import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_FLOAT_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_INT_SCALAR; +import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.ElementGenerator.OPEN_SQUARE; import static io.helidon.microprofile.graphql.server.FormattingHelper.getDefaultDateTimeFormat; import static io.helidon.microprofile.graphql.server.SchemaGenerator.GET; @@ -236,22 +239,22 @@ public final class SchemaGeneratorHelper { // Time scalars put(OffsetTime.class.getName(), - new SchemaScalar(TIME_SCALAR, OFFSET_TIME_CLASS, CustomTimeScalar.INSTANCE, "HH:mm:ssZ")); + new SchemaScalar(TIME_SCALAR, OFFSET_TIME_CLASS, CUSTOM_TIME_SCALAR, "HH:mm:ssZ")); put(LocalTime.class.getName(), - new SchemaScalar(TIME_SCALAR, LOCAL_TIME_CLASS, CustomTimeScalar.INSTANCE, "HH:mm:ss")); + new SchemaScalar(TIME_SCALAR, LOCAL_TIME_CLASS, CUSTOM_TIME_SCALAR, "HH:mm:ss")); // DateTime scalars put(OFFSET_DATE_TIME_CLASS, - new SchemaScalar(DATETIME_SCALAR, OFFSET_DATE_TIME_CLASS, CustomDateTimeScalar.INSTANCE, + new SchemaScalar(DATETIME_SCALAR, OFFSET_DATE_TIME_CLASS, CUSTOM_DATE_TIME_SCALAR, "yyyy-MM-dd'T'HH:mm:ssZ")); put(ZONED_DATE_TIME_CLASS, - new SchemaScalar(DATETIME_SCALAR, ZONED_DATE_TIME_CLASS, CustomDateTimeScalar.INSTANCE, + new SchemaScalar(DATETIME_SCALAR, ZONED_DATE_TIME_CLASS, CUSTOM_DATE_TIME_SCALAR, "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'")); put(LOCAL_DATE_TIME_CLASS, - new SchemaScalar(DATETIME_SCALAR, LOCAL_DATE_TIME_CLASS, CustomDateTimeScalar.INSTANCE, "yyyy-MM-dd'T'HH:mm:ss")); + new SchemaScalar(DATETIME_SCALAR, LOCAL_DATE_TIME_CLASS, CUSTOM_DATE_TIME_SCALAR, "yyyy-MM-dd'T'HH:mm:ss")); // Date scalar - put(LOCAL_DATE_CLASS, new SchemaScalar(DATE_SCALAR, LOCAL_DATE_CLASS, ExtendedScalars.Date, "yyyy-MM-dd")); + put(LOCAL_DATE_CLASS, new SchemaScalar(DATE_SCALAR, LOCAL_DATE_CLASS, CUSTOM_DATE_SCALAR, "yyyy-MM-dd")); // BigDecimal scalars put(BIG_DECIMAL_CLASS, new SchemaScalar(BIG_DECIMAL, BIG_DECIMAL_CLASS, CUSTOM_BIGDECIMAL_SCALAR, null)); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java index 7c16d8ba510..f101c938bbf 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java @@ -134,7 +134,6 @@ protected SchemaPrinter getSchemaPrinter() { if (schemaPrinter == null) { SchemaPrinter.Options options = SchemaPrinter.Options .defaultOptions().includeDirectives(false) - .includeExtendedScalarTypes(true) .includeScalarTypes(true); schemaPrinter = new SchemaPrinter(options); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index e120170efe7..10a4c449cc8 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -29,6 +29,7 @@ import io.helidon.microprofile.graphql.server.test.queries.DuplicateNameQueries; import io.helidon.microprofile.graphql.server.test.queries.PropertyNameQueries; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueryDateTest; import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes; import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty; @@ -343,7 +344,22 @@ public void testNumberFormats() throws IOException { } @Test - // @Disabled + public void interimTest() throws IOException { + setupIndex(indexFileName, SimpleQueryDateTest.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Schema schema = executionContext.getSchema(); + + Map mapResults = getAndAssertResult( + executionContext.execute("query { localDateListFormat }")); + assertThat(mapResults, is(notNullValue())); + List listDates = (ArrayList) mapResults.get("localDateListFormat"); + assertThat(listDates.size(),is(2)); + assertThat(listDates.get(0), is("17/02/1968")); + assertThat(listDates.get(1), is("04/08/1970")); + } + + @Test public void testDateAndTime() throws IOException { setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesNoArgs.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); @@ -373,6 +389,7 @@ public void testDateAndTime() throws IOException { assertDefaultFormat(type, "offsetDateTime", "yyyy-MM-dd'T'HH:mm:ssZ"); assertDefaultFormat(type, "zonedDateTime", "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'"); assertDefaultFormat(type, "localDateNoFormat", "yyyy-MM-dd"); + assertDefaultFormat(type, "significantDates", "yyyy-MM-dd"); fd = getFieldDefinition(type, "localDateTime"); assertThat(fd, is(notNullValue())); @@ -381,16 +398,30 @@ public void testDateAndTime() throws IOException { Map mapResults = getAndAssertResult( executionContext.execute("query { dateAndTimePOJOQuery { offsetDateTime offsetTime zonedDateTime " - + "localDate localDate2 localTime localDateTime } }")); + + "localDate localDate2 localTime localDateTime significantDates } }")); assertThat(mapResults.size(), is(1)); Map mapResults2 = (Map) mapResults.get("dateAndTimePOJOQuery"); assertThat(mapResults2, is(notNullValue())); - assertThat(mapResults2.size(), is(7)); + assertThat(mapResults2.size(), is(8)); assertThat(mapResults2.get("localDate"), is("02/17/1968")); assertThat(mapResults2.get("localDate2"), is("08/04/1970")); assertThat(mapResults2.get("localTime"), is("10:10:20")); assertThat(mapResults2.get("offsetTime"), is("08:10:01+0000")); + Object significantDates = mapResults2.get("significantDates"); + assertThat(significantDates, is(notNullValue())); + List listDates = (ArrayList) mapResults2.get("significantDates"); + assertThat(listDates.size(),is(2)); + assertThat(listDates.get(0), is("1968-02-17")); + assertThat(listDates.get(1), is("1970-08-04")); + + mapResults = getAndAssertResult( + executionContext.execute("query { localDateListFormat }")); + assertThat(mapResults, is(notNullValue())); + listDates = (ArrayList) mapResults.get("localDateListFormat"); + assertThat(listDates.size(),is(2)); + assertThat(listDates.get(0), is("17/02/1968")); + assertThat(listDates.get(1), is("04/08/1970")); } @Test @@ -418,7 +449,7 @@ public void testNulls() throws IOException { assertReturnTypeMandatory(type, "testInputOnly", false); assertArrayReturnTypeMandatory(type, "testInputOnly", false); assertReturnTypeMandatory(type, "testOutputOnly", false); - assertArrayReturnTypeMandatory(type, "testOutputOnly", true); + assertArrayReturnTypeMandatory(type, "testOutputOnly", false); SchemaType query = schema.getTypeByName("Query"); assertReturnTypeMandatory(query, "method1NotNull", true); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index b802d74950e..d3f3c9cabbc 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -122,7 +122,7 @@ public void testEnumGeneration() throws IntrospectionException, ClassNotFoundExc } @Test - public void testGettersPerson() throws IntrospectionException { + public void testGettersPerson() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Person.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(13)); @@ -144,7 +144,7 @@ public void testGettersPerson() throws IntrospectionException { } @Test - public void testMultipleLevels() throws IntrospectionException { + public void testMultipleLevels() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Level0.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); @@ -153,7 +153,7 @@ public void testMultipleLevels() throws IntrospectionException { } @Test - public void testTypeWithIdOnMethod() throws IntrospectionException { + public void testTypeWithIdOnMethod() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(TypeWithIdOnMethod.class, false); assertThat(mapMethods, is(notNullValue())); @@ -163,7 +163,7 @@ public void testTypeWithIdOnMethod() throws IntrospectionException { } @Test - public void testTypeWithIdOnField() throws IntrospectionException { + public void testTypeWithIdOnField() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(TypeWithIdOnField.class, false); assertThat(mapMethods, is(notNullValue())); @@ -173,7 +173,7 @@ public void testTypeWithIdOnField() throws IntrospectionException { } @Test - public void testTypeWithNameAndJsonbProperty() throws IntrospectionException { + public void testTypeWithNameAndJsonbProperty() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(TypeWithNameAndJsonbProperty.class, false); assertThat(mapMethods, is(notNullValue())); @@ -187,7 +187,7 @@ public void testTypeWithNameAndJsonbProperty() throws IntrospectionException { } @Test - public void testInterfaceDiscovery() throws IntrospectionException { + public void testInterfaceDiscovery() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Vehicle.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(6)); @@ -201,7 +201,7 @@ public void testInterfaceDiscovery() throws IntrospectionException { } @Test - public void testInterfaceImplementorDiscovery1() throws IntrospectionException { + public void testInterfaceImplementorDiscovery1() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Car.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); @@ -216,7 +216,7 @@ public void testInterfaceImplementorDiscovery1() throws IntrospectionException { } @Test - public void testInterfaceImplementorDiscovery2() throws IntrospectionException { + public void testInterfaceImplementorDiscovery2() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Motorbike.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); @@ -231,7 +231,7 @@ public void testInterfaceImplementorDiscovery2() throws IntrospectionException { } @Test - public void testObjectWithIgnorableFields() throws IntrospectionException { + public void testObjectWithIgnorableFields() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator.retrieveGetterBeanMethods( ObjectWithIgnorableFieldsAndMethods.class, false); assertThat(mapMethods, is(notNullValue())); @@ -280,11 +280,11 @@ public void testGetSimpleName() throws ClassNotFoundException { } @Test - public void testAllMethods() throws IntrospectionException { + public void testAllMethods() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator .retrieveAllAnnotatedBeanMethods(SimpleQueriesNoArgs.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(14)); + assertThat(mapMethods.size(), is(15)); assertDiscoveredMethod(mapMethods.get("hero"), "hero", STRING, null, false, false, false); assertDiscoveredMethod(mapMethods.get("episodeCount"), "episodeCount", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("numberOfStars"), "numberOfStars", Long.class.getName(), null, false, false, false); @@ -328,7 +328,7 @@ public void testDefaultDescription() { } @Test - public void testArrayDiscoveredMethods() throws IntrospectionException { + public void testArrayDiscoveredMethods() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class, false); assertThat(mapMethods, is(notNullValue())); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithNulls.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithNulls.java index 86737b8cd7b..49bd0bbbcc6 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithNulls.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithNulls.java @@ -27,7 +27,7 @@ import org.eclipse.microprofile.graphql.Query; /** - * Class that holds queries that also have Mut. + * Class that holds queries that also have Null. */ @GraphQLApi @ApplicationScoped diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index a11b4724674..efa5e93dad7 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.UUID; import javax.enterprise.context.ApplicationScoped; @@ -42,6 +43,7 @@ import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; +import org.eclipse.microprofile.graphql.DateFormat; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Id; import org.eclipse.microprofile.graphql.Name; @@ -157,6 +159,12 @@ public DateTimePojo dateTimePojo() { LocalDate.of(1970,8,4), LocalTime.of(10,10,20), OffsetTime.of(8, 10, 1, 0, ZoneOffset.UTC), - LocalDateTime.now(), OffsetDateTime.now(), ZonedDateTime.now(), LocalDate.now()); + LocalDateTime.now(), OffsetDateTime.now(), ZonedDateTime.now(), LocalDate.now(), + List.of(LocalDate.of(1968,2,17), LocalDate.of(1970,8,4))); + } + + @Query("localDateListFormat") + public List<@DateFormat("dd/MM/yyyy") LocalDate> getLocalDateListFormat() { + return List.of(LocalDate.of(1968,2,17), LocalDate.of(1970,8,4)); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueryDateTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueryDateTest.java new file mode 100644 index 00000000000..4463330b7e5 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueryDateTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server.test.queries; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; +import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; +import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; +import io.helidon.microprofile.graphql.server.test.types.Person; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; +import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.json.bind.annotation.JsonbProperty; +import org.eclipse.microprofile.graphql.DateFormat; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Id; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds simple query definitions with no-argument. + */ +@GraphQLApi +@ApplicationScoped +public class SimpleQueryDateTest { + + @Inject + private TestDB testDB; + + public SimpleQueryDateTest() { + } + + @Query("localDateListFormat") + public List<@DateFormat("dd/MM/yyyy") LocalDate> getLocalDateListFormat() { + return List.of(LocalDate.of(1968,2,17), LocalDate.of(1970,8,4)); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java index 0f7a26de00c..4137d180faa 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java @@ -22,6 +22,9 @@ import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import javax.json.bind.annotation.JsonbDateFormat; @@ -50,6 +53,8 @@ public class DateTimePojo { @Description("description") private LocalDate localDateNoFormat; + private List significantDates; + public DateTimePojo(LocalDate localDate, LocalDate localDate2, LocalTime localTime, @@ -57,7 +62,8 @@ public DateTimePojo(LocalDate localDate, LocalDateTime localDateTime, OffsetDateTime offsetDateTime, ZonedDateTime zonedDateTime, - LocalDate localDateNoFormat) { + LocalDate localDateNoFormat, + List significantDates) { this.localDate = localDate; this.localDate2 = localDate2; this.localTime = localTime; @@ -66,6 +72,7 @@ public DateTimePojo(LocalDate localDate, this.offsetDateTime = offsetDateTime; this.zonedDateTime = zonedDateTime; this.localDateNoFormat = localDateNoFormat; + this.significantDates = significantDates; } public LocalDate getLocalDate() { @@ -131,4 +138,12 @@ public LocalDate getLocalDateNoFormat() { public void setLocalDateNoFormat(LocalDate localDateNoFormat) { this.localDateNoFormat = localDateNoFormat; } + + public void setSignificantDates(List significantDates) { + this.significantDates = significantDates; + } + + public List getSignificantDates() { + return this.significantDates; + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java index 99b14363b13..55dcfcd7bad 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java @@ -18,9 +18,12 @@ import javax.json.bind.annotation.JsonbNumberFormat; +import org.eclipse.microprofile.graphql.DateFormat; import org.eclipse.microprofile.graphql.NumberFormat; import org.eclipse.microprofile.graphql.Type; import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; /** * Defines a simple contact which contains number formats. @@ -45,6 +48,8 @@ public class SimpleContactWithNumberFormats { @NumberFormat("BigDecimal-##########") private BigDecimal bigDecimal; + private List<@DateFormat("DD-MM-YYYY") LocalDate> listDates; + public Integer getFormatMethod(@NumberFormat("0 'years old'") int age) { return age; } From b68d2ec3ec570ecd30813e442991e82286507910 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 3 Sep 2020 14:15:33 +0800 Subject: [PATCH 075/178] progress - still issues with argument conversion --- .../test/queries/SimpleQueryDateTest.java | 65 ------------------- 1 file changed, 65 deletions(-) delete mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueryDateTest.java diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueryDateTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueryDateTest.java deleted file mode 100644 index 4463330b7e5..00000000000 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueryDateTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2020 Oracle and/or its affiliates. - * - * 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 io.helidon.microprofile.graphql.server.test.queries; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.OffsetTime; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import io.helidon.microprofile.graphql.server.test.db.TestDB; -import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; -import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; -import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; -import io.helidon.microprofile.graphql.server.test.types.Person; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; -import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import javax.json.bind.annotation.JsonbProperty; -import org.eclipse.microprofile.graphql.DateFormat; -import org.eclipse.microprofile.graphql.GraphQLApi; -import org.eclipse.microprofile.graphql.Id; -import org.eclipse.microprofile.graphql.Name; -import org.eclipse.microprofile.graphql.Query; - -/** - * Class that holds simple query definitions with no-argument. - */ -@GraphQLApi -@ApplicationScoped -public class SimpleQueryDateTest { - - @Inject - private TestDB testDB; - - public SimpleQueryDateTest() { - } - - @Query("localDateListFormat") - public List<@DateFormat("dd/MM/yyyy") LocalDate> getLocalDateListFormat() { - return List.of(LocalDate.of(1968,2,17), LocalDate.of(1970,8,4)); - } -} From 72a54ffc7d878b24f489ac113d0864ef6952bd28 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 7 Sep 2020 07:52:53 +0800 Subject: [PATCH 076/178] progress --- .../graphql/server/DataFetcherUtils.java | 3 +- .../graphql/server/FormattingHelper.java | 11 ++---- .../graphql/server/JandexUtils.java | 12 +++---- .../graphql/server/SchemaGenerator.java | 35 +++++++++++++++---- .../graphql/server/SchemaGeneratorHelper.java | 1 - .../graphql/server/SchemaGeneratorIT.java | 24 +++---------- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 459af7b15e1..33bf7bd598b 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -44,6 +44,7 @@ import static io.helidon.microprofile.graphql.server.FormattingHelper.formatNumber; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectDateFormatter; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectNumberFormat; +import static io.helidon.microprofile.graphql.server.SchemaGenerator.isFormatEmpty; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isDateTimeScalar; @@ -112,7 +113,7 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met } else { // check the format and convert it from a string to the original format String[] format = argument.getFormat(); - if (format != null && format.length == 2) { + if (!isFormatEmpty(format)) { // TODO: This always returns false, need to fix if (isDateTimeScalar(argument.getArgumentType())) { DateTimeFormatter dateFormatter = getCorrectDateFormatter(originalType.getName(), diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java index 7fffe363590..ab34529bac2 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java @@ -428,17 +428,10 @@ public static Object formatNumber(Object originalResult, boolean isScalar, Numbe if (originalResult instanceof Collection) { Collection formattedResult = new ArrayList(); Collection originalCollection = (Collection) originalResult; - originalCollection.forEach(e -> { - if (isScalar) { - formattedResult.add(new FormattedNumberImpl(numberFormat, numberFormat.format(originalResult))); - } else { - formattedResult.add(numberFormat.format(originalResult)); - } - }); + originalCollection.forEach(e -> formattedResult.add(numberFormat.format(originalResult))); return formattedResult; } - return isScalar ? new FormattedNumberImpl(numberFormat, numberFormat.format(originalResult)) - : numberFormat.format(originalResult); + return numberFormat.format(originalResult); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java index 29981923514..0a4a36035fb 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java @@ -155,9 +155,10 @@ protected AnnotationInstance getMethodParameterAnnotation(String clazz, String m MethodInfo methodInfo = classByName.firstMethod(methodName); if (methodInfo != null) { ClassType classType = retrieveInnerMostType(methodInfo.parameters().get(paramNumber)); - return classType.annotations().stream() - .filter(a -> a.name().equals(DotName.createSimple(annotationClazz))) - .findFirst().orElse(null); + return classType == null ? null + : classType.annotations().stream() + .filter(a -> a.name().equals(DotName.createSimple(annotationClazz))) + .findFirst().orElse(null); } } } @@ -191,11 +192,10 @@ protected AnnotationInstance getFieldAnnotation(String clazz, String fieldName, FieldInfo field = classByName.field(fieldName); if (field != null) { ClassType classType = retrieveInnerMostType(field.type()); - if (classType != null) { - return classType.annotations().stream() + return classType == null ? null + : classType.annotations().stream() .filter(a -> a.name().equals(DotName.createSimple(annotationClazz))) .findFirst().orElse(null); - } } } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 4c85f8486ca..c158e2a1bb2 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -527,6 +527,9 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, } a.setArgumentType(inputType.getName()); } + + // in either case, get the argument format + } if (fd != null) { fd.addArgument(a); @@ -536,24 +539,26 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, if (fd != null) { DataFetcher dataFetcher = null; String[] format = discoveredMethod.getFormat(); + SchemaScalar dateScalar = getScalar(discoveredMethod.getReturnType()); // if the type is a Date/Time/DateTime scalar and there is currently no format, // then use the default format if there is one - if (format != null && format.length != 3 && isDateTimeScalar(discoveredMethod.getReturnType())) { - String[] newFormat = ensureFormat(discoveredMethod.getReturnType(), - fd.getOriginalType().getName(), new String[0]); + if ((dateScalar != null && isDateTimeScalar(dateScalar.getName()) && isFormatEmpty(format))) { + Class originalType = fd.isArrayReturnType() ? fd.getOriginalArrayType() : fd.getOriginalType(); + String[] newFormat = ensureFormat(dateScalar.getName(), + originalType.getName(), new String[2]); if (newFormat.length == 2) { format = new String[] {DATE, newFormat[0], newFormat[1] }; } } - if (format != null && format.length == 3) { + if (!isFormatEmpty(format)) { // a format exists on the method return type so format it after returning the value final String graphQLType = getGraphQLType(fd.getReturnType()); final DataFetcher methodDataFetcher = DataFetcherUtils.newMethodDataFetcher(clazz, method, null, fd.getArguments().toArray( new SchemaArgument[0])); final String[] newFormat = new String[] {format[0], format[1], format[2] }; - SchemaScalar dateScalar = getScalar(discoveredMethod.getReturnType()); + if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) { dataFetcher = DataFetcherFactories.wrapDataFetcher(methodDataFetcher, (e, v) -> { @@ -596,6 +601,24 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, } } + /** + * Returns true if the format is empty or undefined. + * @param format format to check + * @return true if the format is empty or undefined + */ + protected static boolean isFormatEmpty(String[] format) { + if (format == null || format.length == 0) { + return true; + } + // now check that each value is not null + for (String entry : format) { + if (entry == null) { + return true; + } + } + return false; + } + /** * Check this new {@link SchemaInputType} contains any types, then they must also have InputTypes created for them if they are * not enums or scalars. @@ -726,7 +749,7 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth // check for format on the property // note: currently the format will be an array of [3] as defined by FormattingHelper.getFormattingAnnotation String[] format = discoveredMethod.getFormat(); - if (propertyName != null && format != null && format.length == 3 && format[0] != null) { + if (propertyName != null && !isFormatEmpty(format)) { if (!isGraphQLType(valueClassName)) { dataFetcher = retrieveFormattingDataFetcher(format, propertyName, graphQLType); context.addFormatter(discoveredMethod.method, propertyName, (FormattingProvider) dataFetcher); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index f4e84a9f5ce..c417039441b 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -38,7 +38,6 @@ import javax.json.bind.annotation.JsonbProperty; import javax.json.bind.annotation.JsonbTransient; -import graphql.Scalars; import graphql.scalars.ExtendedScalars; import org.eclipse.microprofile.graphql.DefaultValue; import org.eclipse.microprofile.graphql.Description; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index 10a4c449cc8..d2c81357ca9 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -29,7 +29,7 @@ import io.helidon.microprofile.graphql.server.test.queries.DuplicateNameQueries; import io.helidon.microprofile.graphql.server.test.queries.PropertyNameQueries; -import io.helidon.microprofile.graphql.server.test.queries.SimpleQueryDateTest; + import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes; import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty; @@ -74,7 +74,7 @@ import io.helidon.microprofile.graphql.server.test.types.VehicleIncident; import org.eclipse.microprofile.graphql.NonNull; -import org.junit.jupiter.api.Disabled; + import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; @@ -343,22 +343,6 @@ public void testNumberFormats() throws IOException { // assertThat(mapResults2, is(notNullValue())); } - @Test - public void interimTest() throws IOException { - setupIndex(indexFileName, SimpleQueryDateTest.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Schema schema = executionContext.getSchema(); - - Map mapResults = getAndAssertResult( - executionContext.execute("query { localDateListFormat }")); - assertThat(mapResults, is(notNullValue())); - List listDates = (ArrayList) mapResults.get("localDateListFormat"); - assertThat(listDates.size(),is(2)); - assertThat(listDates.get(0), is("17/02/1968")); - assertThat(listDates.get(1), is("04/08/1970")); - } - @Test public void testDateAndTime() throws IOException { setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesNoArgs.class); @@ -449,7 +433,7 @@ public void testNulls() throws IOException { assertReturnTypeMandatory(type, "testInputOnly", false); assertArrayReturnTypeMandatory(type, "testInputOnly", false); assertReturnTypeMandatory(type, "testOutputOnly", false); - assertArrayReturnTypeMandatory(type, "testOutputOnly", false); + assertArrayReturnTypeMandatory(type, "testOutputOnly", true); SchemaType query = schema.getTypeByName("Query"); assertReturnTypeMandatory(query, "method1NotNull", true); @@ -840,7 +824,7 @@ public void testMultiLevelListsAndArraysQueries() throws IOException { @SuppressWarnings("unchecked") public void testSimpleQueryGenerationWithArgs() throws IOException { setupIndex(indexFileName, SimpleQueriesWithArgs.class, Car.class, AbstractVehicle.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); + ExecutionContext executionContext = new ExecutionContext(defaultContext); Map mapResults = getAndAssertResult(executionContext.execute("query { hero(heroType: \"human\") }")); assertThat(mapResults.size(), is(1)); From 2997fae7527ac8941cb1cbdc85af3d789674f3cf Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 9 Sep 2020 07:44:27 +0800 Subject: [PATCH 077/178] progress --- microprofile/graphql/pom.xml | 2 +- microprofile/graphql/server/pom.xml | 2 +- .../graphql/server/CustomScalars.java | 186 +++++++++++------- .../graphql/server/DataFetcherUtils.java | 61 +++--- .../graphql/server/DescriptiveElement.java | 11 ++ .../graphql/server/FormattingHelper.java | 5 +- .../graphql/server/SchemaArgument.java | 85 +++++++- .../graphql/server/SchemaFieldDefinition.java | 11 -- .../graphql/server/SchemaGenerator.java | 17 +- .../graphql/server/SchemaArgumentTest.java | 30 +++ .../graphql/server/SchemaGeneratorIT.java | 4 + .../NumberFormatQueriesAndMutations.java | 15 ++ microprofile/tests/tck/tck-graphql/pom.xml | 2 +- 13 files changed, 303 insertions(+), 128 deletions(-) diff --git a/microprofile/graphql/pom.xml b/microprofile/graphql/pom.xml index e71ed13a869..bdff7fde4de 100644 --- a/microprofile/graphql/pom.xml +++ b/microprofile/graphql/pom.xml @@ -20,7 +20,7 @@ io.helidon.microprofile helidon-microprofile-project - 2.0.2-SNAPSHOT + 2.0.3-SNAPSHOT 4.0.0 diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 2f207d456c5..5ede0c9610d 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -20,7 +20,7 @@ io.helidon.microprofile.graphql helidon-microprofile-graphql - 2.0.2-SNAPSHOT + 2.0.3-SNAPSHOT 4.0.0 diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java index cfec09b19f5..355e226af73 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java @@ -20,7 +20,6 @@ import java.math.BigInteger; import java.text.NumberFormat; import java.text.ParseException; -import java.time.LocalDate; import java.time.OffsetDateTime; import java.time.OffsetTime; @@ -29,6 +28,7 @@ import static graphql.Scalars.GraphQLInt; import graphql.Scalars; +import graphql.language.StringValue; import graphql.scalars.ExtendedScalars; import graphql.schema.Coercing; import graphql.schema.CoercingParseLiteralException; @@ -47,14 +47,46 @@ public class CustomScalars { private CustomScalars() { } + /** + * An instance of a custome BigDecimal Scalar. + */ public static final GraphQLScalarType CUSTOM_BIGDECIMAL_SCALAR = newCustomBigDecimalScalar(); + + /** + * An instance of a custom Int scalar. + */ public static final GraphQLScalarType CUSTOM_INT_SCALAR = newCustomGraphQLInt(); + + /** + * An instance of a custom Float scalar. + */ public static final GraphQLScalarType CUSTOM_FLOAT_SCALAR = newCustomGraphQLFloat(); + + /** + * An instance of a custom BigInteger scalar. + */ public static final GraphQLScalarType CUSTOM_BIGINTEGER_SCALAR = newCustomGraphQLBigInteger(); + + /** + * An instance of a custom date/time scalar. + */ public static final GraphQLScalarType CUSTOM_DATE_TIME_SCALAR = newDateTimeScalar(); + + /** + * An instance of a custom time scalar. + */ public static final GraphQLScalarType CUSTOM_TIME_SCALAR = newTimeScalar(); + + /** + * An instance of a custom date scalar. + */ public static final GraphQLScalarType CUSTOM_DATE_SCALAR = newDateScalar(); + /** + * Return a new custom date/time scalar. + * + * @return a new custom date/time scalar + */ public static GraphQLScalarType newDateTimeScalar() { GraphQLScalarType originalScalar = ExtendedScalars.DateTime; Coercing originalCoercing = originalScalar.getCoercing(); @@ -72,17 +104,21 @@ public OffsetDateTime parseValue(Object input) throws CoercingParseValueExceptio return null; } - @Override public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralException { return null; } }) - .name("DateTime") - .description("A Custom RFC-3339 compliant DateTime Scalar") + .name(originalScalar.getName()) + .description("Custom: " + originalScalar.getDescription()) .build(); } + /** + * Return a new custom time scalar. + * + * @return a new custom time scalar + */ public static GraphQLScalarType newTimeScalar() { GraphQLScalarType originalScalar = ExtendedScalars.Time; Coercing originalCoercing = originalScalar.getCoercing(); @@ -104,15 +140,21 @@ public OffsetTime parseLiteral(Object input) throws CoercingParseLiteralExceptio return null; } }) - .name("Time") - .description("A Custom RFC-3339 compliant Time Scalar") + .name(originalScalar.getName()) + .description("Custom: " + originalScalar.getDescription()) .build(); } + /** + * Return a new custom date scalar. + * + * @return a new custom date scalar + */ + @SuppressWarnings("unchecked") public static GraphQLScalarType newDateScalar() { GraphQLScalarType originalScalar = ExtendedScalars.Date; Coercing originalCoercing = originalScalar.getCoercing(); - return GraphQLScalarType.newScalar().coercing(new Coercing() { + return GraphQLScalarType.newScalar().coercing(new Coercing() { public String serialize(Object dataFetcherResult) throws CoercingSerializeException { return dataFetcherResult instanceof String ? (String) dataFetcherResult @@ -120,33 +162,33 @@ public String serialize(Object dataFetcherResult) throws CoercingSerializeExcept } @Override - public LocalDate parseValue(Object input) throws CoercingParseValueException { - return null; + public Object parseValue(Object input) throws CoercingParseValueException { + return input instanceof StringValue ? ((StringValue) input).getValue() + : originalCoercing.parseValue(input); } - @Override - public LocalDate parseLiteral(Object input) throws CoercingParseLiteralException { - return null; + public Object parseLiteral(Object input) throws CoercingParseLiteralException { + return input instanceof StringValue ? ((StringValue) input).getValue() + : originalCoercing.parseLiteral(input); } }) - .name("Date") - .description("A Custom RFC-3339 compliant Date Scalar") + .name(originalScalar.getName()) + .description("Custom: " + originalScalar.getDescription()) .build(); } /** - * Creates a custom BigDecimalScalar which will parse a formatted value which was originally formatted using a {@link - * NumberFormat}. + * Return a new custom BigDecimal scalar. * - * @return a custom BigDecimalScalar + * @return a new custom BigDecimal scalar */ @SuppressWarnings("unchecked") private static GraphQLScalarType newCustomBigDecimalScalar() { GraphQLScalarType originalScalar = Scalars.GraphQLBigDecimal; Coercing originalCoercing = originalScalar.getCoercing(); - Coercing formattingCoercing = new Coercing<>() { + return GraphQLScalarType.newScalar().coercing(new Coercing<>() { @Override public Object serialize(Object dataFetcherResult) throws CoercingSerializeException { return dataFetcherResult instanceof String @@ -163,27 +205,27 @@ public BigDecimal parseValue(Object input) throws CoercingParseValueException { public BigDecimal parseLiteral(Object input) throws CoercingParseLiteralException { return originalCoercing.parseLiteral(input); } - }; - - return GraphQLScalarType.newScalar() - .description("Custom: " + originalScalar.getDescription()) - .definition(originalScalar.getDefinition()) - .name(originalScalar.getName()) - .coercing(formattingCoercing) - .build(); + }) + .name(originalScalar.getName()) + .description("Custom: " + originalScalar.getDescription()) + .build(); } + /** + * Return a new custom Int scalar. + * + * @return a new custom Int scalar + */ + @SuppressWarnings("unchecked") private static GraphQLScalarType newCustomGraphQLInt() { - GraphQLScalarType intScalar = GraphQLInt; - Coercing originalCoercing = intScalar.getCoercing(); - Coercing formattingCoercing = new Coercing<>() { + GraphQLScalarType originalScalar = GraphQLInt; + Coercing originalCoercing = originalScalar.getCoercing(); + return GraphQLScalarType.newScalar().coercing(new Coercing<>() { @Override public Object serialize(Object dataFetcherResult) throws CoercingSerializeException { - Object finalDataFetcherResult = dataFetcherResult; - if (dataFetcherResult instanceof FormattedNumber) { - return ((FormattedNumber) finalDataFetcherResult).getFormattedValue(); - } - return originalCoercing.serialize(finalDataFetcherResult); + return dataFetcherResult instanceof String + ? (String) dataFetcherResult + : originalCoercing.serialize(dataFetcherResult); } @Override @@ -195,27 +237,27 @@ public Integer parseValue(Object input) throws CoercingParseValueException { public Integer parseLiteral(Object input) throws CoercingParseLiteralException { return originalCoercing.parseLiteral(input); } - }; - - return GraphQLScalarType.newScalar() - .description("Custom: " + intScalar.getDescription()) - .definition(intScalar.getDefinition()) - .name(intScalar.getName()) - .coercing(formattingCoercing) - .build(); + }) + .name(originalScalar.getName()) + .description("Custom: " + originalScalar.getDescription()) + .build(); } + /** + * Return a new custom Float scalar. + * + * @return a new custom Float scalar + */ + @SuppressWarnings("unchecked") private static GraphQLScalarType newCustomGraphQLFloat() { - GraphQLScalarType floatScalar = GraphQLFloat; - Coercing originalCoercing = floatScalar.getCoercing(); - Coercing formattingCoercing = new Coercing<>() { + GraphQLScalarType originalScalar = GraphQLFloat; + Coercing originalCoercing = originalScalar.getCoercing(); + return GraphQLScalarType.newScalar().coercing(new Coercing<>() { @Override public Object serialize(Object dataFetcherResult) throws CoercingSerializeException { - Object finalDataFetcherResult = dataFetcherResult; - if (dataFetcherResult instanceof FormattedNumber) { - return ((FormattedNumber) finalDataFetcherResult).getFormattedValue(); - } - return originalCoercing.serialize(finalDataFetcherResult); + return dataFetcherResult instanceof String + ? (String) dataFetcherResult + : originalCoercing.serialize(dataFetcherResult); } @Override @@ -227,27 +269,27 @@ public Double parseValue(Object input) throws CoercingParseValueException { public Double parseLiteral(Object input) throws CoercingParseLiteralException { return originalCoercing.parseLiteral(input); } - }; - - return GraphQLScalarType.newScalar() - .description("Custom: " + floatScalar.getDescription()) - .definition(floatScalar.getDefinition()) - .name(floatScalar.getName()) - .coercing(formattingCoercing) - .build(); + }) + .name(originalScalar.getName()) + .description("Custom: " + originalScalar.getDescription()) + .build(); } + /** + * Return a new custom BigInteger scalar. + * + * @return a new custom BigInteger scalar + */ + @SuppressWarnings("unchecked") private static GraphQLScalarType newCustomGraphQLBigInteger() { - GraphQLScalarType bigIntegerScalar = GraphQLBigInteger; - Coercing originalCoercing = bigIntegerScalar.getCoercing(); - Coercing formattingCoercing = new Coercing<>() { + GraphQLScalarType originalScalar = GraphQLBigInteger; + Coercing originalCoercing = originalScalar.getCoercing(); + return GraphQLScalarType.newScalar().coercing(new Coercing<>() { @Override public Object serialize(Object dataFetcherResult) throws CoercingSerializeException { - Object finalDataFetcherResult = dataFetcherResult; - if (dataFetcherResult instanceof FormattedNumber) { - return ((FormattedNumber) finalDataFetcherResult).getFormattedValue(); - } - return originalCoercing.serialize(finalDataFetcherResult); + return dataFetcherResult instanceof String + ? (String) dataFetcherResult + : originalCoercing.serialize(dataFetcherResult); } @Override @@ -259,14 +301,10 @@ public BigInteger parseValue(Object input) throws CoercingParseValueException { public BigInteger parseLiteral(Object input) throws CoercingParseLiteralException { return originalCoercing.parseLiteral(input); } - }; - - return GraphQLScalarType.newScalar() - .description("Custom: " + bigIntegerScalar.getDescription()) - .definition(bigIntegerScalar.getDefinition()) - .name(bigIntegerScalar.getName()) - .coercing(formattingCoercing) - .build(); + }) + .name(originalScalar.getName()) + .description("Custom: " + originalScalar.getDescription()) + .build(); } private static Object parserNumberFormat(Object dataFetcherResult) { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 33bf7bd598b..4a451a55711 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -22,10 +22,14 @@ import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; + import java.time.temporal.TemporalAccessor; import java.util.ArrayList; -import java.util.Collection; import java.util.Locale; import java.util.Map; import java.util.UUID; @@ -114,11 +118,12 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met // check the format and convert it from a string to the original format String[] format = argument.getFormat(); if (!isFormatEmpty(format)) { - // TODO: This always returns false, need to fix + // TODO: This always returns false, need to fix ?? if (isDateTimeScalar(argument.getArgumentType())) { DateTimeFormatter dateFormatter = getCorrectDateFormatter(originalType.getName(), format[1], format[0]); - listArgumentValues.add(dateFormatter.parse(key.toString())); + listArgumentValues.add( + getOriginalDateTimeValue(originalType, dateFormatter.parse(key.toString()))); } else { NumberFormat numberFormat = getCorrectNumberFormat(originalType.getName(), format[1], format[0]); @@ -230,35 +235,6 @@ public DateTimeFormatter getDateTimeFormat() { } } - - /** - * Create a new {@link DataFetcher} which formats a date/time. - * - * @param propertyName property to extract - * @param valueFormat formatting value - * @param locale formatting locale - * @param type of the source - * @return a new {@link DataFetcher} - */ - public static DataFetcher newDateFormatPropertyDataFetcher(String propertyName, - String valueFormat, String locale) { - Locale actualLocale = SchemaGeneratorHelper.DEFAULT_LOCALE.equals(locale) - ? Locale.getDefault() - : Locale.forLanguageTag(locale); - DateFormat dateFormat = new SimpleDateFormat(valueFormat, actualLocale); - - return environment -> { - S source = environment.getSource(); - if (source == null) { - return null; - } - Object rawValue = PropertyDataFetcherHelper - .getPropertyValue(propertyName, source, environment.getFieldType(), environment); - - return rawValue != null ? dateFormat.format(rawValue) : null; - }; - } - /** * Convert the ID type back to the original type for the method call. * @@ -277,4 +253,25 @@ private static Object getOriginalValue(Class originalType, String key) { return key; } } + + /** + * Return the original date/time value. + * + * @param originalType original type + * @param value the {@link TemporalAccessor} value + * @return the original date/time value + */ + private static TemporalAccessor getOriginalDateTimeValue(Class originalType, TemporalAccessor value) { + if (originalType.equals(LocalDateTime.class)) { + return LocalDateTime.from(value); + } else if (originalType.equals(LocalDate.class)) { + return LocalDate.from(value); + } else if (originalType.equals(ZonedDateTime.class)) { + return ZonedDateTime.from(value); + } else if (originalType.equals(OffsetDateTime.class)) { + return OffsetDateTime.from(value); + } else { + return null; + } + } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java index 5b3c10fda25..8cae23cac67 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java @@ -65,4 +65,15 @@ default String getSchemaElementDescription(String[] format) { } return NOTHING; } + + /** + * Repeat a {@link String} with the value repeated the requested number of times. + * + * @param count number of times to repeat + * @param string {@link String} to repeat + * @return a new {@link String} + */ + default String repeat(int count, String string) { + return new String(new char[count]).replace("\0", string); + } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java index ab34529bac2..6ebdace4b98 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java @@ -258,6 +258,7 @@ protected static String[] getFormattingAnnotation(AnnotatedElement annotatedElem protected static String[] getMethodParameterFormat(JandexUtils jandexUtils, String clazz, String methodName, int paramNumber) { + LOGGER.finest("getMethodParameterFormat(): Class = " + clazz + ", method = " + methodName + ", paramNumber = " + paramNumber); if (jandexUtils.hasIndex()) { AnnotationInstance dateFormat1 = jandexUtils.getMethodParameterAnnotation(clazz, methodName, paramNumber, JSONB_DATE_FORMAT); @@ -286,8 +287,8 @@ private static String[] getFormatFromAnnotationInstance(AnnotationInstance dateF AnnotationInstance numberFormat1, AnnotationInstance numberFormat2) { // ensure that both date and number formatting are not present - if (dateFormat1 != null || dateFormat2 != null && - numberFormat1 != null || numberFormat2 != null) { + if ((dateFormat1 != null || dateFormat2 != null) + && (numberFormat1 != null || numberFormat2 != null)) { ensureRuntimeException(LOGGER, "Cannot have date and number formatting on the same method"); } AnnotationInstance formatInstance = dateFormat1 != null ? dateFormat1 diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index 30a014deb5a..565939c6be6 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -16,6 +16,7 @@ package io.helidon.microprofile.graphql.server; +import java.util.Arrays; import java.util.Objects; @@ -60,6 +61,21 @@ public class SchemaArgument */ private String[] format; + /** + * Indicates if the return type is an array type such as a native array([]) or a List, Collection, etc. + */ + private boolean isArrayReturnType; + + /** + * The number of array levels if return type is an array. + */ + private int arrayLevels; + + /** + * Indicates if the return type is mandatory. + */ + private boolean isArrayReturnTypeMandatory; + /** * Construct a {@link SchemaArgument} instance. * @@ -87,9 +103,17 @@ public SchemaArgument(String argumentName, String argumentType, public String getSchemaAsString() { StringBuilder sb = new StringBuilder(getSchemaElementDescription(getFormat())) .append(getArgumentName()) - .append(COLON) - .append(SPACER) - .append(getArgumentType()); + .append(COLON); + + if (isArrayReturnType()) { + int count = getArrayLevels(); + sb.append(SPACER).append(repeat(count, OPEN_SQUARE)) + .append(getArgumentType()) + .append(isArrayReturnTypeMandatory() ? MANDATORY : NOTHING) + .append(repeat(count, CLOSE_SQUARE)); + } else { + sb.append(SPACER).append(getArgumentType()); + } if (isMandatory) { sb.append(MANDATORY); @@ -201,6 +225,56 @@ public void setFormat(String[] format) { this.format = format; } + /** + * Set the number of array levels if return type is an array. + * + * @param arrayLevels the number of array levels if return type is an array + */ + public void setArrayLevels(int arrayLevels) { + this.arrayLevels = arrayLevels; + } + + /** + * Return the number of array levels if return type is an array. + * + * @return the number of array levels if return type is an array + */ + public int getArrayLevels() { + return arrayLevels; + } + + /** + * Set if the return type is an array type. + * @param isArrayReturnType if the return type is an array type + */ + public void setArrayReturnType(boolean isArrayReturnType) { + this.isArrayReturnType = isArrayReturnType; + } + + /** + * Indicates if the return type is an array type. + * + * @return if the return type is an array type + */ + public boolean isArrayReturnType() { + return isArrayReturnType; + } + + /** + * Indicates if the array return type is mandatory. + * @return if the array return type is mandatory + */ + public boolean isArrayReturnTypeMandatory() { + return isArrayReturnTypeMandatory; + } + + /** + * Sets if the array return type is mandatory. + * @param arrayReturnTypeMandatory if the array return type is mandatory + */ + public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { + isArrayReturnTypeMandatory = arrayReturnTypeMandatory; + } @Override public String toString() { return "Argument{" @@ -210,7 +284,10 @@ public String toString() { + ", defaultValue=" + defaultValue + ", originalType=" + originalType + ", sourceArgument=" + sourceArgument - + ", format=" + format + + ", isReturnTypeMandatory=" + isArrayReturnTypeMandatory + + ", isArrayReturnType=" + isArrayReturnType + + ", arrayLevels=" + arrayLevels + + ", format=" + Arrays.toString(format) + ", description='" + getDescription() + '\'' + '}'; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index d87e49d8737..bd5db2d6e17 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -150,17 +150,6 @@ public String getSchemaAsString() { return sb.toString(); } - /** - * Repeate create a {@link String} with the value repeated the requested number of times. - * - * @param count number of times to repeat - * @param string {@link String} to repeat - * @return a new {@link String} - */ - private String repeat(int count, String string) { - return new String(new char[count]).replace("\0", string); - } - /** * Return the name for the field definition. * diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index c158e2a1bb2..800fee015f8 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -27,7 +27,7 @@ import java.lang.reflect.ParameterizedType; import java.text.NumberFormat; import java.time.format.DateTimeFormatter; -import java.time.temporal.TemporalAccessor; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -1104,6 +1104,7 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM java.lang.reflect.Type[] genericParameterTypes = method.getGenericParameterTypes(); int i = 0; for (Parameter parameter : parameters) { + boolean isID = false; Name paramNameAnnotation = parameter.getAnnotation(Name.class); String parameterName = paramNameAnnotation != null && !paramNameAnnotation.value().isBlank() @@ -1117,6 +1118,7 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM if (parameter.getAnnotation(Id.class) != null) { validateIDClass(returnType.getReturnClass()); returnType.setReturnClass(ID); + isID = true; } String argumentDefaultValue = getDefaultValueAnnotationValue(parameter); @@ -1144,6 +1146,17 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM argument.setSourceArgument(true); } + if (!isID) { + SchemaScalar dateScalar = getScalar(returnType.getReturnClass()); + if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) { + // only set the original array type if it's a date/time + // discoveredMethod.setOriginalArrayType(Class.forName(returnType.returnClass)); + } + argument.setArrayReturnTypeMandatory(returnType.isReturnTypeMandatory); + argument.setArrayReturnType(returnType.isArrayType); + argument.setArrayLevels(returnType.getArrayLevels()); + } + discoveredMethod.addArgument(argument); } } @@ -1212,12 +1225,12 @@ private ReturnType getReturnType(Class returnClazz, java.lang.reflect.Type ge actualReturnType.setArrayLevels(arrayLevels); actualReturnType.setReturnTypeMandatory(rootTypeResult.isArrayReturnTypeMandatory()); actualReturnType.setFormat(rootTypeResult.format); + actualReturnType.setArrayType(true); } else if (!returnClazzName.isEmpty() && returnClazzName.startsWith("[")) { // return type is array of either primitives or Objects/Interface/Enum. actualReturnType.setArrayType(true); actualReturnType.setArrayLevels(getArrayLevels(returnClazzName)); actualReturnType.setReturnClass(getRootArrayClass(returnClazzName)); - } else { // primitive or type actualReturnType.setReturnClass(returnClazzName); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index 048aff1600b..80181f33936 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -73,6 +73,14 @@ public void testConstructors() { assertThat(schemaArgument.getDefaultValue(), is("1")); } + @Test + public void testSchemaArgumentArrayTypes() { + SchemaArgument schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); + assertThat(schemaArgument.isArrayReturnType(), is(false)); + assertThat(schemaArgument.isArrayReturnTypeMandatory(), is(false)); + assertThat(schemaArgument.getArrayLevels(), is(0)); + } + @Test public void testSchemaGeneration() { SchemaArgument schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); @@ -96,5 +104,27 @@ public void testSchemaGeneration() { schemaArgument = new SchemaArgument("name", "Int", false, 10, INTEGER); schemaArgument.setDescription("Description"); assertThat(schemaArgument.getSchemaAsString(), is("\"Description\"\nname: Int = 10")); + + // test array return types + schemaArgument = new SchemaArgument("name", "Int", false, null, INTEGER); + schemaArgument.setArrayReturnType(true); + schemaArgument.setArrayLevels(1); + assertThat(schemaArgument.getSchemaAsString(), is("name: [Int]")); + + schemaArgument.setArrayReturnTypeMandatory(true); + assertThat(schemaArgument.getSchemaAsString(), is("name: [Int!]")); + + schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); + schemaArgument.setArrayReturnType(true); + schemaArgument.setArrayLevels(1); + schemaArgument.setArrayReturnTypeMandatory(true); + assertThat(schemaArgument.getSchemaAsString(), is("name: [Int!]!")); + + schemaArgument = new SchemaArgument("name", "String", true, "Hello", STRING); + schemaArgument.setArrayReturnType(true); + schemaArgument.setArrayLevels(3); + schemaArgument.setArrayReturnTypeMandatory(true); + assertThat(schemaArgument.getSchemaAsString(), is("name: [[[String!]]]! = \"Hello\"")); + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index d2c81357ca9..89bc5e3fe0a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -323,6 +323,10 @@ public void testNumberFormats() throws IOException { assertThat(mapResults, is(notNullValue())); assertThat(mapResults.get("echoBigDecimalUsingFormat"), is(BigDecimal.valueOf(123))); + // COH-21891 + mapResults = getAndAssertResult(executionContext.execute("query { listAsString(arg1: [ [ \"value 12.12\", \"value 33.33\"] ] ) }")); + assertThat(mapResults, is(notNullValue())); + // create a new contact // String contactInput = // "contact: {" diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java index c7c719e6fb4..658cd8b459d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java @@ -20,12 +20,16 @@ import javax.enterprise.context.ApplicationScoped; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; +import javax.json.bind.annotation.JsonbNumberFormat; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.NumberFormat; import org.eclipse.microprofile.graphql.Query; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; /** * Class that holds queries and mutations that have various formatting types. @@ -62,4 +66,15 @@ public int echoNumberUnformatted(@Name("number") @NumberFormat("ID-#########") i public BigDecimal echoBigDecimalUsingFormat(@Name("param1") @NumberFormat("BD-####") BigDecimal param1) { return param1; } + + @Query + public List getListAsString(@Name("arg1") + @JsonbNumberFormat("ignore 00.0000000") + List> values) { + if (values != null) { + return values.stream().map(Object::toString).collect(Collectors.toList()); + } + + return new ArrayList<>(); + } } diff --git a/microprofile/tests/tck/tck-graphql/pom.xml b/microprofile/tests/tck/tck-graphql/pom.xml index e34660856ff..e45a3772ba5 100644 --- a/microprofile/tests/tck/tck-graphql/pom.xml +++ b/microprofile/tests/tck/tck-graphql/pom.xml @@ -21,7 +21,7 @@ io.helidon.microprofile.tests tck-project - 2.0.0-SNAPSHOT + 2.0.3-SNAPSHOT tck-graphql Helidon Microprofile Tests TCK GraphQL From 499ae627955b3ebfeeaa3c41b2ba973ed936ee52 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 9 Sep 2020 08:16:52 +0800 Subject: [PATCH 078/178] Fixup arrays and revert HelidonDeployableContainer for Tomas --- dependencies/pom.xml | 2 +- .../graphql/server/SchemaGeneratorTest.java | 14 +- .../HelidonDeployableContainer.java | 221 +++++++----------- 3 files changed, 92 insertions(+), 145 deletions(-) diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 3a488a99570..0813b01f3d2 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -53,7 +53,7 @@ 2.3.3 20.1.0 15.0 - 1.1.0 + 1.0.1 1.27.1 1.32.1 28.1-jre diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index d3f3c9cabbc..bb546aefa6d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -132,12 +132,12 @@ public void testGettersPerson() throws IntrospectionException, ClassNotFoundExce assertDiscoveredMethod(mapMethods.get("homeAddress"), "homeAddress", ADDRESS, null, false, false, false); assertDiscoveredMethod(mapMethods.get("workAddress"), "workAddress", ADDRESS, null, false, false, false); assertDiscoveredMethod(mapMethods.get("creditLimit"), "creditLimit", BIGDECIMAL, null, false, false, false); - assertDiscoveredMethod(mapMethods.get("listQualifications"), "listQualifications", STRING, COLLECTION, false, true, + assertDiscoveredMethod(mapMethods.get("listQualifications"), "listQualifications", STRING, COLLECTION, true, true, false); - assertDiscoveredMethod(mapMethods.get("previousAddresses"), "previousAddresses", ADDRESS, LIST, false, true, false); + assertDiscoveredMethod(mapMethods.get("previousAddresses"), "previousAddresses", ADDRESS, LIST, true, true, false); assertDiscoveredMethod(mapMethods.get("intArray"), "intArray", "int", null, true, false, false); assertDiscoveredMethod(mapMethods.get("stringArray"), "stringArray", STRING, null, true, false, false); - assertDiscoveredMethod(mapMethods.get("addressMap"), "addressMap", ADDRESS, null, false, false, true); + assertDiscoveredMethod(mapMethods.get("addressMap"), "addressMap", ADDRESS, null, true, false, true); assertDiscoveredMethod(mapMethods.get("localDate"), "localDate", LOCALDATE, null, false, false, false); assertDiscoveredMethod(mapMethods.get("longValue"), "longValue", long.class.getName(), null, false, false, false); assertDiscoveredMethod(mapMethods.get("bigDecimal"), "bigDecimal", BigDecimal.class.getName(), null, false, false, false); @@ -196,7 +196,7 @@ public void testInterfaceDiscovery() throws IntrospectionException, ClassNotFoun assertDiscoveredMethod(mapMethods.get("model"), "model", STRING, null, false, false, false); assertDiscoveredMethod(mapMethods.get("numberOfWheels"), "numberOfWheels", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("manufactureYear"), "manufactureYear", "int", null, false, false, false); - assertDiscoveredMethod(mapMethods.get("incidents"), "incidents", VehicleIncident.class.getName(), COLLECTION, false, true, + assertDiscoveredMethod(mapMethods.get("incidents"), "incidents", VehicleIncident.class.getName(), COLLECTION, true, true, false); } @@ -211,7 +211,7 @@ public void testInterfaceImplementorDiscovery1() throws IntrospectionException, assertDiscoveredMethod(mapMethods.get("numberOfWheels"), "numberOfWheels", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("manufactureYear"), "manufactureYear", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("numberOfDoors"), "numberOfDoors", "int", null, false, false, false); - assertDiscoveredMethod(mapMethods.get("incidents"), "incidents", VehicleIncident.class.getName(), COLLECTION, false, true, + assertDiscoveredMethod(mapMethods.get("incidents"), "incidents", VehicleIncident.class.getName(), COLLECTION, true, true, false); } @@ -226,7 +226,7 @@ public void testInterfaceImplementorDiscovery2() throws IntrospectionException, assertDiscoveredMethod(mapMethods.get("numberOfWheels"), "numberOfWheels", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("manufactureYear"), "manufactureYear", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("hasSideCar"), "hasSideCar", "boolean", null, false, false, false); - assertDiscoveredMethod(mapMethods.get("incidents"), "incidents", VehicleIncident.class.getName(), COLLECTION, false, true, + assertDiscoveredMethod(mapMethods.get("incidents"), "incidents", VehicleIncident.class.getName(), COLLECTION, true, true, false); } @@ -289,7 +289,7 @@ public void testAllMethods() throws IntrospectionException, ClassNotFoundExcepti assertDiscoveredMethod(mapMethods.get("episodeCount"), "episodeCount", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("numberOfStars"), "numberOfStars", Long.class.getName(), null, false, false, false); assertDiscoveredMethod(mapMethods.get("badGuy"), "badGuy", STRING, null, false, false, false); - assertDiscoveredMethod(mapMethods.get("allPeople"), "allPeople", Person.class.getName(), COLLECTION, false, true, false); + assertDiscoveredMethod(mapMethods.get("allPeople"), "allPeople", Person.class.getName(), COLLECTION, true, true, false); assertDiscoveredMethod(mapMethods.get("returnCurrentDate"), "returnCurrentDate", LocalDate.class.getName(), null, false, false, false); assertDiscoveredMethod(mapMethods.get("returnMediumSize"), "returnMediumSize", EnumTestWithEnumName.class.getName(), null, diff --git a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java index 09104aa1f28..c8b36daabe0 100644 --- a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java +++ b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java @@ -22,40 +22,33 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; -import java.util.Enumeration; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Properties; -import java.util.Set; -import java.util.TreeSet; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.function.Consumer; -import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.enterprise.inject.se.SeContainer; import javax.enterprise.inject.spi.CDI; import javax.enterprise.inject.spi.DefinitionException; -import io.helidon.config.Config; -import io.helidon.config.ConfigSources; -import io.helidon.config.spi.ConfigSource; +import io.helidon.config.mp.MpConfigSources; import io.helidon.microprofile.server.Server; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.eclipse.microprofile.config.spi.ConfigSource; import org.jboss.arquillian.container.spi.client.container.DeployableContainer; import org.jboss.arquillian.container.spi.client.container.DeploymentException; import org.jboss.arquillian.container.spi.client.protocol.ProtocolDescription; @@ -86,6 +79,8 @@ */ public class HelidonDeployableContainer implements DeployableContainer { private static final Logger LOGGER = Logger.getLogger(HelidonDeployableContainer.class.getName()); + // runnables that must be executed on stop + private static final ConcurrentLinkedQueue STOP_RUNNABLES = new ConcurrentLinkedQueue<>(); /** * The configuration for this container. @@ -97,7 +92,6 @@ public class HelidonDeployableContainer implements DeployableContainer contexts = new HashMap<>(); - private static ConcurrentLinkedQueue stopCalls = new ConcurrentLinkedQueue<>(); private Server dummyServer = null; @Override @@ -112,18 +106,15 @@ public void setup(HelidonContainerConfiguration configuration) { @Override public void start() { - dummyServer = Server.builder().build(); } @Override public void stop() { - // No-op } @Override public ProtocolDescription getDefaultProtocol() { return new ProtocolDescription(HelidonLocalProtocol.PROTOCOL_NAME); - // return new ProtocolDescription(LocalProtocol.NAME); } @Override @@ -144,53 +135,30 @@ public ProtocolMetaData deploy(Archive archive) throws DeploymentException { } LOGGER.info("Running Arquillian tests in directory: " + context.deployDir.toAbsolutePath()); - // Copy the archive into deployDir. Save off the class names for all classes included in the - // "classes" dir. Later I will visit each of these and see if they are JAX-RS Resources or - // Applications, so I can add those to the Server automatically. - final Set classNames = new TreeSet<>(); - copyArchiveToDeployDir(archive, context.deployDir, p -> { - if (p.endsWith(".class")) { - final int prefixLength = isJavaArchive ? 1 : "/WEB-INF/classes/".length(); - classNames.add(p.substring(prefixLength, p.lastIndexOf(".class")).replace('/', '.')); - } - }); - - // If the configuration specified a Resource to load, add that to the set of class names - if (containerConfig.getResource() != null) { - classNames.add(containerConfig.getResource()); - } + copyArchiveToDeployDir(archive, context.deployDir); - // If the configuration specified an Application to load, add that to the set of class names. - // The "Main" method (see Main.template) will go through all these classes and discover whether - // they are apps or resources and call the right builder methods on the Server.Builder. - if (containerConfig.getApp() != null) { - classNames.add(containerConfig.getApp()); - } - - URL[] classPath; + List classPath = new ArrayList<>(); Path rootDir = context.deployDir.resolve(""); if (isJavaArchive) { ensureBeansXml(rootDir); - classPath = new URL[] { - rootDir.toUri().toURL() - }; + classPath.add(rootDir); } else { // Prepare the launcher files Path webInfDir = context.deployDir.resolve("WEB-INF"); Path classesDir = webInfDir.resolve("classes"); Path libDir = webInfDir.resolve("lib"); ensureBeansXml(classesDir); - classPath = getServerClasspath(classesDir, libDir, rootDir); + addServerClasspath(classPath, classesDir, libDir, rootDir); } - startServer(context, classPath, classNames); + startServer(context, classPath.toArray(new Path[0])); } catch (IOException e) { LOGGER.log(Level.INFO, "Failed to start container", e); throw new DeploymentException("Failed to copy the archive assets into the deployment directory", e); } catch (ReflectiveOperationException e) { LOGGER.log(Level.INFO, "Failed to start container", e); - throw new DefinitionException(e.getCause()); // validation exceptions + throw new DefinitionException(e); // validation exceptions } // Server has started, so we're done. @@ -200,7 +168,7 @@ public ProtocolMetaData deploy(Archive archive) throws DeploymentException { return new ProtocolMetaData(); } - void startServer(RunContext context, URL[] classPath, Set classNames) + void startServer(RunContext context, Path[] classPath) throws ReflectiveOperationException { try { @@ -211,43 +179,11 @@ void startServer(RunContext context, URL[] classPath, Set classNames) // there is no server running } - context.classLoader = new MyClassloader(containerConfig.getExcludeArchivePattern(), new URLClassLoader(classPath)); + context.classLoader = new MyClassloader(new URLClassLoader(toUrls(classPath))); context.oldClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(context.classLoader); - List> configSources = new LinkedList<>(); - configSources.add(ConfigSources.file(context.deployDir.resolve("META-INF/microprofile-config.properties").toString()) - .optional()); - // The following line supports MP OpenAPI, which allows an alternate - // location for the config file. - configSources.add(ConfigSources.file( - context.deployDir.resolve("WEB-INF/classes/META-INF/microprofile-config.properties").toString()) - .optional()); - configSources.add(ConfigSources.file(context.deployDir.resolve("arquillian.properties").toString()).optional()); - configSources.add(ConfigSources.file(context.deployDir.resolve("application.properties").toString()).optional()); - configSources.add(ConfigSources.file(context.deployDir.resolve("application.yaml").toString()).optional()); - configSources.add(ConfigSources.classpath("tck-application.yaml").optional()); - - // workaround for tck-fault-tolerance - if (containerConfig.getReplaceConfigSourcesWithMp()) { - URL mpConfigProps = context.classLoader.getResource("META-INF/microprofile-config.properties"); - if (mpConfigProps != null) { - try { - Properties props = new Properties(); - props.load(mpConfigProps.openStream()); - configSources.clear(); - configSources.add(ConfigSources.create(props)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - Config config = Config.builder() - .sources(configSources) - .build(); - context.runnerClass = context.classLoader .loadClass("io.helidon.microprofile.arquillian.ServerRunner"); @@ -255,7 +191,7 @@ void startServer(RunContext context, URL[] classPath, Set classNames) .getDeclaredConstructor() .newInstance(); - stopCalls.add(() -> { + STOP_RUNNABLES.add(() -> { try { context.runnerClass.getDeclaredMethod("stop").invoke(context.runner); } catch (ReflectiveOperationException e) { @@ -263,34 +199,79 @@ void startServer(RunContext context, URL[] classPath, Set classNames) } }); + // Configuration needs to be explicit, as some TCK libraries contain an unfortunate + // META-INF/microprofile-config.properties (such as JWT-Auth) + Config config = ConfigProviderResolver.instance() + .getBuilder() + .withSources(findMpConfigSources(classPath)) + .addDiscoveredConverters() + // will read application.yaml + .addDiscoveredSources() + .build(); + context.runnerClass - .getDeclaredMethod("start", Config.class, HelidonContainerConfiguration.class, Set.class, ClassLoader.class) - .invoke(context.runner, config, containerConfig, classNames, context.classLoader); + .getDeclaredMethod("start", Config.class, Integer.TYPE) + .invoke(context.runner, config, containerConfig.getPort()); + } + + private URL[] toUrls(Path[] classPath) { + List result = new ArrayList<>(); + + for (Path path : classPath) { + try { + result.add(path.toUri().toURL()); + } catch (MalformedURLException e) { + throw new IllegalStateException("Classpath failed to be constructed for path: " + path); + } + } + + return result.toArray(new URL[0]); } - URL[] getServerClasspath(Path classesDir, Path libDir, Path rootDir) throws IOException { - List urls = new ArrayList<>(); + private ConfigSource[] findMpConfigSources(Path[] classPath) { + String location = "META-INF/microprofile-config.properties"; + List sources = new ArrayList<>(5); + for (Path path : classPath) { + if (Files.isDirectory(path)) { + Path mpConfig = path.resolve(location); + if (Files.exists(mpConfig)) { + sources.add(MpConfigSources.create(mpConfig)); + } + } else { + // this must be a jar file (classpath is either jar file or a directory) + FileSystem fs; + try { + fs = FileSystems.newFileSystem(path, Thread.currentThread().getContextClassLoader()); + Path mpConfig = fs.getPath(location); + if (Files.exists(mpConfig)) { + sources.add(MpConfigSources.create(path + "!" + mpConfig, mpConfig)); + } + } catch (IOException e) { + // ignored + } + } + } + + // add the expected sysprops and env vars + sources.add(MpConfigSources.environmentVariables()); + sources.add(MpConfigSources.systemProperties()); + + return sources.toArray(new ConfigSource[0]); + } + + void addServerClasspath(List classpath, Path classesDir, Path libDir, Path rootDir) throws IOException { // classes directory - urls.add(classesDir.toUri().toURL()); + classpath.add(classesDir); // lib directory - need to find each jar file if (Files.exists(libDir)) { Files.list(libDir) .filter(path -> path.getFileName().toString().endsWith(".jar")) - .forEach(path -> { - try { - urls.add(path.toUri().toURL()); - } catch (MalformedURLException e) { - throw new HelidonArquillianException("Failed to get URL from library on path: " - + path.toAbsolutePath(), e); - } - }); + .forEach(classpath::add); } - urls.add(rootDir.toUri().toURL()); - - return urls.toArray(new URL[0]); + classpath.add(rootDir); } private void ensureBeansXml(Path classesDir) throws IOException { @@ -356,10 +337,10 @@ public void undeploy(Archive archive) { } void stopAll() { - Runnable polled = stopCalls.poll(); + Runnable polled = STOP_RUNNABLES.poll(); while (Objects.nonNull(polled)) { polled.run(); - polled = stopCalls.poll(); + polled = STOP_RUNNABLES.poll(); } } @@ -378,11 +359,10 @@ public void undeploy(Descriptor descriptor) { * * @param archive The archive to deploy. This cannot be null. * @param deployDir The directory to deploy to. This cannot be null. - * @param callback The callback to invoke per item. This can be null. * @throws IOException if there is an I/O failure related to copying the archive assets to the deployment * directory. If this happens, the entire setup is bad and must be terminated. */ - private void copyArchiveToDeployDir(Archive archive, Path deployDir, Consumer callback) throws IOException { + private void copyArchiveToDeployDir(Archive archive, Path deployDir) throws IOException { Map archiveContents = archive.getContent(); for (Map.Entry entry : archiveContents.entrySet()) { ArchivePath path = entry.getKey(); @@ -399,11 +379,6 @@ private void copyArchiveToDeployDir(Archive archive, Path deployDir, Consumer } // Copy the asset to the destination location Files.copy(n.getAsset().openStream(), f, StandardCopyOption.REPLACE_EXISTING); - // Invoke the callback if one was registered - String p = n.getPath().get(); - if (callback != null) { - callback.accept(p); - } } } } @@ -434,48 +409,20 @@ private static class RunContext { static class MyClassloader extends ClassLoader implements Closeable { private final URLClassLoader wrapped; - private final Pattern excludePattern; - MyClassloader(String excludeArchivePattern, URLClassLoader wrapped) { + MyClassloader(URLClassLoader wrapped) { super(wrapped); this.wrapped = wrapped; - this.excludePattern = (null == excludeArchivePattern ? null : Pattern.compile(excludeArchivePattern)); } @Override public InputStream getResourceAsStream(String name) { InputStream stream = wrapped.getResourceAsStream(name); if ((null == stream) && name.startsWith("/")) { - return wrapped.getResourceAsStream(name.substring(1)); - } - return stream; - } - - - @Override - public Enumeration getResources(String name) throws IOException { - if (excludePattern == null) { - return wrapped.getResources(name); + stream = wrapped.getResourceAsStream(name.substring(1)); } - if ("META-INF/beans.xml".equals(name)) { - // workaround for graphql tck - need to exclude the TCK jar - Enumeration resources = wrapped.getResources(name); - List theList = new LinkedList<>(); - while (resources.hasMoreElements()) { - URL url = resources.nextElement(); - String ref = url.toString(); - Matcher matcher = excludePattern.matcher(ref); - if (matcher.matches()) { - LOGGER.info("Excluding " + url + " from bean archives."); - } else { - theList.add(url); - } - } - return Collections.enumeration(theList); - } - - return super.getResources(name); + return stream; } @Override From 6f655a90fdae61df60f47a5ac64cf75ffe4f1b7a Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 10 Sep 2020 01:48:54 +0200 Subject: [PATCH 079/178] Graphql tck (#2) * Changes to run TCK test. Signed-off-by: Tomas Langer * Changes to process GraphQLApi without explicit Jandex use (it is used implicitly by Weld). Fix to service loader (must be both in module-info and in META-INF/services) Signed-off-by: Tomas Langer --- .../cdi/HelidonContainerImpl.java | 3 +- .../graphql/server/GraphQLCdiExtension.java | 17 ++++++ .../graphql/server/SchemaGenerator.java | 19 +++---- .../javax.enterprise.inject.spi.Extension | 1 + .../HelidonContainerConfiguration.java | 27 ---------- .../HelidonDeployableContainer.java | 53 +++++++++++++++++-- microprofile/tests/tck/tck-graphql/pom.xml | 24 --------- .../src/test/resources/arquillian.xml | 2 - 8 files changed, 77 insertions(+), 69 deletions(-) create mode 100644 microprofile/graphql/server/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension diff --git a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/HelidonContainerImpl.java b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/HelidonContainerImpl.java index ea7ec286378..ff1f6de86ee 100644 --- a/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/HelidonContainerImpl.java +++ b/microprofile/cdi/src/main/java/io/helidon/microprofile/cdi/HelidonContainerImpl.java @@ -145,7 +145,8 @@ private HelidonContainerImpl init() { addHelidonBeanDefiningAnnotations("javax.ws.rs.Path", "javax.ws.rs.ext.Provider", - "javax.websocket.server.ServerEndpoint"); + "javax.websocket.server.ServerEndpoint", + "org.eclipse.microprofile.graphql.GraphQLApi"); ResourceLoader resourceLoader = new WeldResourceLoader() { @Override diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java index 9ba36ffa5d9..0359b77573c 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java @@ -16,7 +16,24 @@ package io.helidon.microprofile.graphql.server; +import java.util.ArrayList; +import java.util.List; + +import javax.enterprise.event.Observes; import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.ProcessAnnotatedType; +import javax.enterprise.inject.spi.WithAnnotations; + +import org.eclipse.microprofile.graphql.GraphQLApi; public class GraphQLCdiExtension implements Extension { + private final List> collectedApis = new ArrayList<>(); + + void collectApis(@Observes @WithAnnotations(GraphQLApi.class) ProcessAnnotatedType processAnnotatedType) { + this.collectedApis.add(processAnnotatedType.getAnnotatedType().getJavaClass()); + } + + public Class[] collectedApis() { + return collectedApis.toArray(new Class[0]); + } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 800fee015f8..2107e2bc64c 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -27,7 +27,6 @@ import java.lang.reflect.ParameterizedType; import java.text.NumberFormat; import java.time.format.DateTimeFormatter; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -42,10 +41,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.enterprise.inject.spi.CDI; + import graphql.schema.DataFetcher; import graphql.schema.DataFetcherFactories; import graphql.schema.PropertyDataFetcher; - import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Id; @@ -169,17 +169,12 @@ public SchemaGenerator(Context context) { * @return a {@link Schema} */ public Schema generateSchema() throws IntrospectionException, ClassNotFoundException { - if (!jandexUtils.hasIndex()) { - return generateSchemaFromClasses(); - } - - List> listClasses = jandexUtils.getIndex() - .getKnownClasses() - .stream() - .map(ci -> getSafeClass(ci.toString())) - .collect(Collectors.toList()); + Class[] classes = CDI.current() + .getBeanManager() + .getExtension(GraphQLCdiExtension.class) + .collectedApis(); - return generateSchemaFromClasses(listClasses.toArray(new Class[0])); + return generateSchemaFromClasses(classes); } /** diff --git a/microprofile/graphql/server/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/microprofile/graphql/server/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 00000000000..909ccacf5d9 --- /dev/null +++ b/microprofile/graphql/server/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1 @@ +io.helidon.microprofile.graphql.server.GraphQLCdiExtension diff --git a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonContainerConfiguration.java b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonContainerConfiguration.java index 597ed46a960..271f038c00f 100644 --- a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonContainerConfiguration.java +++ b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonContainerConfiguration.java @@ -35,12 +35,9 @@ */ public class HelidonContainerConfiguration implements ContainerConfiguration { private String appClassName = null; - private String resourceClassName = null; private String excludeArchivePattern = null; private int port = 8080; private boolean deleteTmp = true; - private boolean addResourcesToApps = false; - private boolean replaceConfigSourcesWithMp = false; private boolean useRelativePath = false; public String getApp() { @@ -51,14 +48,6 @@ public void setApp(String app) { this.appClassName = app; } - public String getResource() { - return resourceClassName; - } - - public void setResource(String resource) { - this.resourceClassName = resource; - } - public int getPort() { return port; } @@ -83,22 +72,6 @@ public void setUseRelativePath(boolean b) { this.useRelativePath = b; } - public boolean getAddResourcesToApps() { - return addResourcesToApps; - } - - public void setAddResourcesToApps(boolean addResourcesToApps) { - this.addResourcesToApps = addResourcesToApps; - } - - public void setReplaceConfigSourcesWithMp(boolean replaceConfigSourcesWithMp) { - this.replaceConfigSourcesWithMp = replaceConfigSourcesWithMp; - } - - public boolean getReplaceConfigSourcesWithMp() { - return replaceConfigSourcesWithMp; - } - public String getExcludeArchivePattern() { return excludeArchivePattern; } diff --git a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java index c8b36daabe0..07f0dfd4d9e 100644 --- a/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java +++ b/microprofile/tests/arquillian/src/main/java/io/helidon/microprofile/arquillian/HelidonDeployableContainer.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.FileSystem; @@ -29,15 +30,20 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; +import java.util.Enumeration; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Pattern; import javax.enterprise.inject.se.SeContainer; import javax.enterprise.inject.spi.CDI; @@ -86,6 +92,7 @@ public class HelidonDeployableContainer implements DeployableContainer getConfigurationClass() { @Override public void setup(HelidonContainerConfiguration configuration) { this.containerConfig = configuration; + String excludeArchivePattern = configuration.getExcludeArchivePattern(); + if (excludeArchivePattern == null || excludeArchivePattern.isBlank()) { + this.excludedLibrariesPattern = null; + } else { + this.excludedLibrariesPattern = Pattern.compile(excludeArchivePattern); + } } @Override @@ -179,7 +192,7 @@ void startServer(RunContext context, Path[] classPath) // there is no server running } - context.classLoader = new MyClassloader(new URLClassLoader(toUrls(classPath))); + context.classLoader = new MyClassloader(excludedLibrariesPattern, new URLClassLoader(toUrls(classPath), null)); context.oldClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(context.classLoader); @@ -408,13 +421,47 @@ private static class RunContext { } static class MyClassloader extends ClassLoader implements Closeable { + private final Pattern excludedLibrariesPattern; private final URLClassLoader wrapped; - MyClassloader(URLClassLoader wrapped) { - super(wrapped); + MyClassloader(Pattern excludedLibrariesPattern, URLClassLoader wrapped) { + super(Thread.currentThread().getContextClassLoader()); + + this.excludedLibrariesPattern = excludedLibrariesPattern; this.wrapped = wrapped; } + @Override + public Enumeration getResources(String name) throws IOException { + Set result = new LinkedHashSet<>(); + + Enumeration resources = wrapped.getResources(name); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + result.add(url); + } + + resources = super.getResources(name); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + + if (excludedLibrariesPattern == null) { + result.add(url); + } else { + try { + String path = url.toURI().toString().replace('\\', '/'); + if (!excludedLibrariesPattern.matcher(path).matches()) { + result.add(url); + } + } catch (URISyntaxException e) { + result.add(url); + } + } + } + + return Collections.enumeration(result); + } + @Override public InputStream getResourceAsStream(String name) { InputStream stream = wrapped.getResourceAsStream(name); diff --git a/microprofile/tests/tck/tck-graphql/pom.xml b/microprofile/tests/tck/tck-graphql/pom.xml index e45a3772ba5..5e8e9e617de 100644 --- a/microprofile/tests/tck/tck-graphql/pom.xml +++ b/microprofile/tests/tck/tck-graphql/pom.xml @@ -59,30 +59,6 @@ - - org.apache.maven.plugins - maven-dependency-plugin - - - generate-sources - - unpack - - - - - org.eclipse.microprofile.graphql - microprofile-graphql-tck - jar - true - ${project.build.directory}/test-classes - - - **/tests/,**/dynamic/,**/*Test.class,**/beans.xml - - - - org.jboss.jandex jandex-maven-plugin diff --git a/microprofile/tests/tck/tck-graphql/src/test/resources/arquillian.xml b/microprofile/tests/tck/tck-graphql/src/test/resources/arquillian.xml index 4b1a0e63585..be2a4691b73 100644 --- a/microprofile/tests/tck/tck-graphql/src/test/resources/arquillian.xml +++ b/microprofile/tests/tck/tck-graphql/src/test/resources/arquillian.xml @@ -27,8 +27,6 @@ - false - true true .*/microprofile-graphql-tck-\d+\.\d+.*?\.jar.* From 722e4c82e029ed0f917a58629aec7ad2bc21ef35 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 10 Sep 2020 16:34:46 +0800 Subject: [PATCH 080/178] IT refactoring progress --- microprofile/graphql/server/pom.xml | 5 + .../graphql/server/GraphQLCdiExtension.java | 21 +- .../graphql/server/AbstractGraphQLIT.java | 41 +- .../graphql/server/AbstractGraphQLTest.java | 75 ++ .../graphql/server/DateTimeIT.java | 106 +++ .../graphql/server/DefaultValuesIT.java | 108 +++ .../graphql/server/DescriptionIT.java | 77 ++ .../graphql/server/IngorableIT.java | 72 ++ .../graphql/server/InputTypeIT.java | 44 + .../server/InterfaceOnlyAnnotatedIT.java | 38 + .../server/InterfaceTypeOnlyAnnotatedIT.java | 40 + .../microprofile/graphql/server/Level0IT.java | 56 ++ .../graphql/server/MultiLevelArraysIT.java | 90 ++ .../microprofile/graphql/server/NullIT.java | 83 ++ .../graphql/server/NumberFormatIT.java | 98 ++ .../microprofile/graphql/server/PersonIT.java | 57 ++ .../graphql/server/PropertyNameIT.java | 47 + .../graphql/server/SchemaGeneratorIT.java | 861 +----------------- .../graphql/server/SimpleMutationsIT.java | 63 ++ .../graphql/server/SimpleQueriesIT.java | 156 ++++ .../server/SimpleQueriesWithArgsIT.java | 158 ++++ .../microprofile/graphql/server/SourceIT.java | 76 ++ .../test/mutations/SimpleMutations.java | 15 - .../server/test/types/AbstractVehicle.java | 2 +- 24 files changed, 1504 insertions(+), 885 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PersonIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 5ede0c9610d..77860e092b9 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -103,6 +103,11 @@ hamcrest-all test + + org.jboss.weld + weld-junit5 + test + diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java index 0359b77573c..d2ac4cf7584 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java @@ -25,14 +25,33 @@ import javax.enterprise.inject.spi.WithAnnotations; import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Input; +import org.eclipse.microprofile.graphql.Interface; +import org.eclipse.microprofile.graphql.Type; +/** + * A CDI {@link Extension} to collect the classes that are of interest Microprofile GraphQL. + */ public class GraphQLCdiExtension implements Extension { private final List> collectedApis = new ArrayList<>(); - void collectApis(@Observes @WithAnnotations(GraphQLApi.class) ProcessAnnotatedType processAnnotatedType) { + /** + * Collect the classes that have the following Microprofile GraphQL annotations. + * + * @param processAnnotatedType annotation types to process + */ + void collectApis(@Observes @WithAnnotations({GraphQLApi.class, + Type.class, + Input.class, + Interface.class }) ProcessAnnotatedType processAnnotatedType) { this.collectedApis.add(processAnnotatedType.getAnnotatedType().getJavaClass()); } + /** + * Returns the collected API's. + * + * @return the collected API's + */ public Class[] collectedApis() { return collectedApis.toArray(new Class[0]); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java index e79c537c9ba..1becc4de515 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -16,37 +16,26 @@ package io.helidon.microprofile.graphql.server; -import javax.enterprise.inject.se.SeContainer; -import javax.enterprise.inject.se.SeContainerInitializer; -import org.junit.jupiter.api.AfterAll; +import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; + +import java.beans.IntrospectionException; import java.io.File; import java.io.IOException; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + /** * Common functionality for integration tests. */ -public abstract class AbstractGraphQLIT - extends AbstractGraphQLTest { +public abstract class AbstractGraphQLIT extends AbstractGraphQLTest { protected String indexFileName = null; protected File indexFile = null; protected Context defaultContext; - private static SeContainer container; - - @BeforeAll - public static void initialize() { - container = SeContainerInitializer.newInstance().initialize(); - } - - @AfterAll - public static void teardown() { - container.close(); - } - @BeforeEach public void setupTest() throws IOException { System.clearProperty(JandexUtils.PROP_INDEX_FILE); @@ -61,4 +50,20 @@ public void teardownTest() { indexFile.delete(); } } + + + protected void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException { + SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); + Schema schema = schemaGenerator.generateSchema(); + assertThat(schema, CoreMatchers.is(notNullValue())); + schema.getTypes().forEach(t -> System.out.println(t.getName())); + assertThat(schema.getTypes().size(), CoreMatchers.is(6)); + assertThat(schema.getTypeByName("Vehicle"), CoreMatchers.is(notNullValue())); + assertThat(schema.getTypeByName("Car"), CoreMatchers.is(notNullValue())); + assertThat(schema.getTypeByName("Motorbike"), CoreMatchers.is(notNullValue())); + assertThat(schema.getTypeByName("Incident"), CoreMatchers.is(notNullValue())); + assertThat(schema.getTypeByName("Query"), CoreMatchers.is(notNullValue())); + assertThat(schema.getTypeByName("Mutation"), CoreMatchers.is(notNullValue())); + generateGraphQLSchema(schema); + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java index f101c938bbf..69d7ef4fe54 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java @@ -16,6 +16,7 @@ package io.helidon.microprofile.graphql.server; +import java.beans.IntrospectionException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -30,6 +31,7 @@ import graphql.schema.GraphQLSchema; import graphql.schema.idl.SchemaPrinter; +import io.helidon.microprofile.graphql.server.test.types.SimpleContact; import org.hamcrest.CoreMatchers; import org.jboss.jandex.Index; import org.jboss.jandex.IndexWriter; @@ -208,4 +210,77 @@ protected Map getAndAssertResult(Map result) { } return (Map) result.get(ExecutionContext.DATA); } + + protected String getContactAsQueryInput(SimpleContact contact) { + return new StringBuilder("{") + .append("id: \"").append(contact.getId()).append("\" ") + .append("name: \"").append(contact.getName()).append("\" ") + .append("age: ").append(contact.getAge()) + .append("} ").toString(); + } + + protected void assertDefaultFormat(SchemaType type, String fdName, String defaultFormat) { + assertThat(type, CoreMatchers.is(notNullValue())); + SchemaFieldDefinition fd = getFieldDefinition(type, fdName); + assertThat(fd, CoreMatchers.is(notNullValue())); + String[] format = fd.getFormat(); + assertThat(format, CoreMatchers.is(notNullValue())); + assertThat(format.length == 2, CoreMatchers.is(notNullValue())); + assertThat(format[0], CoreMatchers.is(defaultFormat)); + } + + protected SchemaFieldDefinition getFieldDefinition(SchemaType type, String name) { + for (SchemaFieldDefinition fd : type.getFieldDefinitions()) { + if (fd.getName().equals(name)) { + return fd; + } + } + return null; + } + + protected SchemaArgument getArgument(SchemaFieldDefinition fd, String name) { + assertThat(fd, CoreMatchers.is(notNullValue())); + for (SchemaArgument argument : fd.getArguments()) { + if (argument.getArgumentName().equals(name)) { + return argument; + } + } + return null; + } + + protected void assertReturnTypeDefaultValue(SchemaType type, String fdName, String defaultValue) { + assertThat(type, CoreMatchers.is(notNullValue())); + SchemaFieldDefinition fd = getFieldDefinition(type, fdName); + assertThat(fd, CoreMatchers.is(notNullValue())); + assertThat("Default value for " + fdName + " should be " + defaultValue + + " but is " + fd.getDefaultValue(), fd.getDefaultValue(), CoreMatchers.is(defaultValue)); + } + + protected void assertReturnTypeMandatory(SchemaType type, String fdName, boolean mandatory) { + assertThat(type, CoreMatchers.is(notNullValue())); + SchemaFieldDefinition fd = getFieldDefinition(type, fdName); + assertThat(fd, CoreMatchers.is(notNullValue())); + assertThat("Return type for " + fdName + " should be mandatory=" + mandatory + + " but is " + fd.isReturnTypeMandatory(), fd.isReturnTypeMandatory(), CoreMatchers.is(mandatory)); + } + + protected void assertArrayReturnTypeMandatory(SchemaType type, String fdName, boolean mandatory) { + assertThat(type, CoreMatchers.is(notNullValue())); + SchemaFieldDefinition fd = getFieldDefinition(type, fdName); + assertThat(fd, CoreMatchers.is(notNullValue())); + assertThat("Array return type for " + fdName + " should be mandatory=" + mandatory + + " but is " + fd.isArrayReturnTypeMandatory(), fd.isArrayReturnTypeMandatory(), CoreMatchers + .is(mandatory)); + } + + protected void assertReturnTypeArgumentMandatory(SchemaType type, String fdName, String argumentName, boolean mandatory) { + assertThat(type, CoreMatchers.is(notNullValue())); + SchemaFieldDefinition fd = getFieldDefinition(type, fdName); + assertThat(fd, CoreMatchers.is(notNullValue())); + SchemaArgument argument = getArgument(fd, argumentName); + assertThat(argument, CoreMatchers.is(notNullValue())); + assertThat("Return type for argument " + argumentName + " should be mandatory=" + + mandatory + " but is " + argument.isMandatory(), argument.isMandatory(), CoreMatchers.is(mandatory)); + } + } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java new file mode 100644 index 00000000000..af2f116dea4 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java @@ -0,0 +1,106 @@ +package io.helidon.microprofile.graphql.server; + +import java.beans.IntrospectionException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; +import io.helidon.microprofile.graphql.server.ExecutionContext; +import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; +import io.helidon.microprofile.graphql.server.Schema; +import io.helidon.microprofile.graphql.server.SchemaFieldDefinition; +import io.helidon.microprofile.graphql.server.SchemaGenerator; +import io.helidon.microprofile.graphql.server.SchemaType; +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; +import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; +import io.helidon.microprofile.graphql.server.test.types.Person; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class DateTimeIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(SimpleQueriesNoArgs.class) + .addBeanClass(DateTimePojo.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testDateAndTime() throws IOException { + setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesNoArgs.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Schema schema = executionContext.getSchema(); + SchemaType type = schema.getTypeByName("DateTimePojo"); + + SchemaFieldDefinition fd = getFieldDefinition(type, "localDate"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is("MM/dd/yyyy")); + assertThat(fd.getDescription(), is(nullValue())); + + fd = getFieldDefinition(type, "localTime"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is("hh:mm:ss")); + assertThat(fd.getDescription(), is(nullValue())); + + fd = getFieldDefinition(type, "localDate2"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is("MM/dd/yyyy")); + assertThat(fd.getDescription(), is(nullValue())); + + // test default values for date and time + assertDefaultFormat(type, "offsetTime", "HH:mm:ssZ"); + assertDefaultFormat(type, "localTime", "hh:mm:ss"); + assertDefaultFormat(type, "localDateTime", "yyyy-MM-dd'T'HH:mm:ss"); + assertDefaultFormat(type, "offsetDateTime", "yyyy-MM-dd'T'HH:mm:ssZ"); + assertDefaultFormat(type, "zonedDateTime", "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'"); + assertDefaultFormat(type, "localDateNoFormat", "yyyy-MM-dd"); + assertDefaultFormat(type, "significantDates", "yyyy-MM-dd"); + + fd = getFieldDefinition(type, "localDateTime"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.getFormat()[0], is("yyyy-MM-dd'T'HH:mm:ss")); + + Map mapResults = getAndAssertResult( + executionContext.execute("query { dateAndTimePOJOQuery { offsetDateTime offsetTime zonedDateTime " + + "localDate localDate2 localTime localDateTime significantDates } }")); + assertThat(mapResults.size(), is(1)); + Map mapResults2 = (Map) mapResults.get("dateAndTimePOJOQuery"); + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.size(), is(8)); + + assertThat(mapResults2.get("localDate"), is("02/17/1968")); + assertThat(mapResults2.get("localDate2"), is("08/04/1970")); + assertThat(mapResults2.get("localTime"), is("10:10:20")); + assertThat(mapResults2.get("offsetTime"), is("08:10:01+0000")); + Object significantDates = mapResults2.get("significantDates"); + assertThat(significantDates, is(notNullValue())); + List listDates = (ArrayList) mapResults2.get("significantDates"); + assertThat(listDates.size(),is(2)); + assertThat(listDates.get(0), is("1968-02-17")); + assertThat(listDates.get(1), is("1970-08-04")); + + mapResults = getAndAssertResult( + executionContext.execute("query { localDateListFormat }")); + assertThat(mapResults, is(notNullValue())); + listDates = (ArrayList) mapResults.get("localDateListFormat"); + assertThat(listDates.size(),is(2)); + assertThat(listDates.get(0), is("17/02/1968")); + assertThat(listDates.get(1), is("04/08/1970")); + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java new file mode 100644 index 00000000000..37269a222f9 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java @@ -0,0 +1,108 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.queries.DefaultValueQueries; +import io.helidon.microprofile.graphql.server.test.queries.DescriptionQueries; +import io.helidon.microprofile.graphql.server.test.queries.OddNamedQueriesAndMutations; +import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO; +import io.helidon.microprofile.graphql.server.test.types.DescriptionType; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class DefaultValuesIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(DefaultValuePOJO.class) + .addBeanClass(DefaultValueQueries.class) + .addBeanClass(OddNamedQueriesAndMutations.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + + @Test + public void setOddNamedQueriesAndMutations() throws IOException { + setupIndex(indexFileName, DefaultValuePOJO.class, OddNamedQueriesAndMutations.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Schema schema = executionContext.getSchema(); + assertThat(schema, is(notNullValue())); + SchemaType query = schema.getTypeByName("Query"); + SchemaType mutation = schema.getTypeByName("Mutation"); + assertThat(query, is(notNullValue())); + assertThat(query.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("settlement")).count(), is(1L)); + assertThat(mutation.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("getaway")).count(), is(1L)); + } + + + @Test + public void testDefaultValues() throws IOException { + setupIndex(indexFileName, DefaultValuePOJO.class, DefaultValueQueries.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + // test with both fields as default + Map mapResults = getAndAssertResult( + executionContext.execute("mutation { generateDefaultValuePOJO { id value } }")); + assertThat(mapResults.size(), is(1)); + Map results = (Map) mapResults.get("generateDefaultValuePOJO"); + assertThat(results, is(notNullValue())); + assertThat(results.get("id"), is("ID-1")); + assertThat(results.get("value"), is(1000)); + + // test with a field overridden + mapResults = getAndAssertResult( + executionContext.execute("mutation { generateDefaultValuePOJO(id: \"ID-123\") { id value } }")); + assertThat(mapResults.size(), is(1)); + results = (Map) mapResults.get("generateDefaultValuePOJO"); + assertThat(results, is(notNullValue())); + assertThat(results.get("id"), is("ID-123")); + assertThat(results.get("value"), is(1000)); + + mapResults = getAndAssertResult(executionContext.execute("query { echoDefaultValuePOJO { id value } }")); + assertThat(mapResults.size(), is(1)); + results = (Map) mapResults.get("echoDefaultValuePOJO"); + assertThat(results, is(notNullValue())); + assertThat(results.get("id"), is("ID-1")); + assertThat(results.get("value"), is(1000)); + + mapResults = getAndAssertResult( + executionContext.execute("query { echoDefaultValuePOJO(input: {id: \"X123\" value: 1}) { id value } }")); + assertThat(mapResults.size(), is(1)); + results = (Map) mapResults.get("echoDefaultValuePOJO"); + assertThat(results, is(notNullValue())); + assertThat(results.get("id"), is("X123")); + assertThat(results.get("value"), is(1)); + + mapResults = getAndAssertResult( + executionContext.execute("query { echoDefaultValuePOJO(input: {value: 1}) { id value } }")); + assertThat(mapResults.size(), is(1)); + results = (Map) mapResults.get("echoDefaultValuePOJO"); + assertThat(results, is(notNullValue())); + assertThat(results.get("id"), is("ID-123")); + assertThat(results.get("value"), is(1)); + + Schema schema = executionContext.getSchema(); + SchemaType type = schema.getInputTypeByName("DefaultValuePOJOInput"); + assertReturnTypeDefaultValue(type, "id", "ID-123"); + assertReturnTypeDefaultValue(type, "booleanValue", "false"); + assertReturnTypeMandatory(type, "booleanValue", false); + + SchemaFieldDefinition fd = getFieldDefinition(type, "value"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getDefaultValue(), is("111222")); + } + + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java new file mode 100644 index 00000000000..1ee59e6c1ee --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java @@ -0,0 +1,77 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.queries.DescriptionQueries; +import io.helidon.microprofile.graphql.server.test.types.DescriptionType; + +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class DescriptionIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(DescriptionType.class) + .addBeanClass(DescriptionQueries.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testDescriptions() throws IOException { + setupIndex(indexFileName, DescriptionType.class, DescriptionQueries.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Schema schema = executionContext.getSchema(); + assertThat(schema, is(notNullValue())); + SchemaType type = schema.getTypeByName("DescriptionType"); + assertThat(type, is(notNullValue())); + type.getFieldDefinitions().forEach(fd -> { + if (fd.getName().equals("id")) { + assertThat(fd.getDescription(), is("this is the description")); + } + if (fd.getName().equals("value")) { + assertThat(fd.getDescription(), is("description of value")); + } + if (fd.getName().equals("longValue1")) { + // no description so include the format + assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.getFormat()[0], is("L-########")); + } + if (fd.getName().equals("longValue2")) { + // both description and formatting + assertThat(fd.getDescription(), is("Description")); + } + }); + + SchemaInputType inputType = schema.getInputTypeByName("DescriptionTypeInput"); + assertThat(inputType, is(notNullValue())); + inputType.getFieldDefinitions().forEach(fd -> { + if (fd.getName().equals("value")) { + assertThat(fd.getDescription(), is("description on set for input")); + } + }); + + SchemaType query = schema.getTypeByName("Query"); + assertThat(query, is(notNullValue())); + SchemaFieldDefinition fd = getFieldDefinition(query, "descriptionOnParam"); + assertThat(fd, (is(notNullValue()))); + + fd.getArguments().forEach(a -> { + if (a.getArgumentName().equals("param1")) { + assertThat(a.getDescription(), is("Description for param1")); + } + }); + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java new file mode 100644 index 00000000000..0aaec5fba20 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java @@ -0,0 +1,72 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; + +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.queries.QueriesWithIgnorable; + +import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.opentest4j.AssertionFailedError; + +@ExtendWith(WeldJunit5Extension.class) +public class IngorableIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(QueriesWithIgnorable.class) + .addBeanClass(ObjectWithIgnorableFieldsAndMethods.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testObjectWithIgnorableFields() throws IOException { + setupIndex(indexFileName, ObjectWithIgnorableFieldsAndMethods.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + executionContext.execute("query { hero }"); + } + + @Test + public void testIgnorable() throws IOException { + setupIndex(indexFileName, QueriesWithIgnorable.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Map mapResults = getAndAssertResult( + executionContext.execute("query { testIgnorableFields { id dontIgnore } }")); + assertThat(mapResults.size(), is(1)); + + Map mapResults2 = (Map) mapResults.get("testIgnorableFields"); + assertThat(mapResults2.size(), is(2)); + assertThat(mapResults2.get("id"), is("id")); + assertThat(mapResults2.get("dontIgnore"), is(true)); + + // ensure getting the fields generates an error that is caught by the getAndAssertResult + assertThrows(AssertionFailedError.class, () -> getAndAssertResult(executionContext + .execute( + "query { testIgnorableFields { id " + + "dontIgnore pleaseIgnore " + + "ignoreThisAsWell } }"))); + + Schema schema = executionContext.getSchema(); + SchemaType type = schema.getTypeByName("ObjectWithIgnorableFieldsAndMethods"); + assertThat(type, is(notNullValue())); + assertThat(type.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreGetMethod")).count(), is(0L)); + + SchemaInputType inputType = schema.getInputTypeByName("ObjectWithIgnorableFieldsAndMethodsInput"); + assertThat(inputType, is(notNullValue())); + assertThat(inputType.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreBecauseOfMethod")).count(), + is(0L)); + assertThat(inputType.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("valueSetter")).count(), is(1L)); + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java new file mode 100644 index 00000000000..4f2d1f08615 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java @@ -0,0 +1,44 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputType; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithAddress; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithName; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithNameValue; + +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class InputTypeIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(SimpleContactInputType.class) + .addBeanClass(SimpleContactInputTypeWithName.class) + .addBeanClass(SimpleContactInputTypeWithNameValue.class) + .addBeanClass(SimpleContactInputTypeWithAddress.class) + .addExtension(new GraphQLCdiExtension())); + + + @Test + public void testInputType() throws IOException { + setupIndex(indexFileName, SimpleContactInputType.class, SimpleContactInputTypeWithName.class, + SimpleContactInputTypeWithNameValue.class, SimpleContactInputTypeWithAddress.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Schema schema = executionContext.getSchema(); + assertThat(schema.getInputTypes().size(), is(5)); + assertThat(schema.containsInputTypeWithName("MyInputType"), is(true)); + assertThat(schema.containsInputTypeWithName("SimpleContactInputTypeInput"), is(true)); + assertThat(schema.containsInputTypeWithName("NameInput"), is(true)); + assertThat(schema.containsInputTypeWithName("SimpleContactInputTypeWithAddressInput"), is(true)); + assertThat(schema.containsInputTypeWithName("AddressInput"), is(true)); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java new file mode 100644 index 00000000000..55fe7d54ed4 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java @@ -0,0 +1,38 @@ +package io.helidon.microprofile.graphql.server; + +import java.beans.IntrospectionException; +import java.io.IOException; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; +import io.helidon.microprofile.graphql.server.test.types.Car; +import io.helidon.microprofile.graphql.server.test.types.Motorbike; +import io.helidon.microprofile.graphql.server.test.types.Vehicle; + +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class InterfaceOnlyAnnotatedIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(Vehicle.class) + .addBeanClass(Car.class) + .addBeanClass(Motorbike.class) + .addBeanClass(AbstractVehicle.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + /** + * Test discovery of interfaces when only the interface annotated. + */ + @Test + public void testInterfaceDiscoveryWithImplementorsWithNoTypeAnnotation() + throws IOException, IntrospectionException, ClassNotFoundException { + setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, AbstractVehicle.class); + assertInterfaceResults(); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java new file mode 100644 index 00000000000..d928085da4d --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java @@ -0,0 +1,40 @@ +package io.helidon.microprofile.graphql.server; + +import java.beans.IntrospectionException; +import java.io.IOException; +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; +import io.helidon.microprofile.graphql.server.test.types.Car; +import io.helidon.microprofile.graphql.server.test.types.Motorbike; +import io.helidon.microprofile.graphql.server.test.types.Vehicle; +import io.helidon.microprofile.graphql.server.test.types.VehicleIncident; + +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class InterfaceTypeOnlyAnnotatedIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(Vehicle.class) + .addBeanClass(Car.class) + .addBeanClass(Motorbike.class) + .addBeanClass(Motorbike.class) + .addBeanClass(VehicleIncident.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + /** + * Test discovery of interfaces and subsequent unresolved type which has a Name annotation . + */ + @Test + public void testInterfaceDiscoveryWithUnresolvedType() throws IOException, IntrospectionException, ClassNotFoundException { + setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, VehicleIncident.class, AbstractVehicle.class); + assertInterfaceResults(); + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java new file mode 100644 index 00000000000..72acc27bc17 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java @@ -0,0 +1,56 @@ +package io.helidon.microprofile.graphql.server; + +import java.beans.IntrospectionException; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; +import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; +import io.helidon.microprofile.graphql.server.Schema; +import io.helidon.microprofile.graphql.server.SchemaGenerator; +import io.helidon.microprofile.graphql.server.test.types.Level0; +import io.helidon.microprofile.graphql.server.test.types.Person; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class Level0IT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(Level0.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testLevel0() throws IOException, IntrospectionException, ClassNotFoundException { + setupIndex(indexFileName, Level0.class); + SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); + Schema schema = schemaGenerator.generateSchema(); + assertThat(schema.containsTypeWithName("Level0"), is(true)); + assertThat(schema.containsTypeWithName("Level1"), is(true)); + assertThat(schema.containsTypeWithName("Level2"), is(true)); + generateGraphQLSchema(schema); + } + + @Test + public void testMultipleLevels() throws IOException, IntrospectionException, ClassNotFoundException { + setupIndex(indexFileName, Level0.class); + SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); + Schema schema = schemaGenerator.generateSchema(); + + assertThat(schema, is(notNullValue())); + assertThat(schema.getTypes().size(), is(6)); + assertThat(schema.getTypeByName("Level0"), is(notNullValue())); + assertThat(schema.getTypeByName("Level1"), is(notNullValue())); + assertThat(schema.getTypeByName("Level2"), is(notNullValue())); + assertThat(schema.getTypeByName("Address"), is(notNullValue())); + assertThat(schema.getTypeByName("Query"), is(notNullValue())); + generateGraphQLSchema(schema); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java new file mode 100644 index 00000000000..6a26b63e2e1 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java @@ -0,0 +1,90 @@ +package io.helidon.microprofile.graphql.server; + +import java.beans.IntrospectionException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; +import io.helidon.microprofile.graphql.server.ExecutionContext; +import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; +import io.helidon.microprofile.graphql.server.Schema; +import io.helidon.microprofile.graphql.server.SchemaGenerator; +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; +import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; +import io.helidon.microprofile.graphql.server.test.types.Person; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class MultiLevelArraysIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(MultiLevelListsAndArrays.class) + .addBeanClass(ArrayAndListQueries.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + + @Test + public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassNotFoundException, IOException { + setupIndex(indexFileName, MultiLevelListsAndArrays.class); + SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); + Schema schema = schemaGenerator.generateSchema(); + assertThat(schema.containsTypeWithName("MultiLevelListsAndArrays"), is(true)); + assertThat(schema.containsTypeWithName("Person"), is(true)); + assertThat(schema.containsScalarWithName("BigDecimal"), is(true)); + generateGraphQLSchema(schema); + } + + @Test + @SuppressWarnings("unchecked") + public void testMultiLevelListsAndArraysQueries() throws IOException { + setupIndex(indexFileName, ArrayAndListQueries.class, MultiLevelListsAndArrays.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Map mapResults = getAndAssertResult( + executionContext.execute("query { getMultiLevelList { intMultiLevelArray } }")); + assertThat(mapResults.size(), is(1)); + Map mapResults2 = (Map) mapResults.get("getMultiLevelList"); + ArrayList> intArrayList = (ArrayList>) mapResults2.get("intMultiLevelArray"); + assertThat(intArrayList, is(notNullValue())); + ArrayList integerArrayList1 = intArrayList.get(0); + assertThat(integerArrayList1, is(notNullValue())); + assertThat(integerArrayList1.contains(1), is(true)); + assertThat(integerArrayList1.contains(2), is(true)); + assertThat(integerArrayList1.contains(3), is(true)); + + ArrayList integerArrayList2 = intArrayList.get(1); + assertThat(integerArrayList2, is(notNullValue())); + assertThat(integerArrayList2.contains(4), is(true)); + assertThat(integerArrayList2.contains(5), is(true)); + assertThat(integerArrayList2.contains(6), is(true)); + + mapResults = getAndAssertResult(executionContext.execute("query { returnListOfStringArrays }")); + assertThat(mapResults.size(), is(1)); + ArrayList> stringArrayList = (ArrayList>) mapResults.get("returnListOfStringArrays"); + assertThat(stringArrayList, is(notNullValue())); + + List stringList1 = stringArrayList.get(0); + assertThat(stringList1, is(notNullValue())); + assertThat(stringList1.contains("one"), is(true)); + assertThat(stringList1.contains("two"), is(true)); + List stringList2 = stringArrayList.get(1); + assertThat(stringList2, is(notNullValue())); + assertThat(stringList2.contains("three"), is(true)); + assertThat(stringList2.contains("four"), is(true)); + assertThat(stringList2.contains("five"), is(true)); + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java new file mode 100644 index 00000000000..501bb489ffa --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java @@ -0,0 +1,83 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.queries.QueriesWithNulls; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; +import io.helidon.microprofile.graphql.server.test.types.NullPOJO; +import io.helidon.microprofile.graphql.server.test.types.SimpleContact; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class NullIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(NullPOJO.class) + .addBeanClass(QueriesWithNulls.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testNulls() throws IOException { + setupIndex(indexFileName, NullPOJO.class, QueriesWithNulls.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Schema schema = executionContext.getSchema(); + assertThat(schema, is(notNullValue())); + + // test primitives should be not null be default + SchemaType type = schema.getTypeByName("NullPOJO"); + assertReturnTypeMandatory(type, "id", true); + assertReturnTypeMandatory(type, "longValue", false); + assertReturnTypeMandatory(type, "stringValue", true); + assertReturnTypeMandatory(type, "testNullWithGet", true); + assertReturnTypeMandatory(type, "listNonNullStrings", false); + assertArrayReturnTypeMandatory(type, "listNonNullStrings", true); + assertArrayReturnTypeMandatory(type, "listOfListOfNonNullStrings", true); + assertReturnTypeMandatory(type, "listOfListOfNonNullStrings", false); + assertReturnTypeMandatory(type, "listOfListOfNullStrings", false); + assertArrayReturnTypeMandatory(type, "listOfListOfNullStrings", false); + assertReturnTypeMandatory(type, "testNullWithSet", false); + assertReturnTypeMandatory(type, "listNullStringsWhichIsMandatory", true); + assertArrayReturnTypeMandatory(type, "listNullStringsWhichIsMandatory", false); + assertReturnTypeMandatory(type, "testInputOnly", false); + assertArrayReturnTypeMandatory(type, "testInputOnly", false); + assertReturnTypeMandatory(type, "testOutputOnly", false); + assertArrayReturnTypeMandatory(type, "testOutputOnly", true); + + SchemaType query = schema.getTypeByName("Query"); + assertReturnTypeMandatory(query, "method1NotNull", true); + assertReturnTypeMandatory(query, "method2NotNull", true); + assertReturnTypeMandatory(query, "method3NotNull", false); + + assertReturnTypeArgumentMandatory(query, "paramShouldBeNonMandatory", "value", false); + assertReturnTypeArgumentMandatory(query, "paramShouldBeNonMandatory2", "value", false); + assertReturnTypeArgumentMandatory(query, "paramShouldBeNonMandatory3", "value", false); + + SchemaType input = schema.getInputTypeByName("NullPOJOInput"); + assertReturnTypeMandatory(input, "nonNullForInput", true); + assertReturnTypeMandatory(input, "testNullWithGet", false); + assertReturnTypeMandatory(input, "testNullWithSet", true); + assertReturnTypeMandatory(input, "listNonNullStrings", false); + assertArrayReturnTypeMandatory(input, "listNonNullStrings", true); + + assertArrayReturnTypeMandatory(input, "listOfListOfNonNullStrings", true); + + assertReturnTypeMandatory(input, "testInputOnly", false); + assertArrayReturnTypeMandatory(input, "testInputOnly", true); + + assertReturnTypeMandatory(input, "testOutputOnly", false); + assertArrayReturnTypeMandatory(input, "testOutputOnly", false); + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java new file mode 100644 index 00000000000..8a88571c00f --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java @@ -0,0 +1,98 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; +import io.helidon.microprofile.graphql.server.ExecutionContext; +import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; +import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; +import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; +import io.helidon.microprofile.graphql.server.test.types.Car; +import io.helidon.microprofile.graphql.server.test.types.ContactRelationship; +import io.helidon.microprofile.graphql.server.test.types.SimpleContact; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class NumberFormatIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(SimpleContactWithNumberFormats.class) + .addBeanClass(NumberFormatQueriesAndMutations.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + + @Test + public void testNumberFormats() throws IOException { + setupIndex(indexFileName, SimpleContactWithNumberFormats.class, NumberFormatQueriesAndMutations.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Map mapResults = getAndAssertResult(executionContext + .execute("query { simpleFormattingQuery { id name age " + + "bankBalance value longValue bigDecimal } }")); + assertThat(mapResults.size(), is(1)); + + Map mapResults2 = (Map) mapResults.get("simpleFormattingQuery"); + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("id"), is("1 id")); + assertThat(mapResults2.get("name"), is("Tim")); + assertThat(mapResults2.get("age"), is("50 years old")); + assertThat(mapResults2.get("bankBalance"), is("$ 1200.00")); + assertThat(mapResults2.get("value"), is("10 value")); + assertThat(mapResults2.get("longValue"), is(BigInteger.valueOf(Long.MAX_VALUE))); + assertThat(mapResults2.get("bigDecimal"), is("BigDecimal-100")); + + mapResults = getAndAssertResult(executionContext.execute("mutation { generateDoubleValue }")); + assertThat(mapResults, is(notNullValue())); + assertThat(mapResults.get("generateDoubleValue"), is("Double-123456789")); + + mapResults = getAndAssertResult(executionContext.execute("query { echoBigDecimalUsingFormat(param1: \"BD-123\") }")); + assertThat(mapResults, is(notNullValue())); + assertThat(mapResults.get("echoBigDecimalUsingFormat"), is(BigDecimal.valueOf(123))); + + // COH-21891 + mapResults = getAndAssertResult(executionContext.execute("query { listAsString(arg1: [ [ \"value 12.12\", \"value 33.33\"] ] ) }")); + assertThat(mapResults, is(notNullValue())); + + // create a new contact +// String contactInput = +// "contact: {" +// + "id: \"1 id\" " +// + "name: \"Tim\" " +// + "age: \"20 years old\" " +// + "bankBalance: \"$ 1000.01\" " +// + "value: \"9 value\" " +// + "longValue: 12345" +// + "bigDecimal: \"BigDecimal-12345\"" +// + " } "; +// +// mapResults = getAndAssertResult( +// executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + +// ") { id name } }")); +// assertThat(mapResults.size(), is(1)); +// mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); +// assertThat(mapResults2, is(notNullValue())); + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PersonIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PersonIT.java new file mode 100644 index 00000000000..875e3f4baf1 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PersonIT.java @@ -0,0 +1,57 @@ +package io.helidon.microprofile.graphql.server; + +import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; +import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; +import io.helidon.microprofile.graphql.server.Schema; +import io.helidon.microprofile.graphql.server.SchemaGenerator; +import io.helidon.microprofile.graphql.server.test.types.Person; +import io.helidon.microprofile.graphql.server.test.types.PersonWithName; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import java.beans.IntrospectionException; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +@ExtendWith(WeldJunit5Extension.class) +public class PersonIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(Person.class) + .addExtension(new GraphQLCdiExtension())); + + /** + * Test generation of Type with no-name. + */ + @Test + public void testTypeGenerationWithNoName() throws IntrospectionException, ClassNotFoundException, IOException { + setupIndex(indexFileName, Person.class); + SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); + Schema schema = schemaGenerator.generateSchema(); + assertThat(schema.getTypeByName("Person"), is(notNullValue())); + assertThat(schema.getTypeByName("Address"), is(notNullValue())); + assertThat(schema.containsScalarWithName("Date"), is(notNullValue())); + assertThat(schema.containsScalarWithName("BigDecimal"), is(notNullValue())); + generateGraphQLSchema(schema); + } + + /** + * Test generation of Type with a different name then class name. + */ + @Test + public void testPersonWithName() throws IOException, IntrospectionException, ClassNotFoundException { + setupIndex(indexFileName, PersonWithName.class); + SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); + Schema schema = schemaGenerator.generateSchema(); + + assertThat(schema, is(notNullValue())); + assertThat(schema.getTypeByName("Person"), is(notNullValue())); + generateGraphQLSchema(schema); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java new file mode 100644 index 00000000000..336ae07eb73 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java @@ -0,0 +1,47 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.queries.PropertyNameQueries; +import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty; + +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + + +@ExtendWith(WeldJunit5Extension.class) +public class PropertyNameIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(PropertyNameQueries.class) + .addBeanClass(TypeWithNameAndJsonbProperty.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testDifferentPropertyNames() throws IOException { + setupIndex(indexFileName, PropertyNameQueries.class, TypeWithNameAndJsonbProperty.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Map mapResults = getAndAssertResult( + executionContext.execute("query { query1 { newFieldName1 newFieldName2 " + + " newFieldName3 newFieldName4 newFieldName5 newFieldName6 } }")); + assertThat(mapResults.size(), is(1)); + + Map mapResults2 = (Map) mapResults.get("query1"); + assertThat(mapResults2.size(), is(6)); + for (int i = 1; i <= 6; i++) { + assertThat(mapResults2.get("newFieldName" + i), is("name" + i)); + } + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java index 89bc5e3fe0a..355096f2309 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java @@ -75,13 +75,15 @@ import org.eclipse.microprofile.graphql.NonNull; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; import org.junit.jupiter.api.Test; -import org.opentest4j.AssertionFailedError; +import org.junit.jupiter.api.extension.ExtendWith; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.nullValue; + import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -89,76 +91,13 @@ * Integration tests for {@link SchemaGeneratorTest}. */ @SuppressWarnings("unchecked") -public class SchemaGeneratorIT - extends AbstractGraphQLIT { - - /** - * Test generation of Type with no-name. - */ - @Test - public void testTypeGenerationWithNoName() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(indexFileName, Person.class); - - SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); - Schema schema = schemaGenerator.generateSchema(); - assertThat(schema.getTypeByName("Person"), is(notNullValue())); - assertThat(schema.getTypeByName("Address"), is(notNullValue())); - assertThat(schema.containsScalarWithName("Date"), is(notNullValue())); - assertThat(schema.containsScalarWithName("BigDecimal"), is(notNullValue())); - generateGraphQLSchema(schema); - } - - @Test - public void testLevel0() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(indexFileName, Level0.class); - SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); - Schema schema = schemaGenerator.generateSchema(); - assertThat(schema.containsTypeWithName("Level0"), is(true)); - assertThat(schema.containsTypeWithName("Level1"), is(true)); - assertThat(schema.containsTypeWithName("Level2"), is(true)); - generateGraphQLSchema(schema); - } - - @Test - public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassNotFoundException, IOException { - setupIndex(indexFileName, MultiLevelListsAndArrays.class); - SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); - Schema schema = schemaGenerator.generateSchema(); - assertThat(schema.containsTypeWithName("MultiLevelListsAndArrays"), is(true)); - assertThat(schema.containsTypeWithName("Person"), is(true)); - assertThat(schema.containsScalarWithName("BigDecimal"), is(true)); - generateGraphQLSchema(schema); - } - - /** - * Test generation of Type with a different name then class name. - */ - @Test - public void testPersonWithName() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(indexFileName, PersonWithName.class); - SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); - Schema schema = schemaGenerator.generateSchema(); - - assertThat(schema, is(notNullValue())); - assertThat(schema.getTypeByName("Person"), is(notNullValue())); - generateGraphQLSchema(schema); - } - - @Test - public void testMultipleLevels() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(indexFileName, Level0.class); - SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); - Schema schema = schemaGenerator.generateSchema(); +@ExtendWith(WeldJunit5Extension.class) +public class SchemaGeneratorIT extends AbstractGraphQLIT { - assertThat(schema, is(notNullValue())); - assertThat(schema.getTypes().size(), is(6)); - assertThat(schema.getTypeByName("Level0"), is(notNullValue())); - assertThat(schema.getTypeByName("Level1"), is(notNullValue())); - assertThat(schema.getTypeByName("Level2"), is(notNullValue())); - assertThat(schema.getTypeByName("Address"), is(notNullValue())); - assertThat(schema.getTypeByName("Query"), is(notNullValue())); - generateGraphQLSchema(schema); - } + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(Person.class) + .addExtension(new GraphQLCdiExtension())); @Test public void testJandexUtils() throws IOException { @@ -179,42 +118,6 @@ public void testJandexUtils() throws IOException { assertThat(jandexUtils.methodHasAnnotation(nullPOJO, "getListOfListOfNonNullStrings", noNull), is(false)); } - /** - * Test discovery of interfaces when only the interface annotated. - */ - @Test - public void testInterfaceDiscoveryWithImplementorsWithNoTypeAnnotation() - throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, AbstractVehicle.class); - assertInterfaceResults(); - } - - /** - * Test discovery of interfaces and subsequent unresolved type which has a Name annotation . - */ - @Test - public void testInterfaceDiscoveryWithUnresolvedType() throws IOException, IntrospectionException, ClassNotFoundException { - System.out.println("TESTING"); - setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, VehicleIncident.class, AbstractVehicle.class); - assertInterfaceResults(); - } - - /** - * Test discovery of interfaces when only interface is annotated. - */ - @Test - public void testInterfaceDiscoveryWithoutTypes() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, AbstractVehicle.class); - assertInterfaceResults(); - } - - @Test - public void testObjectWithIgnorableFields() throws IOException { - setupIndex(indexFileName, ObjectWithIgnorableFieldsAndMethods.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - executionContext.execute("query { hero }"); - } - @Test public void testVoidMutations() throws IOException { setupIndex(indexFileName, VoidMutations.class); @@ -274,746 +177,4 @@ public void testDuplicateQueryOrMutationNames() throws IOException { setupIndex(indexFileName, DuplicateNameQueries.class); assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); } - - @Test - public void testSimpleContactWithSelf() throws IOException { - setupIndex(indexFileName, SimpleContactWithSelf.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - executionContext.execute("query { hero }"); - } - - @Test - public void testQueriesWithVariables() throws IOException { - setupIndex(indexFileName, SimpleQueriesWithArgs.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - Map mapVariables = Map.of("first", 10, "second", 20); - Map mapResults = getAndAssertResult(executionContext.execute( - "query additionQuery($first: Int!, $second: Int!) {" - + " additionQuery(value1: $first, value2: $second) }", "additionQuery", mapVariables)); - assertThat(mapResults, is(notNullValue())); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("additionQuery"), is(30)); - } - - @Test - public void testNumberFormats() throws IOException { - setupIndex(indexFileName, SimpleContactWithNumberFormats.class, NumberFormatQueriesAndMutations.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Map mapResults = getAndAssertResult(executionContext - .execute("query { simpleFormattingQuery { id name age " - + "bankBalance value longValue bigDecimal } }")); - assertThat(mapResults.size(), is(1)); - - Map mapResults2 = (Map) mapResults.get("simpleFormattingQuery"); - assertThat(mapResults2, is(notNullValue())); - assertThat(mapResults2.get("id"), is("1 id")); - assertThat(mapResults2.get("name"), is("Tim")); - assertThat(mapResults2.get("age"), is("50 years old")); - assertThat(mapResults2.get("bankBalance"), is("$ 1200.00")); - assertThat(mapResults2.get("value"), is("10 value")); - assertThat(mapResults2.get("longValue"), is(BigInteger.valueOf(Long.MAX_VALUE))); - assertThat(mapResults2.get("bigDecimal"), is("BigDecimal-100")); - - mapResults = getAndAssertResult(executionContext.execute("mutation { generateDoubleValue }")); - assertThat(mapResults, is(notNullValue())); - assertThat(mapResults.get("generateDoubleValue"), is("Double-123456789")); - - mapResults = getAndAssertResult(executionContext.execute("query { echoBigDecimalUsingFormat(param1: \"BD-123\") }")); - assertThat(mapResults, is(notNullValue())); - assertThat(mapResults.get("echoBigDecimalUsingFormat"), is(BigDecimal.valueOf(123))); - - // COH-21891 - mapResults = getAndAssertResult(executionContext.execute("query { listAsString(arg1: [ [ \"value 12.12\", \"value 33.33\"] ] ) }")); - assertThat(mapResults, is(notNullValue())); - - // create a new contact -// String contactInput = -// "contact: {" -// + "id: \"1 id\" " -// + "name: \"Tim\" " -// + "age: \"20 years old\" " -// + "bankBalance: \"$ 1000.01\" " -// + "value: \"9 value\" " -// + "longValue: 12345" -// + "bigDecimal: \"BigDecimal-12345\"" -// + " } "; -// -// mapResults = getAndAssertResult( -// executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + -// ") { id name } }")); -// assertThat(mapResults.size(), is(1)); -// mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); -// assertThat(mapResults2, is(notNullValue())); - } - - @Test - public void testDateAndTime() throws IOException { - setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesNoArgs.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Schema schema = executionContext.getSchema(); - SchemaType type = schema.getTypeByName("DateTimePojo"); - - SchemaFieldDefinition fd = getFieldDefinition(type, "localDate"); - assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("MM/dd/yyyy")); - assertThat(fd.getDescription(), is(nullValue())); - - fd = getFieldDefinition(type, "localTime"); - assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("hh:mm:ss")); - assertThat(fd.getDescription(), is(nullValue())); - - fd = getFieldDefinition(type, "localDate2"); - assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("MM/dd/yyyy")); - assertThat(fd.getDescription(), is(nullValue())); - - // test default values for date and time - assertDefaultFormat(type, "offsetTime", "HH:mm:ssZ"); - assertDefaultFormat(type, "localTime", "hh:mm:ss"); - assertDefaultFormat(type, "localDateTime", "yyyy-MM-dd'T'HH:mm:ss"); - assertDefaultFormat(type, "offsetDateTime", "yyyy-MM-dd'T'HH:mm:ssZ"); - assertDefaultFormat(type, "zonedDateTime", "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'"); - assertDefaultFormat(type, "localDateNoFormat", "yyyy-MM-dd"); - assertDefaultFormat(type, "significantDates", "yyyy-MM-dd"); - - fd = getFieldDefinition(type, "localDateTime"); - assertThat(fd, is(notNullValue())); - assertThat(fd.getDescription(), is(nullValue())); - assertThat(fd.getFormat()[0], is("yyyy-MM-dd'T'HH:mm:ss")); - - Map mapResults = getAndAssertResult( - executionContext.execute("query { dateAndTimePOJOQuery { offsetDateTime offsetTime zonedDateTime " - + "localDate localDate2 localTime localDateTime significantDates } }")); - assertThat(mapResults.size(), is(1)); - Map mapResults2 = (Map) mapResults.get("dateAndTimePOJOQuery"); - assertThat(mapResults2, is(notNullValue())); - assertThat(mapResults2.size(), is(8)); - - assertThat(mapResults2.get("localDate"), is("02/17/1968")); - assertThat(mapResults2.get("localDate2"), is("08/04/1970")); - assertThat(mapResults2.get("localTime"), is("10:10:20")); - assertThat(mapResults2.get("offsetTime"), is("08:10:01+0000")); - Object significantDates = mapResults2.get("significantDates"); - assertThat(significantDates, is(notNullValue())); - List listDates = (ArrayList) mapResults2.get("significantDates"); - assertThat(listDates.size(),is(2)); - assertThat(listDates.get(0), is("1968-02-17")); - assertThat(listDates.get(1), is("1970-08-04")); - - mapResults = getAndAssertResult( - executionContext.execute("query { localDateListFormat }")); - assertThat(mapResults, is(notNullValue())); - listDates = (ArrayList) mapResults.get("localDateListFormat"); - assertThat(listDates.size(),is(2)); - assertThat(listDates.get(0), is("17/02/1968")); - assertThat(listDates.get(1), is("04/08/1970")); - } - - @Test - public void testNulls() throws IOException { - setupIndex(indexFileName, NullPOJO.class, QueriesWithNulls.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - Schema schema = executionContext.getSchema(); - assertThat(schema, is(notNullValue())); - - // test primitives should be not null be default - SchemaType type = schema.getTypeByName("NullPOJO"); - assertReturnTypeMandatory(type, "id", true); - assertReturnTypeMandatory(type, "longValue", false); - assertReturnTypeMandatory(type, "stringValue", true); - assertReturnTypeMandatory(type, "testNullWithGet", true); - assertReturnTypeMandatory(type, "listNonNullStrings", false); - assertArrayReturnTypeMandatory(type, "listNonNullStrings", true); - assertArrayReturnTypeMandatory(type, "listOfListOfNonNullStrings", true); - assertReturnTypeMandatory(type, "listOfListOfNonNullStrings", false); - assertReturnTypeMandatory(type, "listOfListOfNullStrings", false); - assertArrayReturnTypeMandatory(type, "listOfListOfNullStrings", false); - assertReturnTypeMandatory(type, "testNullWithSet", false); - assertReturnTypeMandatory(type, "listNullStringsWhichIsMandatory", true); - assertArrayReturnTypeMandatory(type, "listNullStringsWhichIsMandatory", false); - assertReturnTypeMandatory(type, "testInputOnly", false); - assertArrayReturnTypeMandatory(type, "testInputOnly", false); - assertReturnTypeMandatory(type, "testOutputOnly", false); - assertArrayReturnTypeMandatory(type, "testOutputOnly", true); - - SchemaType query = schema.getTypeByName("Query"); - assertReturnTypeMandatory(query, "method1NotNull", true); - assertReturnTypeMandatory(query, "method2NotNull", true); - assertReturnTypeMandatory(query, "method3NotNull", false); - - assertReturnTypeArgumentMandatory(query, "paramShouldBeNonMandatory", "value", false); - assertReturnTypeArgumentMandatory(query, "paramShouldBeNonMandatory2", "value", false); - assertReturnTypeArgumentMandatory(query, "paramShouldBeNonMandatory3", "value", false); - - SchemaType input = schema.getInputTypeByName("NullPOJOInput"); - assertReturnTypeMandatory(input, "nonNullForInput", true); - assertReturnTypeMandatory(input, "testNullWithGet", false); - assertReturnTypeMandatory(input, "testNullWithSet", true); - assertReturnTypeMandatory(input, "listNonNullStrings", false); - assertArrayReturnTypeMandatory(input, "listNonNullStrings", true); - - assertArrayReturnTypeMandatory(input, "listOfListOfNonNullStrings", true); - - assertReturnTypeMandatory(input, "testInputOnly", false); - assertArrayReturnTypeMandatory(input, "testInputOnly", true); - - assertReturnTypeMandatory(input, "testOutputOnly", false); - assertArrayReturnTypeMandatory(input, "testOutputOnly", false); - } - - @Test - @SuppressWarnings("unchecked") - public void testSimpleMutations() throws IOException { - setupIndex(indexFileName, SimpleMutations.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Map mapResults = getAndAssertResult( - executionContext.execute("mutation { createNewContact { id name age } }")); - assertThat(mapResults.size(), is(1)); - Map mapResults2 = (Map) mapResults.get("createNewContact"); - - assertThat(mapResults2, is(notNullValue())); - assertThat(mapResults2.get("id"), is(notNullValue())); - assertThat(mapResults2.get("name"), is(notNullValue())); - assertThat(((String) mapResults2.get("name")).startsWith("Name"), is(true)); - assertThat(mapResults2.get("age"), is(notNullValue())); - - mapResults = getAndAssertResult( - executionContext.execute("mutation { createContactWithName(name: \"tim\") { id name age } }")); - assertThat(mapResults.size(), is(1)); - mapResults2 = (Map) mapResults.get("createContactWithName"); - - assertThat(mapResults2, is(notNullValue())); - assertThat(mapResults2.get("id"), is(notNullValue())); - assertThat(mapResults2.get("name"), is("tim")); - assertThat(mapResults2.get("age"), is(notNullValue())); - - mapResults = getAndAssertResult(executionContext.execute("mutation { echoStringValue(value: \"echo\") }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("echoStringValue"), is("echo")); - } - - @Test - @SuppressWarnings("unchecked") - public void testSimpleQueryGenerationNoArgs() throws IOException { - setupIndex(indexFileName, SimpleQueriesNoArgs.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Map mapResults = getAndAssertResult(executionContext.execute("query { hero }")); - - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("hero"), is("R2-D2")); - - mapResults = getAndAssertResult(executionContext.execute("query { episodeCount }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("episodeCount"), is(9)); - - mapResults = getAndAssertResult(executionContext.execute("query { badGuy }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("badGuy"), is("Darth Vader")); - - mapResults = getAndAssertResult(executionContext.execute("query { allPeople { personId } }")); - assertThat(mapResults.size(), is(1)); - - ArrayList> arrayList = (ArrayList>) mapResults.get("allPeople"); - assertThat(arrayList.size(), is(TestDB.MAX_PEOPLE)); - - mapResults = getAndAssertResult(executionContext.execute("query { returnMediumSize }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("returnMediumSize"), is("M")); - - mapResults = getAndAssertResult(executionContext.execute("query { returnCurrentDate }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("returnCurrentDate"), is(notNullValue())); - - mapResults = getAndAssertResult( - executionContext.execute("query { returnTypeWithIDs { intId integerId longId longPrimitiveId " - + "stringId uuidId } }")); - - assertThat(mapResults.size(), is(1)); - Map results = (Map) mapResults.get("returnTypeWithIDs"); - TypeWithIDs value = (TypeWithIDs) JsonUtils.convertFromJson(JsonUtils.convertMapToJson(results), TypeWithIDs.class); - assertThat(value, is(notNullValue())); - assertThat(value.getIntegerId(), is(2)); - assertThat(value.getIntId(), is(1)); - assertThat(value.getLongId(), is(10L)); - assertThat(value.getLongPrimitiveId(), is(10L)); - assertThat(value.getStringId(), is("string")); - assertThat(value.getUuidId(), is(notNullValue())); - - mapResults = getAndAssertResult(executionContext.execute("query { getMultiLevelList { listOfListOfBigDecimal } }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("getMultiLevelList"), is(notNullValue())); - - Schema schema = executionContext.getSchema(); - SchemaType query = schema.getTypeByName("Query"); - assertThat(query, is(notNullValue())); - SchemaFieldDefinition fd = getFieldDefinition(query, "idQuery"); - assertThat(fd, is(notNullValue())); - assertThat(fd.getReturnType(), is(ID)); - - assertThat(getFieldDefinition(query, "booleanObject"), is(notNullValue())); - assertThat(getFieldDefinition(query, "booleanPrimitive"), is(notNullValue())); - } - - @Test - public void testDifferentPropertyNames() throws IOException { - setupIndex(indexFileName, PropertyNameQueries.class, TypeWithNameAndJsonbProperty.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Map mapResults = getAndAssertResult( - executionContext.execute("query { query1 { newFieldName1 newFieldName2 " - + " newFieldName3 newFieldName4 newFieldName5 newFieldName6 } }")); - assertThat(mapResults.size(), is(1)); - - Map mapResults2 = (Map) mapResults.get("query1"); - assertThat(mapResults2.size(), is(6)); - for (int i = 1; i <= 6; i++) { - assertThat(mapResults2.get("newFieldName" + i), is("name" + i)); - } - } - - @Test - public void testIgnorable() throws IOException { - setupIndex(indexFileName, QueriesWithIgnorable.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - Map mapResults = getAndAssertResult( - executionContext.execute("query { testIgnorableFields { id dontIgnore } }")); - assertThat(mapResults.size(), is(1)); - - Map mapResults2 = (Map) mapResults.get("testIgnorableFields"); - assertThat(mapResults2.size(), is(2)); - assertThat(mapResults2.get("id"), is("id")); - assertThat(mapResults2.get("dontIgnore"), is(true)); - - // ensure getting the fields generates an error that is caught by the getAndAssertResult - assertThrows(AssertionFailedError.class, () -> getAndAssertResult(executionContext - .execute( - "query { testIgnorableFields { id " - + "dontIgnore pleaseIgnore " - + "ignoreThisAsWell } }"))); - - Schema schema = executionContext.getSchema(); - SchemaType type = schema.getTypeByName("ObjectWithIgnorableFieldsAndMethods"); - assertThat(type, is(notNullValue())); - assertThat(type.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreGetMethod")).count(), is(0L)); - - SchemaInputType inputType = schema.getInputTypeByName("ObjectWithIgnorableFieldsAndMethodsInput"); - assertThat(inputType, is(notNullValue())); - assertThat(inputType.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreBecauseOfMethod")).count(), - is(0L)); - assertThat(inputType.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("valueSetter")).count(), is(1L)); - } - - @Test - public void testDescriptions() throws IOException { - setupIndex(indexFileName, DescriptionType.class, DescriptionQueries.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Schema schema = executionContext.getSchema(); - assertThat(schema, is(notNullValue())); - SchemaType type = schema.getTypeByName("DescriptionType"); - assertThat(type, is(notNullValue())); - type.getFieldDefinitions().forEach(fd -> { - if (fd.getName().equals("id")) { - assertThat(fd.getDescription(), is("this is the description")); - } - if (fd.getName().equals("value")) { - assertThat(fd.getDescription(), is("description of value")); - } - if (fd.getName().equals("longValue1")) { - // no description so include the format - assertThat(fd.getDescription(), is(nullValue())); - assertThat(fd.getFormat()[0], is("L-########")); - } - if (fd.getName().equals("longValue2")) { - // both description and formatting - assertThat(fd.getDescription(), is("Description")); - } - }); - - SchemaInputType inputType = schema.getInputTypeByName("DescriptionTypeInput"); - assertThat(inputType, is(notNullValue())); - inputType.getFieldDefinitions().forEach(fd -> { - if (fd.getName().equals("value")) { - assertThat(fd.getDescription(), is("description on set for input")); - } - }); - - SchemaType query = schema.getTypeByName("Query"); - assertThat(query, is(notNullValue())); - SchemaFieldDefinition fd = getFieldDefinition(query, "descriptionOnParam"); - assertThat(fd, (is(notNullValue()))); - - fd.getArguments().forEach(a -> { - if (a.getArgumentName().equals("param1")) { - assertThat(a.getDescription(), is("Description for param1")); - } - }); - } - - @Test - public void testDefaultValues() throws IOException { - setupIndex(indexFileName, DefaultValuePOJO.class, DefaultValueQueries.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - // test with both fields as default - Map mapResults = getAndAssertResult( - executionContext.execute("mutation { generateDefaultValuePOJO { id value } }")); - assertThat(mapResults.size(), is(1)); - Map results = (Map) mapResults.get("generateDefaultValuePOJO"); - assertThat(results, is(notNullValue())); - assertThat(results.get("id"), is("ID-1")); - assertThat(results.get("value"), is(1000)); - - // test with a field overridden - mapResults = getAndAssertResult( - executionContext.execute("mutation { generateDefaultValuePOJO(id: \"ID-123\") { id value } }")); - assertThat(mapResults.size(), is(1)); - results = (Map) mapResults.get("generateDefaultValuePOJO"); - assertThat(results, is(notNullValue())); - assertThat(results.get("id"), is("ID-123")); - assertThat(results.get("value"), is(1000)); - - mapResults = getAndAssertResult(executionContext.execute("query { echoDefaultValuePOJO { id value } }")); - assertThat(mapResults.size(), is(1)); - results = (Map) mapResults.get("echoDefaultValuePOJO"); - assertThat(results, is(notNullValue())); - assertThat(results.get("id"), is("ID-1")); - assertThat(results.get("value"), is(1000)); - - mapResults = getAndAssertResult( - executionContext.execute("query { echoDefaultValuePOJO(input: {id: \"X123\" value: 1}) { id value } }")); - assertThat(mapResults.size(), is(1)); - results = (Map) mapResults.get("echoDefaultValuePOJO"); - assertThat(results, is(notNullValue())); - assertThat(results.get("id"), is("X123")); - assertThat(results.get("value"), is(1)); - - mapResults = getAndAssertResult( - executionContext.execute("query { echoDefaultValuePOJO(input: {value: 1}) { id value } }")); - assertThat(mapResults.size(), is(1)); - results = (Map) mapResults.get("echoDefaultValuePOJO"); - assertThat(results, is(notNullValue())); - assertThat(results.get("id"), is("ID-123")); - assertThat(results.get("value"), is(1)); - - Schema schema = executionContext.getSchema(); - SchemaType type = schema.getInputTypeByName("DefaultValuePOJOInput"); - assertReturnTypeDefaultValue(type, "id", "ID-123"); - assertReturnTypeDefaultValue(type, "booleanValue", "false"); - assertReturnTypeMandatory(type, "booleanValue", false); - - SchemaFieldDefinition fd = getFieldDefinition(type, "value"); - assertThat(fd, is(notNullValue())); - assertThat(fd.getDefaultValue(), is("111222")); - } - - @Test - public void setOddNamedQueriesAndMutations() throws IOException { - setupIndex(indexFileName, DefaultValuePOJO.class, OddNamedQueriesAndMutations.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Schema schema = executionContext.getSchema(); - assertThat(schema, is(notNullValue())); - SchemaType query = schema.getTypeByName("Query"); - SchemaType mutation = schema.getTypeByName("Mutation"); - assertThat(query, is(notNullValue())); - assertThat(query.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("settlement")).count(), is(1L)); - assertThat(mutation.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("getaway")).count(), is(1L)); - } - - @Test - @SuppressWarnings("unchecked") - public void testSimpleQueriesWithSource() throws IOException { - setupIndex(indexFileName, SimpleQueriesWithSource.class, SimpleContact.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - // since there is a @Source annotation in SimpleQueriesWithSource, then this should add a field - // idAndName to the SimpleContact type - Map mapResults = getAndAssertResult(executionContext.execute("query { findContact { id idAndName } }")); - - assertThat(mapResults.size(), is(1)); - Map mapResults2 = (Map) mapResults.get("findContact"); - assertThat(mapResults2, is(notNullValue())); - assertThat(mapResults2.get("id"), is(notNullValue())); - assertThat(mapResults2.get("idAndName"), is(notNullValue())); - - // test the query at the top level - SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50); - - String json = "contact: " + getContactAsQueryInput(contact1); - - mapResults = getAndAssertResult(executionContext.execute("query { currentJob (" + json + ") }")); - assertThat(mapResults.size(), is(1)); - String currentJob = (String) mapResults.get("currentJob"); - assertThat(currentJob, is(notNullValue())); - - // test the query from the object - mapResults = getAndAssertResult(executionContext.execute("query { findContact { id idAndName currentJob } }")); - assertThat(mapResults.size(), is(1)); - mapResults2 = (Map) mapResults.get("findContact"); - assertThat(mapResults2, is(notNullValue())); - assertThat(mapResults2.get("id"), is(notNullValue())); - assertThat(mapResults2.get("idAndName"), is(notNullValue())); - assertThat(mapResults2.get("currentJob"), is(notNullValue())); - - // test the query from the object - mapResults = getAndAssertResult(executionContext.execute("query { findContact { id lastNAddress(count: 1) { city } } }")); - assertThat(mapResults.size(), is(1)); - mapResults2 = (Map) mapResults.get("findContact"); - assertThat(mapResults2, is(notNullValue())); - assertThat(mapResults2.get("id"), is(notNullValue())); - assertThat(mapResults2.get("lastNAddress"), is(notNullValue())); - } - - @Test - public void testInputType() throws IOException { - setupIndex(indexFileName, SimpleContactInputType.class, SimpleContactInputTypeWithName.class, - SimpleContactInputTypeWithNameValue.class, SimpleContactInputTypeWithAddress.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - Schema schema = executionContext.getSchema(); - assertThat(schema.getInputTypes().size(), is(5)); - assertThat(schema.containsInputTypeWithName("MyInputType"), is(true)); - assertThat(schema.containsInputTypeWithName("SimpleContactInputTypeInput"), is(true)); - assertThat(schema.containsInputTypeWithName("NameInput"), is(true)); - assertThat(schema.containsInputTypeWithName("SimpleContactInputTypeWithAddressInput"), is(true)); - assertThat(schema.containsInputTypeWithName("AddressInput"), is(true)); - } - - @Test - @SuppressWarnings("unchecked") - public void testMultiLevelListsAndArraysQueries() throws IOException { - setupIndex(indexFileName, ArrayAndListQueries.class, MultiLevelListsAndArrays.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Map mapResults = getAndAssertResult( - executionContext.execute("query { getMultiLevelList { intMultiLevelArray } }")); - assertThat(mapResults.size(), is(1)); - Map mapResults2 = (Map) mapResults.get("getMultiLevelList"); - ArrayList> intArrayList = (ArrayList>) mapResults2.get("intMultiLevelArray"); - assertThat(intArrayList, is(notNullValue())); - ArrayList integerArrayList1 = intArrayList.get(0); - assertThat(integerArrayList1, is(notNullValue())); - assertThat(integerArrayList1.contains(1), is(true)); - assertThat(integerArrayList1.contains(2), is(true)); - assertThat(integerArrayList1.contains(3), is(true)); - - ArrayList integerArrayList2 = intArrayList.get(1); - assertThat(integerArrayList2, is(notNullValue())); - assertThat(integerArrayList2.contains(4), is(true)); - assertThat(integerArrayList2.contains(5), is(true)); - assertThat(integerArrayList2.contains(6), is(true)); - - mapResults = getAndAssertResult(executionContext.execute("query { returnListOfStringArrays }")); - assertThat(mapResults.size(), is(1)); - ArrayList> stringArrayList = (ArrayList>) mapResults.get("returnListOfStringArrays"); - assertThat(stringArrayList, is(notNullValue())); - - List stringList1 = stringArrayList.get(0); - assertThat(stringList1, is(notNullValue())); - assertThat(stringList1.contains("one"), is(true)); - assertThat(stringList1.contains("two"), is(true)); - List stringList2 = stringArrayList.get(1); - assertThat(stringList2, is(notNullValue())); - assertThat(stringList2.contains("three"), is(true)); - assertThat(stringList2.contains("four"), is(true)); - assertThat(stringList2.contains("five"), is(true)); - } - - @Test - @SuppressWarnings("unchecked") - public void testSimpleQueryGenerationWithArgs() throws IOException { - setupIndex(indexFileName, SimpleQueriesWithArgs.class, Car.class, AbstractVehicle.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); - - Map mapResults = getAndAssertResult(executionContext.execute("query { hero(heroType: \"human\") }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("hero"), is("Luke")); - - mapResults = getAndAssertResult(executionContext.execute("query { findLocalDates(numberOfValues: 10) }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("findLocalDates"), is(notNullValue())); - List listLocalDate = (List) mapResults.get("findLocalDates"); - assertThat(listLocalDate.size(), is(10)); - - mapResults = getAndAssertResult( - executionContext.execute("query { canFindContact(contact: { id: \"10\" name: \"tim\" age: 52 }) }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("canFindContact"), is(false)); - - mapResults = getAndAssertResult(executionContext.execute("query { hero(heroType: \"droid\") }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("hero"), is("R2-D2")); - - mapResults = getAndAssertResult(executionContext.execute("query { multiply(arg0: 10, arg1: 10) }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("multiply"), is(BigInteger.valueOf(100))); - - mapResults = getAndAssertResult(executionContext - .execute( - "query { findAPerson(personId: 1) { personId creditLimit workAddress { " - + "city state zipCode } } }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("findAPerson"), is(notNullValue())); - - mapResults = getAndAssertResult(executionContext.execute("query { findEnums(arg0: S) }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("findEnums"), is(notNullValue())); - - mapResults = getAndAssertResult(executionContext.execute("query { getMonthFromDate(date: \"2020-12-20\") }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("getMonthFromDate"), is("DECEMBER")); - - mapResults = getAndAssertResult(executionContext.execute("query { findOneEnum(enum: XL) }")); - assertThat(mapResults.size(), is(1)); - Collection listEnums = (Collection) mapResults.get("findOneEnum"); - assertThat(listEnums.size(), is(1)); - assertThat(listEnums.iterator().next(), is(EnumTestWithNameAnnotation.XL.toString())); - - mapResults = getAndAssertResult(executionContext.execute("query { returnIntegerAsId(param1: 123) }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("returnIntegerAsId"), is(123)); - - mapResults = getAndAssertResult(executionContext.execute("query { returnIntAsId(param1: 124) }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("returnIntAsId"), is(124)); - - mapResults = getAndAssertResult(executionContext.execute("query { returnStringAsId(param1: \"StringValue\") }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("returnStringAsId"), is("StringValue")); - - mapResults = getAndAssertResult(executionContext.execute("query { returnLongAsId(param1: " + Long.MAX_VALUE + ") }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("returnLongAsId"), is(BigInteger.valueOf(Long.MAX_VALUE))); - - mapResults = getAndAssertResult( - executionContext.execute("query { returnLongPrimitiveAsId(param1: " + Long.MAX_VALUE + ") }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("returnLongPrimitiveAsId"), is(BigInteger.valueOf(Long.MAX_VALUE))); - - UUID uuid = UUID.randomUUID(); - mapResults = getAndAssertResult(executionContext.execute("query { returnUUIDAsId(param1: \"" - + uuid.toString() + "\") }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("returnUUIDAsId"), is(uuid.toString())); - - mapResults = getAndAssertResult(executionContext.execute( - "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }")); - assertThat(mapResults.size(), is(1)); - assertThat(mapResults.get("findPeopleFromState"), is(notNullValue())); - ArrayList> arrayList = (ArrayList>) mapResults.get("findPeopleFromState"); - assertThat(arrayList, is(notNullValue())); - // since its random data we can't be sure if anyone was created in MA - assertThat(arrayList.size() >= 0, is(true)); - - mapResults = getAndAssertResult(executionContext.execute( - "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }")); - assertThat(mapResults.size(), is(1)); - - SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50); - SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 53); - ContactRelationship relationship = new ContactRelationship(contact1, contact2, "married"); - - String json = "relationship: {" - + " contact1: " + getContactAsQueryInput(contact1) - + " contact2: " + getContactAsQueryInput(contact2) - + " relationship: \"married\"" - + "}"; - mapResults = getAndAssertResult(executionContext.execute("query { canFindContactRelationship( " - + json + - ") }")); - assertThat(mapResults.size(), is(1)); - } - - private String getContactAsQueryInput(SimpleContact contact) { - return new StringBuilder("{") - .append("id: \"").append(contact.getId()).append("\" ") - .append("name: \"").append(contact.getName()).append("\" ") - .append("age: ").append(contact.getAge()) - .append("} ").toString(); - } - - private void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException { - SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); - Schema schema = schemaGenerator.generateSchema(); - System.out.println(schema.generateGraphQLSchema()); - assertThat(schema, is(notNullValue())); - schema.getTypes().forEach(t -> System.out.println(t.getName())); - assertThat(schema.getTypes().size(), is(6)); - assertThat(schema.getTypeByName("Vehicle"), is(notNullValue())); - assertThat(schema.getTypeByName("Car"), is(notNullValue())); - assertThat(schema.getTypeByName("Motorbike"), is(notNullValue())); - assertThat(schema.getTypeByName("Incident"), is(notNullValue())); - assertThat(schema.getTypeByName("Query"), is(notNullValue())); - assertThat(schema.getTypeByName("Mutation"), is(notNullValue())); - generateGraphQLSchema(schema); - } - - private void assertReturnTypeDefaultValue(SchemaType type, String fdName, String defaultValue) { - assertThat(type, is(notNullValue())); - SchemaFieldDefinition fd = getFieldDefinition(type, fdName); - assertThat(fd, is(notNullValue())); - assertThat("Default value for " + fdName + " should be " + defaultValue + - " but is " + fd.getDefaultValue(), fd.getDefaultValue(), is(defaultValue)); - } - - private void assertReturnTypeMandatory(SchemaType type, String fdName, boolean mandatory) { - assertThat(type, is(notNullValue())); - SchemaFieldDefinition fd = getFieldDefinition(type, fdName); - assertThat(fd, is(notNullValue())); - assertThat("Return type for " + fdName + " should be mandatory=" + mandatory + - " but is " + fd.isReturnTypeMandatory(), fd.isReturnTypeMandatory(), is(mandatory)); - } - - private void assertArrayReturnTypeMandatory(SchemaType type, String fdName, boolean mandatory) { - assertThat(type, is(notNullValue())); - SchemaFieldDefinition fd = getFieldDefinition(type, fdName); - assertThat(fd, is(notNullValue())); - assertThat("Array return type for " + fdName + " should be mandatory=" + mandatory + - " but is " + fd.isArrayReturnTypeMandatory(), fd.isArrayReturnTypeMandatory(), is(mandatory)); - } - - private void assertReturnTypeArgumentMandatory(SchemaType type, String fdName, String argumentName, boolean mandatory) { - assertThat(type, is(notNullValue())); - SchemaFieldDefinition fd = getFieldDefinition(type, fdName); - assertThat(fd, is(notNullValue())); - SchemaArgument argument = getArgument(fd, argumentName); - assertThat(argument, is(notNullValue())); - assertThat("Return type for argument " + argumentName + " should be mandatory=" - + mandatory + " but is " + argument.isMandatory(), argument.isMandatory(), is(mandatory)); - } - - private void assertDefaultFormat(SchemaType type, String fdName, String defaultFormat) { - assertThat(type, is(notNullValue())); - SchemaFieldDefinition fd = getFieldDefinition(type, fdName); - assertThat(fd, is(notNullValue())); - String[] format = fd.getFormat(); - assertThat(format, is(notNullValue())); - assertThat(format.length == 2, is(notNullValue())); - assertThat(format[0], is(defaultFormat)); - } - - private SchemaFieldDefinition getFieldDefinition(SchemaType type, String name) { - for (SchemaFieldDefinition fd : type.getFieldDefinitions()) { - if (fd.getName().equals(name)) { - return fd; - } - } - return null; - } - - private SchemaArgument getArgument(SchemaFieldDefinition fd, String name) { - assertThat(fd, is(notNullValue())); - for (SchemaArgument argument : fd.getArguments()) { - if (argument.getArgumentName().equals(name)) { - return argument; - } - } - return null; - } - } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java new file mode 100644 index 00000000000..0f130b93130 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java @@ -0,0 +1,63 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputType; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithAddress; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithName; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithNameValue; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class SimpleMutationsIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(SimpleMutations.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + @SuppressWarnings("unchecked") + public void testSimpleMutations() throws IOException { + setupIndex(indexFileName, SimpleMutations.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Map mapResults = getAndAssertResult( + executionContext.execute("mutation { createNewContact { id name age } }")); + assertThat(mapResults.size(), is(1)); + Map mapResults2 = (Map) mapResults.get("createNewContact"); + + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("id"), is(notNullValue())); + assertThat(mapResults2.get("name"), is(notNullValue())); + assertThat(((String) mapResults2.get("name")).startsWith("Name"), is(true)); + assertThat(mapResults2.get("age"), is(notNullValue())); + + mapResults = getAndAssertResult( + executionContext.execute("mutation { createContactWithName(name: \"tim\") { id name age } }")); + assertThat(mapResults.size(), is(1)); + mapResults2 = (Map) mapResults.get("createContactWithName"); + + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("id"), is(notNullValue())); + assertThat(mapResults2.get("name"), is("tim")); + assertThat(mapResults2.get("age"), is(notNullValue())); + + mapResults = getAndAssertResult(executionContext.execute("mutation { echoStringValue(value: \"echo\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("echoStringValue"), is("echo")); + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesIT.java new file mode 100644 index 00000000000..1ef9b860ade --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesIT.java @@ -0,0 +1,156 @@ +package io.helidon.microprofile.graphql.server; + +import java.beans.IntrospectionException; +import java.io.IOException; +import java.math.BigInteger; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; +import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; +import io.helidon.microprofile.graphql.server.test.types.Car; +import io.helidon.microprofile.graphql.server.test.types.ContactRelationship; +import io.helidon.microprofile.graphql.server.test.types.SimpleContact; + +import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class SimpleQueriesIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(SimpleQueriesWithArgs.class) + .addBeanClass(Car.class) + .addBeanClass(SimpleContactWithSelf.class) + .addBeanClass(AbstractVehicle.class) + .addBeanClass(TestDB.class) + + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testSimpleContactWithSelf() throws IOException { + setupIndex(indexFileName, SimpleContactWithSelf.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + executionContext.execute("query { hero }"); + } + + @Test + @SuppressWarnings("unchecked") + public void testSimpleQueryGenerationWithArgs() throws IOException { + setupIndex(indexFileName, SimpleQueriesWithArgs.class, Car.class, AbstractVehicle.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Map mapResults = getAndAssertResult(executionContext.execute("query { hero(heroType: \"human\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("hero"), is("Luke")); + + mapResults = getAndAssertResult(executionContext.execute("query { findLocalDates(numberOfValues: 10) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findLocalDates"), is(notNullValue())); + List listLocalDate = (List) mapResults.get("findLocalDates"); + assertThat(listLocalDate.size(), is(10)); + + mapResults = getAndAssertResult( + executionContext.execute("query { canFindContact(contact: { id: \"10\" name: \"tim\" age: 52 }) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("canFindContact"), is(false)); + + mapResults = getAndAssertResult(executionContext.execute("query { hero(heroType: \"droid\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("hero"), is("R2-D2")); + + mapResults = getAndAssertResult(executionContext.execute("query { multiply(arg0: 10, arg1: 10) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("multiply"), is(BigInteger.valueOf(100))); + + mapResults = getAndAssertResult(executionContext + .execute( + "query { findAPerson(personId: 1) { personId creditLimit workAddress { " + + "city state zipCode } } }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findAPerson"), is(notNullValue())); + + mapResults = getAndAssertResult(executionContext.execute("query { findEnums(arg0: S) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findEnums"), is(notNullValue())); + + mapResults = getAndAssertResult(executionContext.execute("query { getMonthFromDate(date: \"2020-12-20\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("getMonthFromDate"), is("DECEMBER")); + + mapResults = getAndAssertResult(executionContext.execute("query { findOneEnum(enum: XL) }")); + assertThat(mapResults.size(), is(1)); + Collection listEnums = (Collection) mapResults.get("findOneEnum"); + assertThat(listEnums.size(), is(1)); + assertThat(listEnums.iterator().next(), is(EnumTestWithNameAnnotation.XL.toString())); + + mapResults = getAndAssertResult(executionContext.execute("query { returnIntegerAsId(param1: 123) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnIntegerAsId"), is(123)); + + mapResults = getAndAssertResult(executionContext.execute("query { returnIntAsId(param1: 124) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnIntAsId"), is(124)); + + mapResults = getAndAssertResult(executionContext.execute("query { returnStringAsId(param1: \"StringValue\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnStringAsId"), is("StringValue")); + + mapResults = getAndAssertResult(executionContext.execute("query { returnLongAsId(param1: " + Long.MAX_VALUE + ") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnLongAsId"), is(BigInteger.valueOf(Long.MAX_VALUE))); + + mapResults = getAndAssertResult( + executionContext.execute("query { returnLongPrimitiveAsId(param1: " + Long.MAX_VALUE + ") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnLongPrimitiveAsId"), is(BigInteger.valueOf(Long.MAX_VALUE))); + + UUID uuid = UUID.randomUUID(); + mapResults = getAndAssertResult(executionContext.execute("query { returnUUIDAsId(param1: \"" + + uuid.toString() + "\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnUUIDAsId"), is(uuid.toString())); + + mapResults = getAndAssertResult(executionContext.execute( + "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findPeopleFromState"), is(notNullValue())); + ArrayList> arrayList = (ArrayList>) mapResults.get("findPeopleFromState"); + assertThat(arrayList, is(notNullValue())); + // since its random data we can't be sure if anyone was created in MA + assertThat(arrayList.size() >= 0, is(true)); + + mapResults = getAndAssertResult(executionContext.execute( + "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }")); + assertThat(mapResults.size(), is(1)); + + SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50); + SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 53); + ContactRelationship relationship = new ContactRelationship(contact1, contact2, "married"); + + String json = "relationship: {" + + " contact1: " + getContactAsQueryInput(contact1) + + " contact2: " + getContactAsQueryInput(contact2) + + " relationship: \"married\"" + + "}"; + mapResults = getAndAssertResult(executionContext.execute("query { canFindContactRelationship( " + + json + + ") }")); + assertThat(mapResults.size(), is(1)); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java new file mode 100644 index 00000000000..38e1a5bf4b0 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java @@ -0,0 +1,158 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; +import java.math.BigInteger; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; +import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; +import io.helidon.microprofile.graphql.server.test.types.Car; +import io.helidon.microprofile.graphql.server.test.types.ContactRelationship; +import io.helidon.microprofile.graphql.server.test.types.SimpleContact; + +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class SimpleQueriesWithArgsIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(SimpleQueriesWithArgs.class) + .addBeanClass(Car.class) + .addBeanClass(AbstractVehicle.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + @SuppressWarnings("unchecked") + public void testSimpleQueryGenerationWithArgs() throws IOException { + setupIndex(indexFileName, SimpleQueriesWithArgs.class, Car.class, AbstractVehicle.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Map mapResults = getAndAssertResult(executionContext.execute("query { hero(heroType: \"human\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("hero"), is("Luke")); + + mapResults = getAndAssertResult(executionContext.execute("query { findLocalDates(numberOfValues: 10) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findLocalDates"), is(notNullValue())); + List listLocalDate = (List) mapResults.get("findLocalDates"); + assertThat(listLocalDate.size(), is(10)); + + mapResults = getAndAssertResult( + executionContext.execute("query { canFindContact(contact: { id: \"10\" name: \"tim\" age: 52 }) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("canFindContact"), is(false)); + + mapResults = getAndAssertResult(executionContext.execute("query { hero(heroType: \"droid\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("hero"), is("R2-D2")); + + mapResults = getAndAssertResult(executionContext.execute("query { multiply(arg0: 10, arg1: 10) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("multiply"), is(BigInteger.valueOf(100))); + + mapResults = getAndAssertResult(executionContext + .execute( + "query { findAPerson(personId: 1) { personId creditLimit workAddress { " + + "city state zipCode } } }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findAPerson"), is(notNullValue())); + + mapResults = getAndAssertResult(executionContext.execute("query { findEnums(arg0: S) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findEnums"), is(notNullValue())); + + mapResults = getAndAssertResult(executionContext.execute("query { getMonthFromDate(date: \"2020-12-20\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("getMonthFromDate"), is("DECEMBER")); + + mapResults = getAndAssertResult(executionContext.execute("query { findOneEnum(enum: XL) }")); + assertThat(mapResults.size(), is(1)); + Collection listEnums = (Collection) mapResults.get("findOneEnum"); + assertThat(listEnums.size(), is(1)); + assertThat(listEnums.iterator().next(), is(EnumTestWithNameAnnotation.XL.toString())); + + mapResults = getAndAssertResult(executionContext.execute("query { returnIntegerAsId(param1: 123) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnIntegerAsId"), is(123)); + + mapResults = getAndAssertResult(executionContext.execute("query { returnIntAsId(param1: 124) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnIntAsId"), is(124)); + + mapResults = getAndAssertResult(executionContext.execute("query { returnStringAsId(param1: \"StringValue\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnStringAsId"), is("StringValue")); + + mapResults = getAndAssertResult(executionContext.execute("query { returnLongAsId(param1: " + Long.MAX_VALUE + ") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnLongAsId"), is(BigInteger.valueOf(Long.MAX_VALUE))); + + mapResults = getAndAssertResult( + executionContext.execute("query { returnLongPrimitiveAsId(param1: " + Long.MAX_VALUE + ") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnLongPrimitiveAsId"), is(BigInteger.valueOf(Long.MAX_VALUE))); + + UUID uuid = UUID.randomUUID(); + mapResults = getAndAssertResult(executionContext.execute("query { returnUUIDAsId(param1: \"" + + uuid.toString() + "\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("returnUUIDAsId"), is(uuid.toString())); + + mapResults = getAndAssertResult(executionContext.execute( + "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("findPeopleFromState"), is(notNullValue())); + ArrayList> arrayList = (ArrayList>) mapResults.get("findPeopleFromState"); + assertThat(arrayList, is(notNullValue())); + // since its random data we can't be sure if anyone was created in MA + assertThat(arrayList.size() >= 0, is(true)); + + mapResults = getAndAssertResult(executionContext.execute( + "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }")); + assertThat(mapResults.size(), is(1)); + + SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50); + SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 53); + ContactRelationship relationship = new ContactRelationship(contact1, contact2, "married"); + + String json = "relationship: {" + + " contact1: " + getContactAsQueryInput(contact1) + + " contact2: " + getContactAsQueryInput(contact2) + + " relationship: \"married\"" + + "}"; + mapResults = getAndAssertResult(executionContext.execute("query { canFindContactRelationship( " + + json + + ") }")); + assertThat(mapResults.size(), is(1)); + } + + @Test + public void testQueriesWithVariables() throws IOException { + setupIndex(indexFileName, SimpleQueriesWithArgs.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Map mapVariables = Map.of("first", 10, "second", 20); + Map mapResults = getAndAssertResult(executionContext.execute( + "query additionQuery($first: Int!, $second: Int!) {" + + " additionQuery(value1: $first, value2: $second) }", "additionQuery", mapVariables)); + assertThat(mapResults, is(notNullValue())); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("additionQuery"), is(30)); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java new file mode 100644 index 00000000000..29e0758ad49 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java @@ -0,0 +1,76 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; +import io.helidon.microprofile.graphql.server.test.queries.DefaultValueQueries; +import io.helidon.microprofile.graphql.server.test.queries.OddNamedQueriesAndMutations; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; +import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO; +import io.helidon.microprofile.graphql.server.test.types.SimpleContact; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WeldJunit5Extension.class) +public class SourceIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(SimpleQueriesWithSource.class) + .addBeanClass(SimpleContact.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + @SuppressWarnings("unchecked") + public void testSimpleQueriesWithSource() throws IOException { + setupIndex(indexFileName, SimpleQueriesWithSource.class, SimpleContact.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + // since there is a @Source annotation in SimpleQueriesWithSource, then this should add a field + // idAndName to the SimpleContact type + Map mapResults = getAndAssertResult(executionContext.execute("query { findContact { id idAndName } }")); + + assertThat(mapResults.size(), is(1)); + Map mapResults2 = (Map) mapResults.get("findContact"); + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("id"), is(notNullValue())); + assertThat(mapResults2.get("idAndName"), is(notNullValue())); + + // test the query at the top level + SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50); + + String json = "contact: " + getContactAsQueryInput(contact1); + + mapResults = getAndAssertResult(executionContext.execute("query { currentJob (" + json + ") }")); + assertThat(mapResults.size(), is(1)); + String currentJob = (String) mapResults.get("currentJob"); + assertThat(currentJob, is(notNullValue())); + + // test the query from the object + mapResults = getAndAssertResult(executionContext.execute("query { findContact { id idAndName currentJob } }")); + assertThat(mapResults.size(), is(1)); + mapResults2 = (Map) mapResults.get("findContact"); + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("id"), is(notNullValue())); + assertThat(mapResults2.get("idAndName"), is(notNullValue())); + assertThat(mapResults2.get("currentJob"), is(notNullValue())); + + // test the query from the object + mapResults = getAndAssertResult(executionContext.execute("query { findContact { id lastNAddress(count: 1) { city } } }")); + assertThat(mapResults.size(), is(1)); + mapResults2 = (Map) mapResults.get("findContact"); + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("id"), is(notNullValue())); + assertThat(mapResults2.get("lastNAddress"), is(notNullValue())); + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java index a32b34de99f..5f8bbcec832 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java @@ -16,28 +16,14 @@ package io.helidon.microprofile.graphql.server.test.mutations; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; -import javax.json.bind.annotation.JsonbProperty; import io.helidon.microprofile.graphql.server.test.db.TestDB; -import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; -import io.helidon.microprofile.graphql.server.test.types.ContactRelationship; -import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; import org.eclipse.microprofile.graphql.GraphQLApi; -import org.eclipse.microprofile.graphql.Id; import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Name; -import org.eclipse.microprofile.graphql.Query; /** * Class that holds simple mutations definitions with various numbers of arguments. @@ -77,5 +63,4 @@ public String testId(@Name("name") String name, @Name("id") Long idNumber) { public SimpleContact createNewContact(@Name("newContact") SimpleContact contact) { return contact; } - } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/AbstractVehicle.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/AbstractVehicle.java index fc4c363e540..91b8477d45b 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/AbstractVehicle.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/AbstractVehicle.java @@ -20,7 +20,7 @@ import java.util.Collection; /** - * Abstact implementation of a {@link Vehicle}. + * Abstract implementation of a {@link Vehicle}. */ public abstract class AbstractVehicle implements Vehicle { private String plate; From 186b3ded4da6782712f39b33c8b64cdb48f36c76 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 11 Sep 2020 10:34:21 +0800 Subject: [PATCH 081/178] Fixup failing tests after moving to CDI discovery --- .../graphql/server/CustomScalars.java | 2 + .../server/AbstractGraphQLEndpointIT.java | 3 + .../graphql/server/DateTimeIT.java | 37 ++-- .../graphql/server/DefaultValuesIT.java | 23 ++- .../graphql/server/DescriptionIT.java | 16 ++ .../server/ErrorConditionsTestContainer.java | 190 ++++++++++++++++++ .../graphql/server/ExceptionHandlingIT.java | 44 ++-- .../graphql/server/GraphQLEndpointIT.java | 7 +- .../graphql/server/IngorableIT.java | 16 ++ .../graphql/server/InputTypeIT.java | 16 ++ .../server/InterfaceOnlyAnnotatedIT.java | 16 ++ .../server/InterfaceTypeOnlyAnnotatedIT.java | 16 ++ .../graphql/server/JandexUtilsIT.java | 54 +++++ .../graphql/server/MultiLevelArraysIT.java | 26 ++- .../microprofile/graphql/server/NullIT.java | 23 ++- .../graphql/server/NumberFormatIT.java | 34 ++-- .../{PersonIT.java => PojoNamingIT.java} | 27 ++- .../graphql/server/PropertyNameIT.java | 17 ++ .../graphql/server/SchemaGeneratorIT.java | 180 ----------------- .../graphql/server/SimpleMutationsIT.java | 21 +- .../graphql/server/SimpleQueriesIT.java | 20 +- .../server/SimpleQueriesWithArgsIT.java | 23 ++- .../microprofile/graphql/server/SourceIT.java | 23 ++- 23 files changed, 583 insertions(+), 251 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{PersonIT.java => PojoNamingIT.java} (76%) delete mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java index 355e226af73..46d4fbf7f28 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java @@ -87,6 +87,7 @@ private CustomScalars() { * * @return a new custom date/time scalar */ + @SuppressWarnings("unchecked") public static GraphQLScalarType newDateTimeScalar() { GraphQLScalarType originalScalar = ExtendedScalars.DateTime; Coercing originalCoercing = originalScalar.getCoercing(); @@ -119,6 +120,7 @@ public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralExce * * @return a new custom time scalar */ + @SuppressWarnings("unchecked") public static GraphQLScalarType newTimeScalar() { GraphQLScalarType originalScalar = ExtendedScalars.Time; Coercing originalCoercing = originalScalar.getCoercing(); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java index c5bc2a7b9a1..81d5b1e2ab7 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java @@ -23,6 +23,8 @@ import java.util.logging.Level; import java.util.logging.Logger; +import io.helidon.microprofile.graphql.server.test.types.Person; +import io.helidon.microprofile.server.Server; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.WebTarget; @@ -32,6 +34,7 @@ import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.logging.LoggingFeature; + import org.junit.jupiter.api.AfterAll; import static org.hamcrest.CoreMatchers.is; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java index af2f116dea4..6874991bf7d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java @@ -1,6 +1,21 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; -import java.beans.IntrospectionException; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -11,17 +26,10 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; -import io.helidon.microprofile.graphql.server.ExecutionContext; -import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; -import io.helidon.microprofile.graphql.server.Schema; -import io.helidon.microprofile.graphql.server.SchemaFieldDefinition; -import io.helidon.microprofile.graphql.server.SchemaGenerator; -import io.helidon.microprofile.graphql.server.SchemaType; import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; -import io.helidon.microprofile.graphql.server.test.types.Person; + import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; @@ -38,10 +46,11 @@ public class DateTimeIT extends AbstractGraphQLIT { .addBeanClass(TestDB.class) .addExtension(new GraphQLCdiExtension())); - @Test + @Test + @SuppressWarnings("unchecked") public void testDateAndTime() throws IOException { setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesNoArgs.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); + ExecutionContext executionContext = new ExecutionContext(defaultContext); Schema schema = executionContext.getSchema(); SchemaType type = schema.getTypeByName("DateTimePojo"); @@ -77,7 +86,7 @@ public void testDateAndTime() throws IOException { Map mapResults = getAndAssertResult( executionContext.execute("query { dateAndTimePOJOQuery { offsetDateTime offsetTime zonedDateTime " - + "localDate localDate2 localTime localDateTime significantDates } }")); + + "localDate localDate2 localTime localDateTime significantDates } }")); assertThat(mapResults.size(), is(1)); Map mapResults2 = (Map) mapResults.get("dateAndTimePOJOQuery"); assertThat(mapResults2, is(notNullValue())); @@ -90,7 +99,7 @@ public void testDateAndTime() throws IOException { Object significantDates = mapResults2.get("significantDates"); assertThat(significantDates, is(notNullValue())); List listDates = (ArrayList) mapResults2.get("significantDates"); - assertThat(listDates.size(),is(2)); + assertThat(listDates.size(), is(2)); assertThat(listDates.get(0), is("1968-02-17")); assertThat(listDates.get(1), is("1970-08-04")); @@ -98,7 +107,7 @@ public void testDateAndTime() throws IOException { executionContext.execute("query { localDateListFormat }")); assertThat(mapResults, is(notNullValue())); listDates = (ArrayList) mapResults.get("localDateListFormat"); - assertThat(listDates.size(),is(2)); + assertThat(listDates.size(), is(2)); assertThat(listDates.get(0), is("17/02/1968")); assertThat(listDates.get(1), is("04/08/1970")); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java index 37269a222f9..0dac4549b75 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; @@ -20,6 +36,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +/** + * Tests for default values. + */ @ExtendWith(WeldJunit5Extension.class) public class DefaultValuesIT extends AbstractGraphQLIT { @@ -33,6 +52,7 @@ public class DefaultValuesIT extends AbstractGraphQLIT { @Test + @SuppressWarnings("unchecked") public void setOddNamedQueriesAndMutations() throws IOException { setupIndex(indexFileName, DefaultValuePOJO.class, OddNamedQueriesAndMutations.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); @@ -48,6 +68,7 @@ public void setOddNamedQueriesAndMutations() throws IOException { @Test + @SuppressWarnings("unchecked") public void testDefaultValues() throws IOException { setupIndex(indexFileName, DefaultValuePOJO.class, DefaultValueQueries.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); @@ -103,6 +124,4 @@ public void testDefaultValues() throws IOException { assertThat(fd, is(notNullValue())); assertThat(fd.getDefaultValue(), is("111222")); } - - } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java index 1ee59e6c1ee..4802d1ea5ed 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java new file mode 100644 index 00000000000..8b289b440c7 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations; + +import io.helidon.microprofile.graphql.server.test.queries.DuplicateNameQueries; +import io.helidon.microprofile.graphql.server.test.queries.InvalidQueries; +import io.helidon.microprofile.graphql.server.test.queries.VoidQueries; +import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Container for Error conditions tests. + */ +public class ErrorConditionsTestContainer { + + @ExtendWith(WeldJunit5Extension.class) + public static class VoidMutationsIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(VoidMutations.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testVoidMutations() throws IOException { + setupIndex(indexFileName, VoidMutations.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + + @ExtendWith(WeldJunit5Extension.class) + public static class VoidQueriesIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(VoidMutations.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testVoidQueries() throws IOException { + setupIndex(indexFileName, VoidQueries.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + + @ExtendWith(WeldJunit5Extension.class) + public static class InvalidQueriesIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(VoidMutations.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testInvalidQueries() throws IOException { + setupIndex(indexFileName, InvalidQueries.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + + @ExtendWith(WeldJunit5Extension.class) + public static class DuplicateQueriesAndMutationsIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(VoidMutations.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testDuplicateQueryOrMutationNames() throws IOException { + setupIndex(indexFileName, DuplicateNameQueries.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + + @ExtendWith(WeldJunit5Extension.class) + public static class InvalidNamedTypeIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testInvalidNamedType() throws IOException { + setupIndex(indexFileName, InvalidNamedTypes.InvalidNamedPerson.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + + @ExtendWith(WeldJunit5Extension.class) + public static class InvalidNamedInputTypeIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testInvalidNamedInputType() throws IOException { + setupIndex(indexFileName, InvalidNamedTypes.InvalidInputType.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + + @ExtendWith(WeldJunit5Extension.class) + public static class InvalidNamedInterfaceIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testInvalidNamedInterface() throws IOException { + setupIndex(indexFileName, InvalidNamedTypes.InvalidInterface.class, InvalidNamedTypes.InvalidClass.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + + @ExtendWith(WeldJunit5Extension.class) + public static class InvalidNamedQueryIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testInvalidNamedQuery() throws IOException { + setupIndex(indexFileName, InvalidNamedTypes.ClassWithInvalidQuery.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + + @ExtendWith(WeldJunit5Extension.class) + public static class InvalidNamedMutationIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testInvalidNamedMutation() throws IOException { + setupIndex(indexFileName, InvalidNamedTypes.ClassWithInvalidMutation.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + + @ExtendWith(WeldJunit5Extension.class) + public static class InvalidNamedEnumIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) + .addExtension(new GraphQLCdiExtension())); + + @Test + public void testInvalidNameEnum() throws IOException { + setupIndex(indexFileName, InvalidNamedTypes.Size.class); + assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); + } + } + +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java index c28c243d729..13f3806f330 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java @@ -22,13 +22,19 @@ import java.util.Map; import io.helidon.config.mp.MpConfigSources; + +import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.hamcrest.Matchers; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; @@ -39,15 +45,22 @@ * Integration tests for testing exception handing in {@link SchemaGeneratorTest}. */ @SuppressWarnings("unchecked") -public class ExceptionHandlingIT - extends AbstractGraphQLIT { +@ExtendWith(WeldJunit5Extension.class) +public class ExceptionHandlingIT extends AbstractGraphQLIT { + + @WeldSetup + private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() + .addBeanClass(ExceptionQueries.class) + .addBeanClass(SimpleContact.class) + .addBeanClass(TestDB.class) + .addExtension(new GraphQLCdiExtension())); @Test public void testAllDefaultsForConfig() throws IOException { setupConfig(null); setupIndex(indexFileName); - ExecutionContext executionContext = new ExecutionContext(defaultContext); + ExecutionContext executionContext = new ExecutionContext(defaultContext); assertThat(executionContext, is(notNullValue())); assertThat(executionContext.getDefaultErrorMessage(), is("Server Error")); assertThat(executionContext.getExceptionBlacklist().size(), is(0)); @@ -61,7 +74,7 @@ public void testDifferentMessage() throws IOException { setupIndex(indexFileName); - ExecutionContext executionContext = new ExecutionContext(defaultContext); + ExecutionContext executionContext = new ExecutionContext(defaultContext); assertThat(executionContext.getDefaultErrorMessage(), is("new message")); } @@ -71,7 +84,7 @@ public void testBlackListAndWhiteList() throws IOException { setupIndex(indexFileName); - ExecutionContext executionContext = new ExecutionContext(defaultContext); + ExecutionContext executionContext = new ExecutionContext(defaultContext); assertThat(executionContext.getDefaultErrorMessage(), is("Server Error")); assertThat(executionContext.getExceptionBlacklist().size(), is(2)); assertThat(executionContext.getExceptionWhitelist().size(), is(1)); @@ -85,7 +98,7 @@ public void testBlackListAndWhiteList() throws IOException { @Test public void testEmptyErrorPayloads() throws IOException { setupIndex(indexFileName, ExceptionQueries.class); - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); Map errorMap = executionContext.newErrorPayload(); assertPayload(errorMap); @@ -98,7 +111,7 @@ public void testEmptyErrorPayloads() throws IOException { @Test public void testErrorPayLoadWithMessages() throws IOException { setupIndex(indexFileName, ExceptionQueries.class); - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); Map errorMap = executionContext.newErrorPayload(); assertPayload(errorMap); @@ -119,7 +132,7 @@ public void testErrorPayLoadWithMessages() throws IOException { @Test public void testErrorPayLoadWithMessagesAndLocations() throws IOException { setupIndex(indexFileName, ExceptionQueries.class); - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); Map errorMap = executionContext.newErrorPayload(); assertPayload(errorMap); @@ -151,7 +164,7 @@ public void testErrorPayLoadWithMessagesAndLocations() throws IOException { @Test public void testErrorPayLoadWithMessagesLocationsAndExtensions() throws IOException { setupIndex(indexFileName, ExceptionQueries.class); - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); Map errorMap = executionContext.newErrorPayload(); assertPayload(errorMap); @@ -186,8 +199,8 @@ public void testUnknownField() throws IOException { setupConfig(null); setupIndex(indexFileName, ExceptionQueries.class, SimpleContact.class); assertMessageValue("query { defaultContact { invalidField } }", - "Validation error of type FieldUndefined: Field 'invalidField' in" + - " type 'SimpleContact' is undefined @ 'defaultContact/invalidField'", true); + "Validation error of type FieldUndefined: Field 'invalidField' in" + + " type 'SimpleContact' is undefined @ 'defaultContact/invalidField'", true); } @Test @@ -213,7 +226,7 @@ public void testWhiteListOfCheckedException() throws IOException { } private void assertMessageValue(String query, String expectedMessage, boolean dataExpected) { - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); Map mapResults = executionContext.execute(query); if (dataExpected && mapResults.size() != 2) { System.out.println(JsonUtils.convertMapToJson(mapResults)); @@ -238,9 +251,10 @@ protected Config setupConfig(String propertiesFile) { Config config = propertiesFile == null ? ConfigProviderResolver.instance().getBuilder().build() : ConfigProviderResolver.instance() - .getBuilder() - .withSources(MpConfigSources.create(ExceptionHandlingIT.class.getClassLoader().getResource(propertiesFile))) - .build(); + .getBuilder() + .withSources( + MpConfigSources.create(ExceptionHandlingIT.class.getClassLoader().getResource(propertiesFile))) + .build(); ConfigProviderResolver.instance() .registerConfig(config, Thread.currentThread().getContextClassLoader()); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java index 58e71244fbf..ca784d622a1 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java @@ -28,7 +28,9 @@ import io.helidon.microprofile.graphql.server.test.types.Person; import org.hamcrest.CoreMatchers; + import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static io.helidon.microprofile.graphql.server.GraphQLResource.SCHEMA_URL; @@ -39,8 +41,7 @@ /** * Integration tests for microprofile-graphql implementation via /graphql endpoint. */ -public class GraphQLEndpointIT - extends AbstractGraphQLEndpointIT { +public class GraphQLEndpointIT extends AbstractGraphQLEndpointIT { @BeforeAll public static void setup() throws IOException { @@ -48,6 +49,7 @@ public static void setup() throws IOException { } @Test + @Disabled("COH-21920") public void basicEndpointTests() throws UnsupportedEncodingException { // test /graphql endpoint WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL); @@ -87,6 +89,7 @@ public void testUIEndpoint() throws InterruptedException { } @Test + @Disabled("COH-21920") public void testGetSchema() { // see https://github.com/eclipse/microprofile-graphql/issues/202 // add text/plain diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java index 0aaec5fba20..8579f2a0a52 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java index 4f2d1f08615..5ac2d942d90 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java index 55fe7d54ed4..61b00d5f016 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.beans.IntrospectionException; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java index d928085da4d..b77a3282768 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.beans.IntrospectionException; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java new file mode 100644 index 00000000000..439c1cd297b --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; + +import java.io.IOException; + +import io.helidon.microprofile.graphql.server.test.queries.QueriesWithNulls; +import io.helidon.microprofile.graphql.server.test.types.NullPOJO; + +import org.eclipse.microprofile.graphql.NonNull; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Integration tests for {@link SchemaGeneratorTest}. + */ +public class JandexUtilsIT extends AbstractGraphQLIT { + + @Test + public void testJandexUtils() throws IOException { + setupIndex(indexFileName, NullPOJO.class, QueriesWithNulls.class); + SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); + String queriesWithNulls = QueriesWithNulls.class.getName(); + String nullPOJO = NullPOJO.class.getName(); + String noNull = NonNull.class.getName(); + + JandexUtils jandexUtils = schemaGenerator.getJandexUtils(); + + assertThat(jandexUtils.methodParameterHasAnnotation(queriesWithNulls, "query1", 0, noNull), is(false)); + + assertThat(jandexUtils.fieldHasAnnotation(nullPOJO, "listNonNullStrings", noNull), is(true)); + assertThat(jandexUtils.fieldHasAnnotation(nullPOJO, "listOfListOfNonNullStrings", noNull), is(true)); + assertThat(jandexUtils.fieldHasAnnotation(nullPOJO, "listOfListOfNullStrings", noNull), is(false)); + + assertThat(jandexUtils.methodHasAnnotation(nullPOJO, "getListOfListOfNonNullStrings", noNull), is(false)); + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java index 6a26b63e2e1..fd23993a22b 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.beans.IntrospectionException; @@ -10,21 +26,19 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; -import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; -import io.helidon.microprofile.graphql.server.ExecutionContext; -import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; -import io.helidon.microprofile.graphql.server.Schema; -import io.helidon.microprofile.graphql.server.SchemaGenerator; import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; -import io.helidon.microprofile.graphql.server.test.types.Person; + import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +/** + * Tests for Multi-level arrays. + */ @ExtendWith(WeldJunit5Extension.class) public class MultiLevelArraysIT extends AbstractGraphQLIT { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java index 501bb489ffa..7b47ff0d004 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java @@ -1,7 +1,22 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; -import java.util.Map; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; @@ -9,15 +24,17 @@ import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.queries.QueriesWithNulls; -import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; import io.helidon.microprofile.graphql.server.test.types.NullPOJO; -import io.helidon.microprofile.graphql.server.test.types.SimpleContact; + import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +/** + * Tests for Nulls. + */ @ExtendWith(WeldJunit5Extension.class) public class NullIT extends AbstractGraphQLIT { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java index 8a88571c00f..792d5afb727 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java @@ -1,37 +1,43 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; import java.util.Map; -import java.util.UUID; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; -import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; -import io.helidon.microprofile.graphql.server.ExecutionContext; -import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; import io.helidon.microprofile.graphql.server.test.db.TestDB; -import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations; -import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; -import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; -import io.helidon.microprofile.graphql.server.test.types.Car; -import io.helidon.microprofile.graphql.server.test.types.ContactRelationship; -import io.helidon.microprofile.graphql.server.test.types.SimpleContact; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; + import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +/** + * Tests for NUmber formats. + */ @ExtendWith(WeldJunit5Extension.class) public class NumberFormatIT extends AbstractGraphQLIT { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PersonIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java similarity index 76% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PersonIT.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java index 875e3f4baf1..e3356100826 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PersonIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java @@ -1,16 +1,30 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; -import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; -import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; -import io.helidon.microprofile.graphql.server.Schema; -import io.helidon.microprofile.graphql.server.SchemaGenerator; import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.PersonWithName; + import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; + import java.beans.IntrospectionException; import java.io.IOException; @@ -18,8 +32,11 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; +/** + * Tests for naming of Pojo's. + */ @ExtendWith(WeldJunit5Extension.class) -public class PersonIT extends AbstractGraphQLIT { +public class PojoNamingIT extends AbstractGraphQLIT { @WeldSetup private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java index 336ae07eb73..003f126b165 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; @@ -28,6 +44,7 @@ public class PropertyNameIT extends AbstractGraphQLIT { .addExtension(new GraphQLCdiExtension())); @Test + @SuppressWarnings("unchecked") public void testDifferentPropertyNames() throws IOException { setupIndex(indexFileName, PropertyNameQueries.class, TypeWithNameAndJsonbProperty.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java deleted file mode 100644 index 355096f2309..00000000000 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorIT.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2020 Oracle and/or its affiliates. - * - * 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 io.helidon.microprofile.graphql.server; - -import java.beans.IntrospectionException; -import java.io.IOException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import io.helidon.microprofile.graphql.server.test.queries.DuplicateNameQueries; -import io.helidon.microprofile.graphql.server.test.queries.PropertyNameQueries; - -import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes; -import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty; - -import io.helidon.microprofile.graphql.server.test.db.TestDB; -import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; -import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations; -import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations; -import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; -import io.helidon.microprofile.graphql.server.test.queries.DefaultValueQueries; -import io.helidon.microprofile.graphql.server.test.queries.DescriptionQueries; -import io.helidon.microprofile.graphql.server.test.queries.InvalidQueries; -import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations; -import io.helidon.microprofile.graphql.server.test.queries.OddNamedQueriesAndMutations; -import io.helidon.microprofile.graphql.server.test.queries.QueriesWithIgnorable; -import io.helidon.microprofile.graphql.server.test.queries.QueriesWithNulls; -import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; -import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; -import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; -import io.helidon.microprofile.graphql.server.test.queries.VoidQueries; -import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle; -import io.helidon.microprofile.graphql.server.test.types.Car; -import io.helidon.microprofile.graphql.server.test.types.ContactRelationship; -import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; -import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO; -import io.helidon.microprofile.graphql.server.test.types.DescriptionType; -import io.helidon.microprofile.graphql.server.test.types.Level0; -import io.helidon.microprofile.graphql.server.test.types.Motorbike; -import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; -import io.helidon.microprofile.graphql.server.test.types.NullPOJO; -import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods; -import io.helidon.microprofile.graphql.server.test.types.Person; -import io.helidon.microprofile.graphql.server.test.types.PersonWithName; -import io.helidon.microprofile.graphql.server.test.types.SimpleContact; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputType; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithAddress; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithName; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithNameValue; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; -import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; -import io.helidon.microprofile.graphql.server.test.types.Vehicle; -import io.helidon.microprofile.graphql.server.test.types.VehicleIncident; - -import org.eclipse.microprofile.graphql.NonNull; - -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -/** - * Integration tests for {@link SchemaGeneratorTest}. - */ -@SuppressWarnings("unchecked") -@ExtendWith(WeldJunit5Extension.class) -public class SchemaGeneratorIT extends AbstractGraphQLIT { - - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(Person.class) - .addExtension(new GraphQLCdiExtension())); - - @Test - public void testJandexUtils() throws IOException { - setupIndex(indexFileName, NullPOJO.class, QueriesWithNulls.class); - SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); - String queriesWithNulls = QueriesWithNulls.class.getName(); - String nullPOJO = NullPOJO.class.getName(); - String noNull = NonNull.class.getName(); - - JandexUtils jandexUtils = schemaGenerator.getJandexUtils(); - - assertThat(jandexUtils.methodParameterHasAnnotation(queriesWithNulls, "query1", 0, noNull), is(false)); - - assertThat(jandexUtils.fieldHasAnnotation(nullPOJO, "listNonNullStrings", noNull), is(true)); - assertThat(jandexUtils.fieldHasAnnotation(nullPOJO, "listOfListOfNonNullStrings", noNull), is(true)); - assertThat(jandexUtils.fieldHasAnnotation(nullPOJO, "listOfListOfNullStrings", noNull), is(false)); - - assertThat(jandexUtils.methodHasAnnotation(nullPOJO, "getListOfListOfNonNullStrings", noNull), is(false)); - } - - @Test - public void testVoidMutations() throws IOException { - setupIndex(indexFileName, VoidMutations.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } - - @Test - public void testInvalidNamedType() throws IOException { - setupIndex(indexFileName, InvalidNamedTypes.InvalidNamedPerson.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } - - @Test - public void testInvalidNamedInputType() throws IOException { - setupIndex(indexFileName, InvalidNamedTypes.InvalidInputType.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } - - @Test - public void testInvalidNamedInterface() throws IOException { - setupIndex(indexFileName, InvalidNamedTypes.InvalidInterface.class, InvalidNamedTypes.InvalidClass.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } - - @Test - public void testInvalidNamedQuery() throws IOException { - setupIndex(indexFileName, InvalidNamedTypes.ClassWithInvalidQuery.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } - - @Test - public void testInvalidNamedMutation() throws IOException { - setupIndex(indexFileName, InvalidNamedTypes.ClassWithInvalidMutation.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } - - @Test - public void testInvalidNameEnum() throws IOException { - setupIndex(indexFileName, InvalidNamedTypes.Size.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } - - @Test - public void testVoidQueries() throws IOException { - setupIndex(indexFileName, VoidQueries.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } - - @Test - public void testInvalidQueries() throws IOException { - setupIndex(indexFileName, InvalidQueries.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } - - @Test - public void testDuplicateQueryOrMutationNames() throws IOException { - setupIndex(indexFileName, DuplicateNameQueries.class); - assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); - } -} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java index 0f130b93130..1786c49bf1f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; @@ -9,10 +25,7 @@ import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputType; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithAddress; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithName; -import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithNameValue; + import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesIT.java index 1ef9b860ade..c38952abc55 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesIT.java @@ -1,6 +1,21 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; -import java.beans.IntrospectionException; import java.io.IOException; import java.math.BigInteger; import java.time.LocalDate; @@ -29,6 +44,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +/** + * Tests for simple queries. + */ @ExtendWith(WeldJunit5Extension.class) public class SimpleQueriesIT extends AbstractGraphQLIT { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java index 38e1a5bf4b0..2946e534017 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; @@ -27,6 +43,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +/** + * Tests for simple queries with args. + */ @ExtendWith(WeldJunit5Extension.class) public class SimpleQueriesWithArgsIT extends AbstractGraphQLIT { @@ -142,11 +161,11 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { ") }")); assertThat(mapResults.size(), is(1)); } - + @Test public void testQueriesWithVariables() throws IOException { setupIndex(indexFileName, SimpleQueriesWithArgs.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); + ExecutionContext executionContext = new ExecutionContext(defaultContext); Map mapVariables = Map.of("first", 10, "second", 20); Map mapResults = getAndAssertResult(executionContext.execute( "query additionQuery($first: Int!, $second: Int!) {" diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java index 29e0758ad49..b838b497842 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; import java.io.IOException; @@ -8,17 +24,18 @@ import static org.hamcrest.MatcherAssert.assertThat; import io.helidon.microprofile.graphql.server.test.db.TestDB; -import io.helidon.microprofile.graphql.server.test.queries.DefaultValueQueries; -import io.helidon.microprofile.graphql.server.test.queries.OddNamedQueriesAndMutations; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; -import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; + import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +/** + * Tests for Source annotation. + */ @ExtendWith(WeldJunit5Extension.class) public class SourceIT extends AbstractGraphQLIT { From 019ff09ff72589ad1375a454aa96f0bfd884524c Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 11 Sep 2020 15:04:02 +0800 Subject: [PATCH 082/178] progress on tck tests --- .../graphql/server/CustomScalars.java | 58 +++++++---- .../microprofile/graphql/server/Schema.java | 15 +++ .../graphql/server/SchemaFieldDefinition.java | 22 +++++ .../graphql/server/SchemaGenerator.java | 95 +++++++++++++------ .../graphql/server/SchemaGeneratorHelper.java | 52 ++++++---- .../graphql/server/AbstractGraphQLTest.java | 3 +- .../graphql/server/DateTimeIT.java | 53 +++++++++-- .../graphql/server/SchemaArgumentTest.java | 9 ++ .../server/SchemaFieldDefinitionTest.java | 11 +++ .../graphql/server/SchemaGeneratorTest.java | 2 +- .../test/queries/SimpleQueriesNoArgs.java | 5 + .../server/test/types/DateTimePojo.java | 9 ++ microprofile/tests/tck/tck-graphql/pom.xml | 24 +++++ 13 files changed, 285 insertions(+), 73 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java index 46d4fbf7f28..fc9694f38b9 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java @@ -23,10 +23,6 @@ import java.time.OffsetDateTime; import java.time.OffsetTime; -import static graphql.Scalars.GraphQLBigInteger; -import static graphql.Scalars.GraphQLFloat; -import static graphql.Scalars.GraphQLInt; - import graphql.Scalars; import graphql.language.StringValue; import graphql.scalars.ExtendedScalars; @@ -36,6 +32,16 @@ import graphql.schema.CoercingSerializeException; import graphql.schema.GraphQLScalarType; +import static graphql.Scalars.GraphQLBigInteger; +import static graphql.Scalars.GraphQLFloat; +import static graphql.Scalars.GraphQLInt; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATETIME_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATETIME_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_TIME_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.TIME_SCALAR; + /** * Custom scalars. */ @@ -68,27 +74,44 @@ private CustomScalars() { public static final GraphQLScalarType CUSTOM_BIGINTEGER_SCALAR = newCustomGraphQLBigInteger(); /** - * An instance of a custom date/time scalar. + * An instance of a custom formatted date/time scalar. + */ + public static final GraphQLScalarType FORMATTED_CUSTOM_DATE_TIME_SCALAR = newDateTimeScalar(FORMATTED_DATETIME_SCALAR); + + /** + * An instance of a custom formatted time scalar. */ - public static final GraphQLScalarType CUSTOM_DATE_TIME_SCALAR = newDateTimeScalar(); + public static final GraphQLScalarType FORMATTED_CUSTOM_TIME_SCALAR = newTimeScalar(FORMATTED_TIME_SCALAR); /** - * An instance of a custom time scalar. + * An instance of a custom formatted date scalar. */ - public static final GraphQLScalarType CUSTOM_TIME_SCALAR = newTimeScalar(); + public static final GraphQLScalarType FORMATTED_CUSTOM_DATE_SCALAR = newDateScalar(FORMATTED_DATE_SCALAR); /** - * An instance of a custom date scalar. + * An instance of a custom date/time scalar (with default formatting). */ - public static final GraphQLScalarType CUSTOM_DATE_SCALAR = newDateScalar(); + public static final GraphQLScalarType CUSTOM_DATE_TIME_SCALAR = newDateTimeScalar(DATETIME_SCALAR); + + /** + * An instance of a custom time scalar (with default formatting). + */ + public static final GraphQLScalarType CUSTOM_TIME_SCALAR = newTimeScalar(TIME_SCALAR); + + /** + * An instance of a custom date scalar (with default formatting). + */ + public static final GraphQLScalarType CUSTOM_DATE_SCALAR = newDateScalar(DATE_SCALAR); /** * Return a new custom date/time scalar. * + * @param name the name of the scalar + * * @return a new custom date/time scalar */ @SuppressWarnings("unchecked") - public static GraphQLScalarType newDateTimeScalar() { + public static GraphQLScalarType newDateTimeScalar(String name) { GraphQLScalarType originalScalar = ExtendedScalars.DateTime; Coercing originalCoercing = originalScalar.getCoercing(); return GraphQLScalarType.newScalar().coercing(new Coercing() { @@ -110,7 +133,7 @@ public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralExce return null; } }) - .name(originalScalar.getName()) + .name(name) .description("Custom: " + originalScalar.getDescription()) .build(); } @@ -118,10 +141,11 @@ public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralExce /** * Return a new custom time scalar. * + * @param name the name of the scalar * @return a new custom time scalar */ @SuppressWarnings("unchecked") - public static GraphQLScalarType newTimeScalar() { + public static GraphQLScalarType newTimeScalar(String name) { GraphQLScalarType originalScalar = ExtendedScalars.Time; Coercing originalCoercing = originalScalar.getCoercing(); return GraphQLScalarType.newScalar().coercing(new Coercing() { @@ -142,7 +166,7 @@ public OffsetTime parseLiteral(Object input) throws CoercingParseLiteralExceptio return null; } }) - .name(originalScalar.getName()) + .name(name) .description("Custom: " + originalScalar.getDescription()) .build(); } @@ -150,10 +174,12 @@ public OffsetTime parseLiteral(Object input) throws CoercingParseLiteralExceptio /** * Return a new custom date scalar. * + * @param name the name of the scalar + * * @return a new custom date scalar */ @SuppressWarnings("unchecked") - public static GraphQLScalarType newDateScalar() { + public static GraphQLScalarType newDateScalar(String name) { GraphQLScalarType originalScalar = ExtendedScalars.Date; Coercing originalCoercing = originalScalar.getCoercing(); return GraphQLScalarType.newScalar().coercing(new Coercing() { @@ -175,7 +201,7 @@ public Object parseLiteral(Object input) throws CoercingParseLiteralException { : originalCoercing.parseLiteral(input); } }) - .name(originalScalar.getName()) + .name(name) .description("Custom: " + originalScalar.getDescription()) .build(); } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java index d3ff2d13c12..41dcf6b5885 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java @@ -330,6 +330,21 @@ public SchemaScalar getScalarByActualClass(String actualClazz) { return null; } + /** + * Returns a {@link SchemaScalar} which matches the provided scalar name. + * + * @param scalarName the scalar name to match + * @return {@link SchemaScalar} or null if none found + */ + public SchemaScalar getScalarByName(String scalarName) { + for (SchemaScalar schemaScalar : getScalars()) { + if (schemaScalar.getName().equals(scalarName)) { + return schemaScalar; + } + } + return null; + } + /** * Returns true of the {@link SchemaType} with the the given name is present for this {@link Schema}. * diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index bd5db2d6e17..3c21b3765e3 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -90,6 +90,11 @@ public class SchemaFieldDefinition */ private Object defaultValue; + /** + * Indicates if the field has a default format applied (such as default date format) rather than a specific format supplied. + */ + private boolean defaultFormatApplied; + /** * Construct a {@link SchemaFieldDefinition}. * @@ -328,6 +333,22 @@ public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { isArrayReturnTypeMandatory = arrayReturnTypeMandatory; } + /** + * Set if the field has a default format applied. + * @param defaultFormatApplied if the field has a default format applied + */ + public void setDefaultFormatApplied(boolean defaultFormatApplied) { + this.defaultFormatApplied = defaultFormatApplied; + } + + /** + * Return if the field has a default format applied. + * @return if the field has a default format applied + */ + public boolean isDefaultFormatApplied() { + return defaultFormatApplied; + } + @Override public String toString() { return "FieldDefinition{" @@ -339,6 +360,7 @@ public String toString() { + ", listArguments=" + listSchemaArguments + ", arrayLevels=" + arrayLevels + ", originalType=" + originalType + + ", defaultFormatApplied=" + defaultFormatApplied + ", originalArrayType=" + originalArrayType + ", format=" + Arrays.toString(format) + ", description='" + getDescription() + '\'' + '}'; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 2107e2bc64c..1ec727099ef 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -41,6 +41,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import graphql.schema.GraphQLScalarType; import javax.enterprise.inject.spi.CDI; import graphql.schema.DataFetcher; @@ -59,6 +60,9 @@ import org.eclipse.microprofile.graphql.Type; import org.jboss.jandex.AnnotationInstance; +import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_TIME_SCALAR; +import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.FormattingHelper.DATE; import static io.helidon.microprofile.graphql.server.FormattingHelper.NO_FORMATTING; import static io.helidon.microprofile.graphql.server.FormattingHelper.NUMBER; @@ -69,8 +73,14 @@ import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormattingAnnotation; import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.MUTATION_TYPE; import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.QUERY_TYPE; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATETIME_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATETIME_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.TIME_SCALAR; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.checkScalars; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureFormat; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException; @@ -334,11 +344,12 @@ private void processDefaultDateTimeValues(Schema schema) { Stream streamInputTypes = schema.getInputTypes().stream().map(it -> (SchemaType) it); Stream streamAll = Stream.concat(streamInputTypes, schema.getTypes().stream()); streamAll.forEach(t -> { + System.out.println(t); t.getFieldDefinitions().forEach(fd -> { String returnType = fd.getReturnType(); - // only check Date/Time/DateTime scalars that don't have data fetchers already + // only check Date/Time/DateTime scalars that are not Queries or don't have data fetchers // as default formatting has already been dealt with - if (isDateTimeScalar(returnType) && fd.getDataFetcher() == null) { + if (isDateTimeScalar(returnType) && (t.getName().equals(Schema.QUERY) || fd.getDataFetcher() == null)) { String[] existingFormat = fd.getFormat(); // check if this type is an array type and if so then get the actual original type Class clazzOriginalType = fd.getOriginalArrayType() != null @@ -347,11 +358,33 @@ private void processDefaultDateTimeValues(Schema schema) { if (!Arrays.equals(newFormat, existingFormat) && newFormat.length == 2) { // formats differ so set the new format and DataFetcher fd.setFormat(newFormat); - // create the raw array to pass to the retrieveFormattingDataFetcher method - DataFetcher dataFetcher = retrieveFormattingDataFetcher(new String[] {DATE, newFormat[0], newFormat[1] }, - fd.getName(), clazzOriginalType.getName()); - fd.setDataFetcher(dataFetcher); - // context.addFormatter(null, fd.getName(), (FormattingProvider) dataFetcher); + if (fd.getDataFetcher() == null) { + // create the raw array to pass to the retrieveFormattingDataFetcher method + DataFetcher dataFetcher = retrieveFormattingDataFetcher( + new String[] { DATE, newFormat[0], newFormat[1] }, + fd.getName(), clazzOriginalType.getName()); + fd.setDataFetcher(dataFetcher); + } + fd.setDefaultFormatApplied(true); + SchemaScalar scalar = schema.getScalarByName(fd.getReturnType()); + GraphQLScalarType newScalarType = null; + if (fd.getReturnType().equals(FORMATTED_DATE_SCALAR)) { + fd.setReturnType(DATE_SCALAR); + newScalarType = CUSTOM_DATE_SCALAR; + } else if (fd.getReturnType().equals(FORMATTED_TIME_SCALAR)) { + fd.setReturnType(TIME_SCALAR); + newScalarType = CUSTOM_TIME_SCALAR; + } else if (fd.getReturnType().equals(FORMATTED_DATETIME_SCALAR)) { + fd.setReturnType(DATETIME_SCALAR); + newScalarType = CUSTOM_DATE_TIME_SCALAR; + } + + // clone the scalar with the new scalar name + SchemaScalar newScalar = new SchemaScalar(fd.getReturnType(), scalar.getActualClass(), + newScalarType, scalar.getDefaultFormat()); + if (!schema.containsScalarWithName(newScalar.getName())) { + schema.addScalar(newScalar); + } } } @@ -524,7 +557,7 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, } // in either case, get the argument format - + } if (fd != null) { fd.addArgument(a); @@ -543,7 +576,7 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, String[] newFormat = ensureFormat(dateScalar.getName(), originalType.getName(), new String[2]); if (newFormat.length == 2) { - format = new String[] {DATE, newFormat[0], newFormat[1] }; + format = new String[] { DATE, newFormat[0], newFormat[1] }; } } if (!isFormatEmpty(format)) { @@ -552,24 +585,26 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, final DataFetcher methodDataFetcher = DataFetcherUtils.newMethodDataFetcher(clazz, method, null, fd.getArguments().toArray( new SchemaArgument[0])); - final String[] newFormat = new String[] {format[0], format[1], format[2] }; + final String[] newFormat = new String[] { format[0], format[1], format[2] }; if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) { dataFetcher = DataFetcherFactories.wrapDataFetcher(methodDataFetcher, - (e, v) -> { - DateTimeFormatter dateTimeFormatter = - getCorrectDateFormatter( - graphQLType, newFormat[2], newFormat[1]); - return formatDate(v, dateTimeFormatter); - }); + (e, v) -> { + DateTimeFormatter dateTimeFormatter = + getCorrectDateFormatter( + graphQLType, newFormat[2], + newFormat[1]); + return formatDate(v, dateTimeFormatter); + }); } else { dataFetcher = DataFetcherFactories.wrapDataFetcher(methodDataFetcher, - (e, v) -> { - NumberFormat numberFormat = getCorrectNumberFormat( - graphQLType, newFormat[2], newFormat[1]); - boolean isScalar = SchemaGeneratorHelper.getScalar(discoveredMethod.getReturnType()) != null; - return formatNumber(v, isScalar, numberFormat); - }); + (e, v) -> { + NumberFormat numberFormat = getCorrectNumberFormat( + graphQLType, newFormat[2], newFormat[1]); + boolean isScalar = SchemaGeneratorHelper.getScalar( + discoveredMethod.getReturnType()) != null; + return formatNumber(v, isScalar, numberFormat); + }); fd.setReturnType(STRING); } } else { @@ -598,6 +633,7 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, /** * Returns true if the format is empty or undefined. + * * @param format format to check * @return true if the format is empty or undefined */ @@ -1128,7 +1164,7 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM String[] argumentFormat = FormattingHelper.getFormattingAnnotation(parameter); argument.setDescription(argumentDescription); if (argumentFormat[0] != null) { - argument.setFormat(new String[] {argumentFormat[1], argumentFormat[2] }); + argument.setFormat(new String[] { argumentFormat[1], argumentFormat[2] }); argument.setArgumentType(String.class.getName()); } @@ -1145,7 +1181,7 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM SchemaScalar dateScalar = getScalar(returnType.getReturnClass()); if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) { // only set the original array type if it's a date/time - // discoveredMethod.setOriginalArrayType(Class.forName(returnType.returnClass)); + // discoveredMethod.setOriginalArrayType(Class.forName(returnType.returnClass)); } argument.setArrayReturnTypeMandatory(returnType.isReturnTypeMandatory); argument.setArrayReturnType(returnType.isArrayType); @@ -1270,7 +1306,7 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp hasAnnotation = jandexUtils .methodParameterHasAnnotation(clazzName, methodName, parameterNumber, nonNullClazz); format = FormattingHelper.getMethodParameterFormat(jandexUtils, clazzName, methodName, - parameterNumber); + parameterNumber); } else { hasAnnotation = jandexUtils.methodHasAnnotation(clazzName, methodName, nonNullClazz); format = FormattingHelper.getMethodFormat(jandexUtils, clazzName, methodName); @@ -1283,7 +1319,7 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp Class clazz = genericReturnType.getClass(); isReturnTypeMandatory = clazz.getAnnotation(NonNull.class) != null || isPrimitive(clazz.getName()); -// format = FormattingHelper.getMethodFormat(jandexUtils, clazzName, methodName); + // format = FormattingHelper.getMethodFormat(jandexUtils, clazzName, methodName); return new RootTypeResult(((Class) genericReturnType).getName(), level, isReturnTypeMandatory, format); } } @@ -1722,7 +1758,7 @@ public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { * * @param originalArrayType the original array type */ - public void setOriginalArrayType(Class originalArrayType) { + public void setOriginalArrayType(Class originalArrayType) { this.originalArrayType = originalArrayType; } @@ -1945,6 +1981,7 @@ public void setReturnTypeMandatory(boolean returnTypeMandatory) { /** * Return the format of the result class. + * * @return the format of the result class */ public String[] getFormat() { @@ -1953,7 +1990,8 @@ public String[] getFormat() { /** * Set the format of the result class. - * @param format the format of the result class + * + * @param format the format of the result class */ public void setFormat(String[] format) { this.format = format; @@ -2038,6 +2076,7 @@ public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { /** * Return the format of the result class. + * * @return the format of the result class */ public String[] getFormat() { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index c417039441b..dd9ed37de83 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -53,11 +53,11 @@ import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_BIGDECIMAL_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_BIGINTEGER_SCALAR; -import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_SCALAR; -import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_TIME_SCALAR; +import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_DATE_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_FLOAT_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_INT_SCALAR; -import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_TIME_SCALAR; +import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.ElementGenerator.OPEN_SQUARE; import static io.helidon.microprofile.graphql.server.FormattingHelper.getDefaultDateTimeFormat; import static io.helidon.microprofile.graphql.server.SchemaGenerator.GET; @@ -170,19 +170,35 @@ public final class SchemaGeneratorHelper { protected static final String SHORT_PRIMITIVE_CLASS = short.class.getName(); /** - * Date scalar. + * Formatted Date scalar. */ - protected static final String DATE_SCALAR = "Date"; + public static final String FORMATTED_DATE_SCALAR = "FormattedDate"; /** - * DateTime scalar. + * Formatted DateTime scalar. */ - protected static final String DATETIME_SCALAR = "DateTime"; + public static final String FORMATTED_DATETIME_SCALAR = "FormattedDateTime"; /** - * Time Scalar. + * Formatted Time Scalar. */ - protected static final String TIME_SCALAR = "Time"; + public static final String FORMATTED_TIME_SCALAR = "FormattedTime"; + + /** + * Date scalar (with default formatting). + */ + public static final String DATE_SCALAR = "Date"; + + /** + * DateTime scalar (with default formatting). + */ + public static final String DATETIME_SCALAR = "DateTime"; + + /** + * Time Scalar (with default formatting). + */ + public static final String TIME_SCALAR = "Time"; + /** * Defines a {@link BigDecimal} type. @@ -238,22 +254,22 @@ public final class SchemaGeneratorHelper { // Time scalars put(OffsetTime.class.getName(), - new SchemaScalar(TIME_SCALAR, OFFSET_TIME_CLASS, CUSTOM_TIME_SCALAR, "HH:mm:ssZ")); + new SchemaScalar(FORMATTED_TIME_SCALAR, OFFSET_TIME_CLASS, FORMATTED_CUSTOM_TIME_SCALAR, "HH:mm:ssZ")); put(LocalTime.class.getName(), - new SchemaScalar(TIME_SCALAR, LOCAL_TIME_CLASS, CUSTOM_TIME_SCALAR, "HH:mm:ss")); + new SchemaScalar(FORMATTED_TIME_SCALAR, LOCAL_TIME_CLASS, FORMATTED_CUSTOM_TIME_SCALAR, "HH:mm:ss")); // DateTime scalars put(OFFSET_DATE_TIME_CLASS, - new SchemaScalar(DATETIME_SCALAR, OFFSET_DATE_TIME_CLASS, CUSTOM_DATE_TIME_SCALAR, + new SchemaScalar(FORMATTED_DATETIME_SCALAR, OFFSET_DATE_TIME_CLASS, FORMATTED_CUSTOM_DATE_TIME_SCALAR, "yyyy-MM-dd'T'HH:mm:ssZ")); put(ZONED_DATE_TIME_CLASS, - new SchemaScalar(DATETIME_SCALAR, ZONED_DATE_TIME_CLASS, CUSTOM_DATE_TIME_SCALAR, + new SchemaScalar(FORMATTED_DATETIME_SCALAR, ZONED_DATE_TIME_CLASS, FORMATTED_CUSTOM_DATE_TIME_SCALAR, "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'")); put(LOCAL_DATE_TIME_CLASS, - new SchemaScalar(DATETIME_SCALAR, LOCAL_DATE_TIME_CLASS, CUSTOM_DATE_TIME_SCALAR, "yyyy-MM-dd'T'HH:mm:ss")); + new SchemaScalar(FORMATTED_DATETIME_SCALAR, LOCAL_DATE_TIME_CLASS, FORMATTED_CUSTOM_DATE_TIME_SCALAR, "yyyy-MM-dd'T'HH:mm:ss")); // Date scalar - put(LOCAL_DATE_CLASS, new SchemaScalar(DATE_SCALAR, LOCAL_DATE_CLASS, CUSTOM_DATE_SCALAR, "yyyy-MM-dd")); + put(LOCAL_DATE_CLASS, new SchemaScalar(FORMATTED_DATE_SCALAR, LOCAL_DATE_CLASS, FORMATTED_CUSTOM_DATE_SCALAR, "yyyy-MM-dd")); // BigDecimal scalars put(BIG_DECIMAL_CLASS, new SchemaScalar(BIG_DECIMAL, BIG_DECIMAL_CLASS, CUSTOM_BIGDECIMAL_SCALAR, null)); @@ -430,9 +446,9 @@ protected static String getGraphQLType(String className) { * @return rue of the name is a Date, DateTime, or Time scalar */ protected static boolean isDateTimeScalar(String scalarName) { - return DATE_SCALAR.equals(scalarName) - || TIME_SCALAR.equals(scalarName) - || DATETIME_SCALAR.equals(scalarName); + return FORMATTED_DATE_SCALAR.equals(scalarName) + || FORMATTED_TIME_SCALAR.equals(scalarName) + || FORMATTED_DATETIME_SCALAR.equals(scalarName); } /** * Return true if the type type is a native GraphQLType. diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java index 69d7ef4fe54..78bd12bd853 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java @@ -219,10 +219,11 @@ protected String getContactAsQueryInput(SimpleContact contact) { .append("} ").toString(); } - protected void assertDefaultFormat(SchemaType type, String fdName, String defaultFormat) { + protected void assertDefaultFormat(SchemaType type, String fdName, String defaultFormat, boolean isDefaultFormatApplied) { assertThat(type, CoreMatchers.is(notNullValue())); SchemaFieldDefinition fd = getFieldDefinition(type, fdName); assertThat(fd, CoreMatchers.is(notNullValue())); + assertThat(fd.isDefaultFormatApplied(), is(isDefaultFormatApplied)); String[] format = fd.getFormat(); assertThat(format, CoreMatchers.is(notNullValue())); assertThat(format.length == 2, CoreMatchers.is(notNullValue())); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java index 6874991bf7d..6e9182a10cb 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java @@ -21,6 +21,11 @@ import java.util.List; import java.util.Map; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATETIME_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_TIME_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.TIME_SCALAR; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; @@ -59,30 +64,51 @@ public void testDateAndTime() throws IOException { assertThat(fd, is(notNullValue())); assertThat(fd.getFormat()[0], is("MM/dd/yyyy")); assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.isDefaultFormatApplied(), is(false)); + assertThat(fd.getReturnType(), is(FORMATTED_DATE_SCALAR)); fd = getFieldDefinition(type, "localTime"); assertThat(fd, is(notNullValue())); assertThat(fd.getFormat()[0], is("hh:mm:ss")); assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.isDefaultFormatApplied(), is(false)); + assertThat(fd.getReturnType(), is(FORMATTED_TIME_SCALAR)); fd = getFieldDefinition(type, "localDate2"); assertThat(fd, is(notNullValue())); assertThat(fd.getFormat()[0], is("MM/dd/yyyy")); assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.isDefaultFormatApplied(), is(false)); + assertThat(fd.getReturnType(), is(FORMATTED_DATE_SCALAR)); // test default values for date and time - assertDefaultFormat(type, "offsetTime", "HH:mm:ssZ"); - assertDefaultFormat(type, "localTime", "hh:mm:ss"); - assertDefaultFormat(type, "localDateTime", "yyyy-MM-dd'T'HH:mm:ss"); - assertDefaultFormat(type, "offsetDateTime", "yyyy-MM-dd'T'HH:mm:ssZ"); - assertDefaultFormat(type, "zonedDateTime", "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'"); - assertDefaultFormat(type, "localDateNoFormat", "yyyy-MM-dd"); - assertDefaultFormat(type, "significantDates", "yyyy-MM-dd"); + assertDefaultFormat(type, "offsetTime", "HH:mm:ssZ", true); + assertDefaultFormat(type, "localTime", "hh:mm:ss", false); + assertDefaultFormat(type, "localDateTime", "yyyy-MM-dd'T'HH:mm:ss", true); + assertDefaultFormat(type, "offsetDateTime", "yyyy-MM-dd'T'HH:mm:ssZ", true); + assertDefaultFormat(type, "zonedDateTime", "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'", true); + assertDefaultFormat(type, "localDateNoFormat", "yyyy-MM-dd", true); + assertDefaultFormat(type, "significantDates", "yyyy-MM-dd", true); + + // testing the conversion of the following scalars when they have default formatting applied + // FormattedDate -> Date + // FormattedTime -> Time + // FormattedDateTime -> DateTime fd = getFieldDefinition(type, "localDateTime"); assertThat(fd, is(notNullValue())); - assertThat(fd.getDescription(), is(nullValue())); - assertThat(fd.getFormat()[0], is("yyyy-MM-dd'T'HH:mm:ss")); + assertThat(fd.isDefaultFormatApplied(), is(true)); + assertThat(fd.getReturnType(), is(DATETIME_SCALAR)); + + fd = getFieldDefinition(type, "localDateNoFormat"); + assertThat(fd, is(notNullValue())); + assertThat(fd.isDefaultFormatApplied(), is(true)); + assertThat(fd.getReturnType(), is(DATE_SCALAR)); + + fd = getFieldDefinition(type, "localTimeNoFormat"); + assertThat(fd, is(notNullValue())); + assertThat(fd.isDefaultFormatApplied(), is(true)); + assertThat(fd.getReturnType(), is(TIME_SCALAR)); Map mapResults = getAndAssertResult( executionContext.execute("query { dateAndTimePOJOQuery { offsetDateTime offsetTime zonedDateTime " @@ -110,6 +136,15 @@ public void testDateAndTime() throws IOException { assertThat(listDates.size(), is(2)); assertThat(listDates.get(0), is("17/02/1968")); assertThat(listDates.get(1), is("04/08/1970")); + + // test formats on queries + SchemaType typeQuery = schema.getTypeByName("Query"); + fd = getFieldDefinition(typeQuery, "localDateNoFormat"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is("yyyy-MM-dd")); + assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.isDefaultFormatApplied(), is(true)); + assertThat(fd.getReturnType(), is(DATE_SCALAR)); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index 80181f33936..1107f97f6cd 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -81,6 +81,15 @@ public void testSchemaArgumentArrayTypes() { assertThat(schemaArgument.getArrayLevels(), is(0)); } + @Test + public void testSchemaGenerationWithArrays() { + SchemaArgument schemaArgument = new SchemaArgument("name", "String", false, null, STRING); + schemaArgument.setArrayLevels(1); + schemaArgument.setArrayReturnTypeMandatory(true); + schemaArgument.setArrayReturnType(true); + assertThat(schemaArgument.getSchemaAsString(), is("name: [String!]")); + } + @Test public void testSchemaGeneration() { SchemaArgument schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java index 5e2387f545d..ab5be8cfcd1 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java @@ -75,6 +75,10 @@ public void testConstructors() { assertThat(schemaFieldDefinition.isArrayReturnTypeMandatory(), is(false)); schemaFieldDefinition.setArrayReturnTypeMandatory(true); assertThat(schemaFieldDefinition.isArrayReturnTypeMandatory(), is(true)); + + assertThat(schemaFieldDefinition.isDefaultFormatApplied(), is(false)); + schemaFieldDefinition.setDefaultFormatApplied(true); + assertThat(schemaFieldDefinition.isDefaultFormatApplied(), is(true)); } @Test @@ -131,6 +135,13 @@ public void testFieldDefinitionWith1ArgumentAndArrayType() { assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): [Person]!")); } + @Test + public void testFieldDefinitionWithNoArgumentsAndMandatoryArrayType() { + SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("superPowers", "String", true, false, 1); + schemaFieldDefinition.setArrayReturnTypeMandatory(true); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("superPowers: [String!]")); + } + @Test public void testFieldDefinitionWith1MandatoryArgument() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index bb546aefa6d..7c5e6dca3f5 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -284,7 +284,7 @@ public void testAllMethods() throws IntrospectionException, ClassNotFoundExcepti Map mapMethods = schemaGenerator .retrieveAllAnnotatedBeanMethods(SimpleQueriesNoArgs.class); assertThat(mapMethods, is(notNullValue())); - assertThat(mapMethods.size(), is(15)); + assertDiscoveredMethod(mapMethods.get("hero"), "hero", STRING, null, false, false, false); assertDiscoveredMethod(mapMethods.get("episodeCount"), "episodeCount", "int", null, false, false, false); assertDiscoveredMethod(mapMethods.get("numberOfStars"), "numberOfStars", Long.class.getName(), null, false, false, false); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java index efa5e93dad7..3775dd390da 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java @@ -167,4 +167,9 @@ public DateTimePojo dateTimePojo() { public List<@DateFormat("dd/MM/yyyy") LocalDate> getLocalDateListFormat() { return List.of(LocalDate.of(1968,2,17), LocalDate.of(1970,8,4)); } + + @Query("localDateNoFormat") + public LocalDate localDateNoFormat() { + return LocalDate.of(1968,02,17); + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java index 4137d180faa..396ca06195c 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java @@ -52,6 +52,7 @@ public class DateTimePojo { private ZonedDateTime zonedDateTime; @Description("description") private LocalDate localDateNoFormat; + private LocalTime localTimeNoFormat; private List significantDates; @@ -75,6 +76,14 @@ public DateTimePojo(LocalDate localDate, this.significantDates = significantDates; } + public LocalTime getLocalTimeNoFormat() { + return localTimeNoFormat; + } + + public void setLocalTimeNoFormat(LocalTime localTimeNoFormat) { + this.localTimeNoFormat = localTimeNoFormat; + } + public LocalDate getLocalDate() { return localDate; } diff --git a/microprofile/tests/tck/tck-graphql/pom.xml b/microprofile/tests/tck/tck-graphql/pom.xml index 5e8e9e617de..240111c5553 100644 --- a/microprofile/tests/tck/tck-graphql/pom.xml +++ b/microprofile/tests/tck/tck-graphql/pom.xml @@ -59,6 +59,30 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + generate-sources + + unpack + + + + + org.eclipse.microprofile.graphql + microprofile-graphql-tck + jar + true + ${project.build.directory}/test-classes + + + **/tests/,**/dynamic/,**/*Test.class,**/beans.xml + + + + org.jboss.jandex jandex-maven-plugin From 8f1eccd3729d6dd62b5e554280de94e1d6ecd46e Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 15 Sep 2020 11:05:54 +0800 Subject: [PATCH 083/178] Work through TCK failures --- .../graphql/server/CustomScalars.java | 26 +++--- .../graphql/server/DataFetcherUtils.java | 40 +++++--- .../graphql/server/ExecutionContext.java | 14 ++- .../graphql/server/FormattingHelper.java | 10 +- .../graphql/server/SchemaGenerator.java | 24 ++++- .../graphql/server/SchemaGeneratorHelper.java | 30 ++++-- .../graphql/server/DateTimeIT.java | 61 +++++++++++- .../graphql/server/NumberFormatIT.java | 92 ++++++++++++++++--- .../graphql/server/SchemaGeneratorTest.java | 4 +- .../graphql/server/test/db/TestDB.java | 17 ++++ .../test/mutations/SimpleMutations.java | 1 + .../NumberFormatQueriesAndMutations.java | 26 +++++- ...gs.java => SimpleQueriesAndMutations.java} | 42 +++++++-- .../server/test/types/DateTimePojo.java | 14 ++- .../types/SimpleContactWithNumberFormats.java | 6 +- 15 files changed, 324 insertions(+), 83 deletions(-) rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/{SimpleQueriesNoArgs.java => SimpleQueriesAndMutations.java} (84%) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java index fc9694f38b9..85c26964d2b 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java @@ -114,7 +114,7 @@ private CustomScalars() { public static GraphQLScalarType newDateTimeScalar(String name) { GraphQLScalarType originalScalar = ExtendedScalars.DateTime; Coercing originalCoercing = originalScalar.getCoercing(); - return GraphQLScalarType.newScalar().coercing(new Coercing() { + return GraphQLScalarType.newScalar().coercing(new Coercing() { public String serialize(Object dataFetcherResult) throws CoercingSerializeException { if (dataFetcherResult instanceof String) { return (String) dataFetcherResult; @@ -124,13 +124,15 @@ public String serialize(Object dataFetcherResult) throws CoercingSerializeExcept } @Override - public OffsetDateTime parseValue(Object input) throws CoercingParseValueException { - return null; + public Object parseValue(Object input) throws CoercingParseValueException { + return input instanceof StringValue ? ((StringValue) input).getValue() + : originalCoercing.parseValue(input); } @Override - public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralException { - return null; + public Object parseLiteral(Object input) throws CoercingParseLiteralException { + return input instanceof StringValue ? ((StringValue) input).getValue() + : originalCoercing.parseLiteral(input); } }) .name(name) @@ -147,8 +149,8 @@ public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralExce @SuppressWarnings("unchecked") public static GraphQLScalarType newTimeScalar(String name) { GraphQLScalarType originalScalar = ExtendedScalars.Time; - Coercing originalCoercing = originalScalar.getCoercing(); - return GraphQLScalarType.newScalar().coercing(new Coercing() { + Coercing originalCoercing = originalScalar.getCoercing(); + return GraphQLScalarType.newScalar().coercing(new Coercing() { public String serialize(Object dataFetcherResult) throws CoercingSerializeException { return dataFetcherResult instanceof String ? (String) dataFetcherResult @@ -156,14 +158,16 @@ public String serialize(Object dataFetcherResult) throws CoercingSerializeExcept } @Override - public OffsetTime parseValue(Object input) throws CoercingParseValueException { - return null; + public Object parseValue(Object input) throws CoercingParseValueException { + return input instanceof StringValue ? ((StringValue) input).getValue() + : originalCoercing.parseValue(input); } @Override - public OffsetTime parseLiteral(Object input) throws CoercingParseLiteralException { - return null; + public Object parseLiteral(Object input) throws CoercingParseLiteralException { + return input instanceof StringValue ? ((StringValue) input).getValue() + : originalCoercing.parseLiteral(input); } }) .name(name) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 4a451a55711..2a6157ba479 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -19,18 +19,16 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.text.DateFormat; +import java.math.BigInteger; import java.text.NumberFormat; -import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; - import java.time.temporal.TemporalAccessor; + import java.util.ArrayList; -import java.util.Locale; import java.util.Map; import java.util.UUID; import java.util.logging.Logger; @@ -40,8 +38,6 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.PropertyDataFetcher; -import graphql.schema.PropertyDataFetcherHelper; - import graphql.GraphQLException; import static io.helidon.microprofile.graphql.server.FormattingHelper.formatDate; @@ -51,7 +47,7 @@ import static io.helidon.microprofile.graphql.server.SchemaGenerator.isFormatEmpty; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isDateTimeScalar; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isDateTimeClass; /** * Utilities for working with {@link DataFetcher}s. @@ -113,13 +109,12 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met listArgumentValues.add(Enum.valueOf(enumClass, key.toString())); } else if (argument.getArgumentType().equals(ID)) { // convert back to original data type - listArgumentValues.add(getOriginalValue(originalType, (String) key)); + listArgumentValues.add(getOriginalIDValue(originalType, (String) key)); } else { // check the format and convert it from a string to the original format String[] format = argument.getFormat(); if (!isFormatEmpty(format)) { - // TODO: This always returns false, need to fix ?? - if (isDateTimeScalar(argument.getArgumentType())) { + if (isDateTimeClass(argument.getOriginalType())) { DateTimeFormatter dateFormatter = getCorrectDateFormatter(originalType.getName(), format[1], format[0]); listArgumentValues.add( @@ -138,7 +133,8 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met } } } else { - listArgumentValues.add(key); + // process value in case we have to convert between say a Double/Float as they are interchangeable + listArgumentValues.add(getOriginalValue(originalType, key)); } } } @@ -189,7 +185,6 @@ public NumberFormattingDataFetcher(String propertyName, String type, String valu @Override public Object get(DataFetchingEnvironment environment) { - Object originalResult = super.get(environment); return formatNumber(super.get(environment), isScalar, numberFormat); } @@ -242,7 +237,7 @@ public DateTimeFormatter getDateTimeFormat() { * @param key the key value passed in * @return the value as the original type */ - private static Object getOriginalValue(Class originalType, String key) { + private static Object getOriginalIDValue(Class originalType, String key) { if (originalType.equals(Long.class) || originalType.equals(long.class)) { return Long.parseLong(key); } else if (originalType.equals(Integer.class) || originalType.equals(int.class)) { @@ -254,6 +249,25 @@ private static Object getOriginalValue(Class originalType, String key) { } } + /** + * Convert the Object back to the original type for the method call. + * + * @param originalType original type + * @param key the key value passed in + * @return the value as the original type + */ + private static Object getOriginalValue(Class originalType, Object key) { + if (originalType.equals(Float.class) || originalType.equals(float.class)) { + // key could be a float or double + return key instanceof Double ? Float.valueOf(key.toString()) : (Float) key; + } else if (originalType.equals(Long.class) || originalType.equals(long.class)) { + // key could be BigInteger or long + return Long.valueOf(key.toString()); + } + + return key; + } + /** * Return the original date/time value. * diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index 6c4f1afabd4..acad476cb81 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -292,14 +292,12 @@ public Map execute(String query, String operationName, Map formattedResult.add(e instanceof TemporalAccessor ? dateTimeFormatter .format((TemporalAccessor) e) : e) ); + LOGGER.info("Formatted result = " + formattedResult); return formattedResult; - } else { return originalResult instanceof TemporalAccessor ? dateTimeFormatter.format((TemporalAccessor) originalResult) : originalResult; @@ -430,6 +431,7 @@ public static Object formatNumber(Object originalResult, boolean isScalar, Numbe Collection formattedResult = new ArrayList(); Collection originalCollection = (Collection) originalResult; originalCollection.forEach(e -> formattedResult.add(numberFormat.format(originalResult))); + LOGGER.info("Formatted result = " + formattedResult); return formattedResult; } return numberFormat.format(originalResult); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 1ec727099ef..d8a8bfdba04 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -344,7 +344,6 @@ private void processDefaultDateTimeValues(Schema schema) { Stream streamInputTypes = schema.getInputTypes().stream().map(it -> (SchemaType) it); Stream streamAll = Stream.concat(streamInputTypes, schema.getTypes().stream()); streamAll.forEach(t -> { - System.out.println(t); t.getFieldDefinitions().forEach(fd -> { String returnType = fd.getReturnType(); // only check Date/Time/DateTime scalars that are not Queries or don't have data fetchers @@ -783,7 +782,13 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth if (propertyName != null && !isFormatEmpty(format)) { if (!isGraphQLType(valueClassName)) { dataFetcher = retrieveFormattingDataFetcher(format, propertyName, graphQLType); - context.addFormatter(discoveredMethod.method, propertyName, (FormattingProvider) dataFetcher); + // TODO: Is the following required after refactor? + // context.addFormatter(discoveredMethod.method, propertyName, (FormattingProvider) dataFetcher); + + // if the format is a number format then set teh return type to String + if (NUMBER.equals(format[0])) { + graphQLType = STRING; + } } } else { // Add a PropertyDataFetcher if the name has been changed via annotation @@ -1035,6 +1040,14 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, if (isSetArrayMandatory && !isArrayReturnTypeMandatory) { isArrayReturnTypeMandatory = true; } + + // if the set method has a format then this should overwrite any formatting + // as this is an InputType + String[] writeMethodFormat = getFormattingAnnotation(writeMethod); + if (writeMethodFormat[0] != null) { + format = writeMethodFormat; + } + } } else { NonNull methodAnnotation = method.getAnnotation(NonNull.class); @@ -1051,6 +1064,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, || nonNullAnnotation != null && defaultValue == null; } catch (NoSuchFieldException e) { + // TODO: ? } if (fieldHasIdAnnotation || method.getAnnotation(Id.class) != null) { @@ -1058,8 +1072,8 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, returnClazzName = ID; } - // check for format on the property - if (field != null) { + // check for format on the property but only override if it is null + if (field != null && (format.length == 0 || format[0] == null)) { format = getFormattingAnnotation(field); } } else { @@ -1075,7 +1089,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, // check for method return type number format String[] methodFormat = getFormattingAnnotation(method); - if (methodFormat[0] != null) { + if (methodFormat[0] != null && !isInputType) { format = methodFormat; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index dd9ed37de83..c1622ef2075 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -184,6 +184,10 @@ public final class SchemaGeneratorHelper { */ public static final String FORMATTED_TIME_SCALAR = "FormattedTime"; + /** + * Formatted Int. + */ + /** * Date scalar (with default formatting). */ @@ -199,7 +203,6 @@ public final class SchemaGeneratorHelper { */ public static final String TIME_SCALAR = "Time"; - /** * Defines a {@link BigDecimal} type. */ @@ -218,27 +221,27 @@ public final class SchemaGeneratorHelper { /** * GraphQL Int. */ - protected static final String INT = "Int"; + public static final String INT = "Int"; /** * GraphQL Float. */ - protected static final String FLOAT = "Float"; + public static final String FLOAT = "Float"; /** * GraphQL String. */ - protected static final String STRING = "String"; + public static final String STRING = "String"; /** * GraphQL ID. */ - protected static final String ID = "ID"; + public static final String ID = "ID"; /** * GraphQL Boolean. */ - protected static final String BOOLEAN = "Boolean"; + public static final String BOOLEAN = "Boolean"; /** * Logger. @@ -529,6 +532,21 @@ protected static Class getSafeClass(String clazzName) { } } + /** + * Return true if the class is a date, time or date/time. + * + * @param clazz the {@link Class} to check + * @return true if the class is a date, time or date/time + */ + protected static boolean isDateTimeClass(Class clazz) { + return clazz != null && ( + clazz.equals(LocalDate.class) + || clazz.equals(LocalTime.class) + || clazz.equals(LocalDateTime.class) + || clazz.equals(OffsetTime.class) + || clazz.equals(OffsetDateTime.class)); + } + /** * Return true if the {@link Class} is a valid {@link Class} to apply the {@link Id} annotation. * diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java index 6e9182a10cb..127c381d81c 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java @@ -17,6 +17,7 @@ package io.helidon.microprofile.graphql.server; import java.io.IOException; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -32,7 +33,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import io.helidon.microprofile.graphql.server.test.db.TestDB; -import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesAndMutations; import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; import org.jboss.weld.junit5.WeldInitiator; @@ -46,7 +47,7 @@ public class DateTimeIT extends AbstractGraphQLIT { @WeldSetup private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(SimpleQueriesNoArgs.class) + .addBeanClass(SimpleQueriesAndMutations.class) .addBeanClass(DateTimePojo.class) .addBeanClass(TestDB.class) .addExtension(new GraphQLCdiExtension())); @@ -54,7 +55,7 @@ public class DateTimeIT extends AbstractGraphQLIT { @Test @SuppressWarnings("unchecked") public void testDateAndTime() throws IOException { - setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesNoArgs.class); + setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesAndMutations.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); Schema schema = executionContext.getSchema(); @@ -89,6 +90,7 @@ public void testDateAndTime() throws IOException { assertDefaultFormat(type, "zonedDateTime", "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'", true); assertDefaultFormat(type, "localDateNoFormat", "yyyy-MM-dd", true); assertDefaultFormat(type, "significantDates", "yyyy-MM-dd", true); + assertDefaultFormat(type, "formattedListOfDates", "dd/MM", false); // testing the conversion of the following scalars when they have default formatting applied // FormattedDate -> Date @@ -112,11 +114,12 @@ public void testDateAndTime() throws IOException { Map mapResults = getAndAssertResult( executionContext.execute("query { dateAndTimePOJOQuery { offsetDateTime offsetTime zonedDateTime " - + "localDate localDate2 localTime localDateTime significantDates } }")); + + "localDate localDate2 localTime localDateTime significantDates " + + "formattedListOfDates } }")); assertThat(mapResults.size(), is(1)); Map mapResults2 = (Map) mapResults.get("dateAndTimePOJOQuery"); assertThat(mapResults2, is(notNullValue())); - assertThat(mapResults2.size(), is(8)); + assertThat(mapResults2.size(), is(9)); assertThat(mapResults2.get("localDate"), is("02/17/1968")); assertThat(mapResults2.get("localDate2"), is("08/04/1970")); @@ -129,6 +132,12 @@ public void testDateAndTime() throws IOException { assertThat(listDates.get(0), is("1968-02-17")); assertThat(listDates.get(1), is("1970-08-04")); + listDates = (List) mapResults2.get("formattedListOfDates"); + assertThat(listDates, is(notNullValue())); + assertThat(listDates.size(), is(2)); + assertThat(listDates.get(0), is("17/02")); + assertThat(listDates.get(1), is("04/08")); + mapResults = getAndAssertResult( executionContext.execute("query { localDateListFormat }")); assertThat(mapResults, is(notNullValue())); @@ -147,4 +156,46 @@ public void testDateAndTime() throws IOException { assertThat(fd.getReturnType(), is(DATE_SCALAR)); } + @Test + @SuppressWarnings("unchecked") + public void testDatesAndMutations() throws IOException { + setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesAndMutations.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Map mapResults = getAndAssertResult( + executionContext.execute("mutation { dateTimePojoMutation { formattedListOfDates } }")); + assertThat(mapResults.size(), is(1)); + Map mapResults2 = (Map) mapResults.get("dateTimePojoMutation"); + assertThat(mapResults2, is(notNullValue())); + + List listDates = (List) mapResults2.get("formattedListOfDates"); + assertThat(listDates, is(notNullValue())); + assertThat(listDates.size(), is(2)); + assertThat(listDates.get(0), is("17/02")); + assertThat(listDates.get(1), is("04/08")); + + mapResults = getAndAssertResult(executionContext.execute("mutation { echoLocalDate(dateArgument: \"17/02/1968\") }")); + assertThat(mapResults.size(), is(1)); + String result = (String) mapResults.get("echoLocalDate"); + assertThat(result, is("1968-02-17")); + + Map results = executionContext.execute("mutation { echoLocalDate(dateArgument: \"Today\") }"); + List> listErrors = (List>) results.get(ExecutionContext.ERRORS); + assertThat(listErrors, is(notNullValue())); + assertThat(listErrors.size(), is(1)); + + mapResults = getAndAssertResult( + executionContext.execute("mutation { testDefaultFormatLocalDateTime(dateTime: \"2020-01-12T10:00:00\") }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("testDefaultFormatLocalDateTime"), is( "10:00:00 12-01-2020")); + + + mapResults = getAndAssertResult( + executionContext.execute("query { transformedDate }")); + assertThat(mapResults, is(notNullValue())); + result = (String) mapResults.get("transformedDate"); + assertThat(result, is("16 Aug 2016")); + + } + } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java index 792d5afb727..3da8f7cea1b 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java @@ -21,8 +21,11 @@ import java.math.BigInteger; import java.util.Map; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import io.helidon.microprofile.graphql.server.test.db.TestDB; @@ -48,15 +51,16 @@ public class NumberFormatIT extends AbstractGraphQLIT { .addBeanClass(TestDB.class) .addExtension(new GraphQLCdiExtension())); - @Test + @SuppressWarnings("unchecked") public void testNumberFormats() throws IOException { setupIndex(indexFileName, SimpleContactWithNumberFormats.class, NumberFormatQueriesAndMutations.class); - ExecutionContext executionContext = new ExecutionContext(defaultContext); + ExecutionContext executionContext = new ExecutionContext(defaultContext); Map mapResults = getAndAssertResult(executionContext .execute("query { simpleFormattingQuery { id name age " - + "bankBalance value longValue bigDecimal } }")); + + "bankBalance value longValue bigDecimal " + + "} }")); assertThat(mapResults.size(), is(1)); Map mapResults2 = (Map) mapResults.get("simpleFormattingQuery"); @@ -73,32 +77,90 @@ public void testNumberFormats() throws IOException { assertThat(mapResults, is(notNullValue())); assertThat(mapResults.get("generateDoubleValue"), is("Double-123456789")); + mapResults = getAndAssertResult(executionContext.execute("mutation { transformedNumber(arg0: 123) }")); + assertThat(mapResults, is(notNullValue())); + assertThat(mapResults.get("transformedNumber"), is("number 123")); + mapResults = getAndAssertResult(executionContext.execute("query { echoBigDecimalUsingFormat(param1: \"BD-123\") }")); assertThat(mapResults, is(notNullValue())); assertThat(mapResults.get("echoBigDecimalUsingFormat"), is(BigDecimal.valueOf(123))); + mapResults = getAndAssertResult(executionContext.execute("mutation { echoBankBalance(bankBalance: \"$ 106,963.87\") }")); + assertThat(mapResults, is(notNullValue())); + assertThat(mapResults.get("echoBankBalance"), is(Double.valueOf("106963.87"))); + + mapResults = getAndAssertResult(executionContext.execute("mutation { echoFloat(size: 10.0123) }")); + assertThat(mapResults, is(notNullValue())); + assertThat(mapResults.get("echoFloat"), is(Double.valueOf("10.0123"))); + + mapResults = getAndAssertResult(executionContext.execute("mutation { idNumber(name: \"Tim\", id: 123) }")); + assertThat(mapResults, is(notNullValue())); + assertThat(mapResults.get("idNumber"), is("Tim-123")); + + + // COH-21891 - mapResults = getAndAssertResult(executionContext.execute("query { listAsString(arg1: [ [ \"value 12.12\", \"value 33.33\"] ] ) }")); + mapResults = getAndAssertResult( + executionContext.execute("query { listAsString(arg1: [ [ \"value 12.12\", \"value 33.33\"] ] ) }")); assertThat(mapResults, is(notNullValue())); // create a new contact -// String contactInput = -// "contact: {" -// + "id: \"1 id\" " -// + "name: \"Tim\" " -// + "age: \"20 years old\" " -// + "bankBalance: \"$ 1000.01\" " -// + "value: \"9 value\" " -// + "longValue: 12345" -// + "bigDecimal: \"BigDecimal-12345\"" -// + " } "; -// + String contactInput = + "contact: {" + + "id: 1 " + + "name: \"Tim\" " + + "age: \"20 years old\" " + + "bankBalance: \"$ 1000.01\" " + + "value: \"9 value\" " + + "longValue: \"LongValue-123\"" + + "bigDecimal: \"BigDecimal-12345\"" + + " } "; + // mapResults = getAndAssertResult( // executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + // ") { id name } }")); // assertThat(mapResults.size(), is(1)); // mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); // assertThat(mapResults2, is(notNullValue())); + } + + @Test + public void testCorrectNumberScalarTypesAndFormats() throws IOException { + setupIndex(indexFileName, SimpleContactWithNumberFormats.class, NumberFormatQueriesAndMutations.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Schema schema = executionContext.getSchema(); + + // validate the formats on the type + SchemaType type = schema.getTypeByName("SimpleContactWithNumberFormats"); + assertThat(type, is(notNullValue())); + + SchemaFieldDefinition fd = getFieldDefinition(type, "id"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is("0 'id'")); + assertThat(fd.getReturnType(), is(STRING)); + + fd = getFieldDefinition(type, "age"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is("0 'years old'")); + assertThat(fd.getReturnType(), is(STRING)); + + // validate the formats on the Input Type + SchemaType inputType = schema.getInputTypeByName("SimpleContactWithNumberFormatsInput"); + assertThat(inputType, is(notNullValue())); + fd = getFieldDefinition(inputType, "id"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is(nullValue())); + assertThat(fd.getReturnType(), is(INT)); + + fd = getFieldDefinition(inputType, "longValue"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is("LongValue-##########")); + assertThat(fd.getReturnType(), is(STRING)); + + fd = getFieldDefinition(inputType, "age"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is("0 'years old'")); + assertThat(fd.getReturnType(), is(STRING)); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index 7c5e6dca3f5..e43cb2684e8 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -35,7 +35,7 @@ import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAndNameAnnotation; import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation; import io.helidon.microprofile.graphql.server.test.queries.OddNamedQueriesAndMutations; -import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesNoArgs; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesAndMutations; import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; import io.helidon.microprofile.graphql.server.test.types.Address; import io.helidon.microprofile.graphql.server.test.types.Car; @@ -282,7 +282,7 @@ public void testGetSimpleName() throws ClassNotFoundException { @Test public void testAllMethods() throws IntrospectionException, ClassNotFoundException { Map mapMethods = schemaGenerator - .retrieveAllAnnotatedBeanMethods(SimpleQueriesNoArgs.class); + .retrieveAllAnnotatedBeanMethods(SimpleQueriesAndMutations.class); assertThat(mapMethods, is(notNullValue())); assertDiscoveredMethod(mapMethods.get("hero"), "hero", STRING, null, false, false, false); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java index 8f932cb15ca..f50453a627f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java @@ -18,6 +18,12 @@ import java.math.BigDecimal; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -27,6 +33,7 @@ import java.util.Random; import java.util.UUID; +import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty; import javax.enterprise.context.ApplicationScoped; @@ -185,4 +192,14 @@ public DefaultValuePOJO generatePOJO(String id, int value) { public TypeWithNameAndJsonbProperty getTypeWithNameAndJsonbProperty() { return new TypeWithNameAndJsonbProperty("name1", "name2", "name3", "name4", "name5", "name6"); } + + public DateTimePojo getDateTimePOJO() { + return new DateTimePojo(LocalDate.of(1968, 2, 17), + LocalDate.of(1970, 8, 4), + LocalTime.of(10, 10, 20), + OffsetTime.of(8, 10, 1, 0, ZoneOffset.UTC), + LocalDateTime.now(), OffsetDateTime.now(), ZonedDateTime.now(), LocalDate.now(), + List.of(LocalDate.of(1968, 2, 17), LocalDate.of(1970, 8, 4)), + List.of(LocalDate.of(1968, 2, 17), LocalDate.of(1970, 8, 4))); + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java index 5f8bbcec832..57a48803760 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java @@ -21,6 +21,7 @@ import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; +import javax.json.bind.annotation.JsonbNumberFormat; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Name; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java index 658cd8b459d..ea5b82c3e63 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java @@ -16,7 +16,6 @@ package io.helidon.microprofile.graphql.server.test.queries; - import javax.enterprise.context.ApplicationScoped; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; @@ -70,11 +69,34 @@ public BigDecimal echoBigDecimalUsingFormat(@Name("param1") @NumberFormat("BD-## @Query public List getListAsString(@Name("arg1") @JsonbNumberFormat("ignore 00.0000000") - List> values) { + List> values) { if (values != null) { return values.stream().map(Object::toString).collect(Collectors.toList()); } return new ArrayList<>(); } + + @Mutation + public Double echoBankBalance(@JsonbNumberFormat(value = "¤ ###,###.##", locale = "en-US") + @Name("bankBalance") Double bankBalance) { + return bankBalance; + } + + @Mutation + public float echoFloat(@Name("size") float size) { + return size; + } + + @Mutation + @NumberFormat(value = "number #", locale = "en-GB") + public Integer transformedNumber(Integer input) { + return input; + } + + @Mutation + public String idNumber(@Name("name") String name, + @Name("id") Long idNumber) { + return name + "-" + idNumber; + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java similarity index 84% rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java index 3775dd390da..a72ee2946e3 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesNoArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java @@ -33,6 +33,7 @@ import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; +import javax.json.bind.annotation.JsonbDateFormat; import javax.json.bind.annotation.JsonbProperty; import io.helidon.microprofile.graphql.server.test.db.TestDB; @@ -44,8 +45,10 @@ import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf; import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs; import org.eclipse.microprofile.graphql.DateFormat; +import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Id; +import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.Query; @@ -54,12 +57,12 @@ */ @GraphQLApi @ApplicationScoped -public class SimpleQueriesNoArgs { +public class SimpleQueriesAndMutations { @Inject private TestDB testDB; - public SimpleQueriesNoArgs() { + public SimpleQueriesAndMutations() { } @Query @@ -155,21 +158,40 @@ public MultiLevelListsAndArrays returnLists() { @Query("dateAndTimePOJOQuery") public DateTimePojo dateTimePojo() { - return new DateTimePojo(LocalDate.of(1968,2,17), - LocalDate.of(1970,8,4), - LocalTime.of(10,10,20), - OffsetTime.of(8, 10, 1, 0, ZoneOffset.UTC), - LocalDateTime.now(), OffsetDateTime.now(), ZonedDateTime.now(), LocalDate.now(), - List.of(LocalDate.of(1968,2,17), LocalDate.of(1970,8,4))); + return testDB.getDateTimePOJO(); } @Query("localDateListFormat") public List<@DateFormat("dd/MM/yyyy") LocalDate> getLocalDateListFormat() { - return List.of(LocalDate.of(1968,2,17), LocalDate.of(1970,8,4)); + return List.of(LocalDate.of(1968, 2, 17), LocalDate.of(1970, 8, 4)); + } + + @Query + @DateFormat(value = "dd MMM yyyy") + public LocalDate transformedDate() { + String date = "2016-08-16"; + return LocalDate.parse(date); } @Query("localDateNoFormat") public LocalDate localDateNoFormat() { - return LocalDate.of(1968,02,17); + return LocalDate.of(1968, 02, 17); + } + + @Mutation + public DateTimePojo dateTimePojoMutation() { + return testDB.getDateTimePOJO(); } + + @Mutation + public LocalDate echoLocalDate(@DateFormat("dd/MM/yyyy") @Name("dateArgument") LocalDate date) { + return date; + } + + @Mutation + @JsonbDateFormat("HH:mm:ss dd-MM-yyyy") + public LocalDateTime testDefaultFormatLocalDateTime(@Name("dateTime") LocalDateTime localDateTime) { + return localDateTime; + } + } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java index 396ca06195c..adb057a61f2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java @@ -56,6 +56,8 @@ public class DateTimePojo { private List significantDates; + private List formattedListOfDates; + public DateTimePojo(LocalDate localDate, LocalDate localDate2, LocalTime localTime, @@ -64,7 +66,8 @@ public DateTimePojo(LocalDate localDate, OffsetDateTime offsetDateTime, ZonedDateTime zonedDateTime, LocalDate localDateNoFormat, - List significantDates) { + List significantDates, + List formattedListOfDates) { this.localDate = localDate; this.localDate2 = localDate2; this.localTime = localTime; @@ -74,6 +77,7 @@ public DateTimePojo(LocalDate localDate, this.zonedDateTime = zonedDateTime; this.localDateNoFormat = localDateNoFormat; this.significantDates = significantDates; + this.formattedListOfDates = formattedListOfDates; } public LocalTime getLocalTimeNoFormat() { @@ -155,4 +159,12 @@ public void setSignificantDates(List significantDates) { public List getSignificantDates() { return this.significantDates; } + + public List<@DateFormat("dd/MM") LocalDate> getFormattedListOfDates() { + return formattedListOfDates; + } + + public void setFormattedListOfDates(List formattedListOfDates) { + this.formattedListOfDates = formattedListOfDates; + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java index 55dcfcd7bad..93e227cb786 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java @@ -26,13 +26,14 @@ import java.util.List; /** - * Defines a simple contact which contains number formats. + * Defines a simple contact which contains a number formats. */ @Type public class SimpleContactWithNumberFormats { private Integer id; private String name; + // this formatting will apply to both Input and standard type @NumberFormat("0 'years old'") private Integer age; @@ -70,6 +71,7 @@ public SimpleContactWithNumberFormats(Integer id, this.bigDecimal = bigDecimal; } + // this format should only apply to the standard type and not Input Type @NumberFormat("0 'id'") public Integer getId() { return id; @@ -119,6 +121,8 @@ public Long getLongValue() { return longValue; } + // this format should only be applied to the InputType and not the Type + @NumberFormat("LongValue-##########") public void setLongValue(Long longValue) { this.longValue = longValue; } From a17bff7bade36f416e491946818b656dae66a18f Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 15 Sep 2020 15:52:27 +0800 Subject: [PATCH 084/178] Fixup handling of Collections - part 1 --- .../graphql/server/DataFetcherUtils.java | 12 ++++++++++++ .../graphql/server/SimpleMutationsIT.java | 7 +++++++ .../server/test/mutations/SimpleMutations.java | 8 ++++++++ 3 files changed, 27 insertions(+) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 2a6157ba479..fae92fc1c74 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -29,7 +29,11 @@ import java.time.temporal.TemporalAccessor; import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import java.util.UUID; import java.util.logging.Logger; @@ -101,6 +105,14 @@ public static DataFetcher newMethodDataFetcher(Class clazz, Method met // this means the type is an input type so convert it to the correct class instance listArgumentValues.add(JsonUtils.convertFromJson(JsonUtils.convertMapToJson((Map) key), argument.getOriginalType())); + } else if (key instanceof Collection) { + // handle collection type - just working on simple String type for the moment + // TODO: Need to handle collections of types + // TODO: need to handle formatting + // ensure we preserve the order + listArgumentValues.add(argument.getOriginalType().equals(List.class) + ? new ArrayList((Collection) key) + : new TreeSet((Collection) key)); } else { // standard type or enum Class originalType = argument.getOriginalType(); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java index 1786c49bf1f..6ef76233ca5 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java @@ -71,6 +71,13 @@ public void testSimpleMutations() throws IOException { mapResults = getAndAssertResult(executionContext.execute("mutation { echoStringValue(value: \"echo\") }")); assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("echoStringValue"), is("echo")); + + mapResults = getAndAssertResult(executionContext.execute( + "mutation { testStringArrays(places: [\"place1\", \"place2\", \"place3\"]) }")); + assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("testStringArrays"), is("place1place2place3")); + + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java index 57a48803760..d9da658ee4d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java @@ -25,6 +25,7 @@ import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Name; +import java.util.Set; /** * Class that holds simple mutations definitions with various numbers of arguments. @@ -64,4 +65,11 @@ public String testId(@Name("name") String name, @Name("id") Long idNumber) { public SimpleContact createNewContact(@Name("newContact") SimpleContact contact) { return contact; } + + @Mutation + public String testStringArrays(@Name("places") Set places) { + StringBuilder sb = new StringBuilder(); + places.forEach(sb::append); + return sb.toString(); + } } From bdd9a2f483c6ed58419f54eea45144089203b0af Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 16 Sep 2020 14:43:46 +0800 Subject: [PATCH 085/178] TCK progress --- microprofile/graphql/server/pom.xml | 3 +- .../graphql/server/CustomDateTimeScalar.java | 141 ------------------ .../graphql/server/CustomScalars.java | 1 + .../graphql/server/DataFetcherUtils.java | 42 +++--- .../graphql/server/ExecutionContext.java | 2 +- .../graphql/server/SchemaGenerator.java | 2 +- .../graphql/server/AbstractGraphQLIT.java | 4 +- .../graphql/server/DateTimeIT.java | 14 +- .../graphql/server/JandexUtilsIT.java | 6 +- .../microprofile/graphql/server/NullIT.java | 23 ++- .../graphql/server/NumberFormatIT.java | 7 + .../graphql/server/test/db/TestDB.java | 5 + .../NumberFormatQueriesAndMutations.java | 6 + ...java => QueriesAndMutationsWithNulls.java} | 27 +++- microprofile/tests/tck/tck-graphql/pom.xml | 9 ++ 15 files changed, 111 insertions(+), 181 deletions(-) delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomDateTimeScalar.java rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/{QueriesWithNulls.java => QueriesAndMutationsWithNulls.java} (78%) diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 77860e092b9..19a7ac3fbf7 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -1,5 +1,4 @@ - - + + + jakarta.el-api jakarta.el @@ -103,8 +85,8 @@ test - org.jboss.weld - weld-junit5 + io.helidon.microprofile.tests + helidon-microprofile-tests-junit5 test diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index c4cf648ecdb..edf507e9b1e 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -172,13 +172,18 @@ protected static Object generateArgumentValue(Schema schema, String argumentType return JsonUtils.convertFromJson(JsonUtils.convertMapToJson(mapConverted), originalType); } else if (rawValue instanceof Collection) { - // handle collection type - just working on simple String type for the moment - // TODO: Need to handle collections of types - // TODO: need to handle formatting - // ensure we preserve the order - return originalType.equals(List.class) - ? new ArrayList((Collection) rawValue) - : new TreeSet((Collection) rawValue); + SchemaInputType inputType = schema.getInputTypeByName(argumentType); + if (inputType != null) { + // handle complex types + System.out.println(inputType); +// SchemaFieldDefinition fd = inputType.getFieldDefinitionByName(fdName); + throw new RuntimeException("Arg"); + } else { + // ensure we preserve the order + return originalType.equals(List.class) + ? new ArrayList((Collection) rawValue) + : new TreeSet((Collection) rawValue); + } } else { return parseArgumentValue(originalType, argumentType, rawValue, format); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java index ee36b3e78ef..013a3c5f944 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -16,6 +16,9 @@ package io.helidon.microprofile.graphql.server; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java index 252af47b4a4..5e2be8c44e7 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java @@ -26,16 +26,19 @@ import io.helidon.microprofile.graphql.server.test.types.SimpleContact; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -43,19 +46,18 @@ /** * Tests for {@link DataFetcherUtils} class. */ -@ExtendWith(WeldJunit5Extension.class) + +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(SimpleQueriesWithArgs.class) +@AddBean(SimpleContactWithNumberFormats.class) +@AddBean(ContactRelationship.class) +@AddBean(SimpleContact.class) +@AddBean(TestDB.class) class DataFetcherUtilsIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(SimpleQueriesWithArgs.class) - .addBeanClass(SimpleContactWithNumberFormats.class) - .addBeanClass(ContactRelationship.class) - .addBeanClass(SimpleContact.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - @Test public void testSimpleContact() throws Exception { setupIndex(indexFileName, SimpleQueriesWithArgs.class, SimpleContact.class); @@ -178,6 +180,37 @@ public void testArraysAndObjects() { } + @Test + @SuppressWarnings("unchecked") + public void testCollections() throws Exception { + setupIndex(indexFileName, SimpleQueriesWithArgs.class, SimpleContact.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Schema schema = executionContext.getSchema(); + + List listInteger = getList(1, 2, 3); + List listString = getList("A", "B", "C"); + Collection colBigInteger = getList(BigInteger.valueOf(1), BigInteger.valueOf(222), BigInteger.valueOf(333)); + + assertArgumentResult(schema, "echoListOfIntegers", "value", listInteger, listInteger); + assertArgumentResult(schema, "echoListOfStrings", "value", listString, listString); + assertArgumentResult(schema, "echoListOfBigIntegers", "value", colBigInteger, colBigInteger); + + // TODO: Test formatting + } + + @Test + @SuppressWarnings("unchecked") + public void testCollectionsAndObjects() throws IOException { + setupIndex(indexFileName, SimpleQueriesWithArgs.class, SimpleContact.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Schema schema = executionContext.getSchema(); + + // simple collections + List listContacts = getList(new SimpleContact("c1", "Contact 1", 50), + new SimpleContact("c2", "Contact 2", 52)); + + } + @Test @SuppressWarnings("unchecked") public void testObjectGraphs() throws Exception { @@ -194,11 +227,19 @@ public void testObjectGraphs() throws Exception { // Create a map representing the above contact relationship Map mapContactRel = Map.of("relationship", "married", - "contact1", contact1Map, - "contact2", contact2Map); + "contact1", contact1Map, + "contact2", contact2Map); assertArgumentResult(schema, "canFindContactRelationship", "relationship", mapContactRel, relationship); + } + @SuppressWarnings({"rawtypes","unchecked"}) + protected List getList(Object ... values) { + ArrayList list = new ArrayList(); + for (Object value : values) { + list.add(value); + } + return list; } /** diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java index 62fee85b479..91688d001fe 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java @@ -36,22 +36,21 @@ import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesAndMutations; import io.helidon.microprofile.graphql.server.test.types.DateTimePojo; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; + +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(SimpleQueriesAndMutations.class) +@AddBean(DateTimePojo.class) +@AddBean(TestDB.class) public class DateTimeIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(SimpleQueriesAndMutations.class) - .addBeanClass(DateTimePojo.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - @Test @SuppressWarnings("unchecked") public void testDateAndTime() throws IOException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java index 59b688df71c..4ab0b717b8c 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java @@ -21,36 +21,33 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.queries.DefaultValueQueries; -import io.helidon.microprofile.graphql.server.test.queries.DescriptionQueries; import io.helidon.microprofile.graphql.server.test.queries.OddNamedQueriesAndMutations; import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO; -import io.helidon.microprofile.graphql.server.test.types.DescriptionType; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; + +import io.helidon.microprofile.tests.junit5.AddBean; + +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; + /** * Tests for default values. */ -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(DefaultValuePOJO.class) +@AddBean(DefaultValueQueries.class) +@AddBean(OddNamedQueriesAndMutations.class) +@AddBean(TestDB.class) public class DefaultValuesIT extends AbstractGraphQLIT { - - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(DefaultValuePOJO.class) - .addBeanClass(DefaultValueQueries.class) - .addBeanClass(OddNamedQueriesAndMutations.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - - + @Test @SuppressWarnings("unchecked") public void setOddNamedQueriesAndMutations() throws IOException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java index 4802d1ea5ed..52392787c16 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java @@ -27,22 +27,20 @@ import io.helidon.microprofile.graphql.server.test.queries.DescriptionQueries; import io.helidon.microprofile.graphql.server.test.types.DescriptionType; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(DescriptionType.class) +@AddBean(DescriptionQueries.class) +@AddBean(TestDB.class) public class DescriptionIT extends AbstractGraphQLIT { - - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(DescriptionType.class) - .addBeanClass(DescriptionQueries.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - + @Test public void testDescriptions() throws IOException { setupIndex(indexFileName, DescriptionType.class, DescriptionQueries.class); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java index 8b289b440c7..13be96be932 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java @@ -26,25 +26,24 @@ import io.helidon.microprofile.graphql.server.test.queries.InvalidQueries; import io.helidon.microprofile.graphql.server.test.queries.VoidQueries; import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; + +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; /** * Container for Error conditions tests. */ -public class ErrorConditionsTestContainer { - @ExtendWith(WeldJunit5Extension.class) +public class ErrorConditionsTestContainer { + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(VoidMutations.class) public static class VoidMutationsIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(VoidMutations.class) - .addExtension(new GraphQLCdiExtension())); - @Test public void testVoidMutations() throws IOException { setupIndex(indexFileName, VoidMutations.class); @@ -52,14 +51,12 @@ public void testVoidMutations() throws IOException { } } - @ExtendWith(WeldJunit5Extension.class) + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(VoidMutations.class) public static class VoidQueriesIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(VoidMutations.class) - .addExtension(new GraphQLCdiExtension())); - @Test public void testVoidQueries() throws IOException { setupIndex(indexFileName, VoidQueries.class); @@ -67,14 +64,12 @@ public void testVoidQueries() throws IOException { } } - @ExtendWith(WeldJunit5Extension.class) + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(VoidMutations.class) public static class InvalidQueriesIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(VoidMutations.class) - .addExtension(new GraphQLCdiExtension())); - @Test public void testInvalidQueries() throws IOException { setupIndex(indexFileName, InvalidQueries.class); @@ -82,14 +77,12 @@ public void testInvalidQueries() throws IOException { } } - @ExtendWith(WeldJunit5Extension.class) + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(VoidMutations.class) public static class DuplicateQueriesAndMutationsIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(VoidMutations.class) - .addExtension(new GraphQLCdiExtension())); - @Test public void testDuplicateQueryOrMutationNames() throws IOException { setupIndex(indexFileName, DuplicateNameQueries.class); @@ -97,13 +90,12 @@ public void testDuplicateQueryOrMutationNames() throws IOException { } } - @ExtendWith(WeldJunit5Extension.class) - public static class InvalidNamedTypeIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) - .addExtension(new GraphQLCdiExtension())); + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(InvalidNamedTypes.InvalidNamedPerson.class) + public static class InvalidNamedTypeIT extends AbstractGraphQLIT { @Test public void testInvalidNamedType() throws IOException { @@ -112,14 +104,12 @@ public void testInvalidNamedType() throws IOException { } } - @ExtendWith(WeldJunit5Extension.class) + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(InvalidNamedTypes.InvalidNamedPerson.class) public static class InvalidNamedInputTypeIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) - .addExtension(new GraphQLCdiExtension())); - @Test public void testInvalidNamedInputType() throws IOException { setupIndex(indexFileName, InvalidNamedTypes.InvalidInputType.class); @@ -127,13 +117,12 @@ public void testInvalidNamedInputType() throws IOException { } } - @ExtendWith(WeldJunit5Extension.class) - public static class InvalidNamedInterfaceIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) - .addExtension(new GraphQLCdiExtension())); + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(InvalidNamedTypes.InvalidNamedPerson.class) + public static class InvalidNamedInterfaceIT extends AbstractGraphQLIT { @Test public void testInvalidNamedInterface() throws IOException { @@ -142,13 +131,12 @@ public void testInvalidNamedInterface() throws IOException { } } - @ExtendWith(WeldJunit5Extension.class) - public static class InvalidNamedQueryIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) - .addExtension(new GraphQLCdiExtension())); + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(InvalidNamedTypes.ClassWithInvalidQuery.class) + public static class InvalidNamedQueryIT extends AbstractGraphQLIT { @Test public void testInvalidNamedQuery() throws IOException { @@ -157,13 +145,12 @@ public void testInvalidNamedQuery() throws IOException { } } - @ExtendWith(WeldJunit5Extension.class) - public static class InvalidNamedMutationIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) - .addExtension(new GraphQLCdiExtension())); + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(InvalidNamedTypes.ClassWithInvalidMutation.class) + public static class InvalidNamedMutationIT extends AbstractGraphQLIT { @Test public void testInvalidNamedMutation() throws IOException { @@ -172,13 +159,12 @@ public void testInvalidNamedMutation() throws IOException { } } - @ExtendWith(WeldJunit5Extension.class) - public static class InvalidNamedEnumIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(InvalidNamedTypes.InvalidNamedPerson.class) - .addExtension(new GraphQLCdiExtension())); + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean( InvalidNamedTypes.Size.class) + public static class InvalidNamedEnumIT extends AbstractGraphQLIT { @Test public void testInvalidNameEnum() throws IOException { @@ -186,5 +172,4 @@ public void testInvalidNameEnum() throws IOException { assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext)); } } - } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java index 13f3806f330..79ffd32b7c2 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java @@ -26,15 +26,17 @@ import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.hamcrest.Matchers; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; + import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; + import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; @@ -45,16 +47,15 @@ * Integration tests for testing exception handing in {@link SchemaGeneratorTest}. */ @SuppressWarnings("unchecked") -@ExtendWith(WeldJunit5Extension.class) -public class ExceptionHandlingIT extends AbstractGraphQLIT { - - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(ExceptionQueries.class) - .addBeanClass(SimpleContact.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(ExceptionQueries.class) +@AddBean(SimpleContact.class) +@AddBean(TestDB.class) +public class ExceptionHandlingIT extends AbstractGraphQLIT { + @Test public void testAllDefaultsForConfig() throws IOException { setupConfig(null); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java index 25f8342cf55..c2a00b34597 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java @@ -29,23 +29,23 @@ import io.helidon.microprofile.graphql.server.test.queries.QueriesWithIgnorable; import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; + +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; + import org.opentest4j.AssertionFailedError; -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(QueriesWithIgnorable.class) +@AddBean(ObjectWithIgnorableFieldsAndMethods.class) +@AddBean(TestDB.class) public class IngorableIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(QueriesWithIgnorable.class) - .addBeanClass(ObjectWithIgnorableFieldsAndMethods.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - @Test public void testObjectWithIgnorableFields() throws IOException { setupIndex(indexFileName, ObjectWithIgnorableFieldsAndMethods.class); @@ -85,5 +85,4 @@ public void testIgnorable() throws IOException { is(0L)); assertThat(inputType.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("valueSetter")).count(), is(1L)); } - } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java index 5ac2d942d90..bfbe835441e 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java @@ -26,24 +26,21 @@ import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithName; import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithNameValue; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(SimpleContactInputType.class) +@AddBean(SimpleContactInputTypeWithName.class) +@AddBean(SimpleContactInputTypeWithNameValue.class) +@AddBean(SimpleContactInputTypeWithAddress.class) public class InputTypeIT extends AbstractGraphQLIT { - - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(SimpleContactInputType.class) - .addBeanClass(SimpleContactInputTypeWithName.class) - .addBeanClass(SimpleContactInputTypeWithNameValue.class) - .addBeanClass(SimpleContactInputTypeWithAddress.class) - .addExtension(new GraphQLCdiExtension())); - - + @Test public void testInputType() throws IOException { setupIndex(indexFileName, SimpleContactInputType.class, SimpleContactInputTypeWithName.class, diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java index 61b00d5f016..86ed4729997 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java @@ -25,23 +25,22 @@ import io.helidon.microprofile.graphql.server.test.types.Motorbike; import io.helidon.microprofile.graphql.server.test.types.Vehicle; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(Vehicle.class) +@AddBean(Car.class) +@AddBean(Motorbike.class) +@AddBean(AbstractVehicle.class) +@AddBean(TestDB.class) public class InterfaceOnlyAnnotatedIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(Vehicle.class) - .addBeanClass(Car.class) - .addBeanClass(Motorbike.class) - .addBeanClass(AbstractVehicle.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); /** * Test discovery of interfaces when only the interface annotated. */ diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java index b77a3282768..21ebdde0378 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java @@ -25,25 +25,22 @@ import io.helidon.microprofile.graphql.server.test.types.Vehicle; import io.helidon.microprofile.graphql.server.test.types.VehicleIncident; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(Vehicle.class) +@AddBean(Car.class) +@AddBean(Motorbike.class) +@AddBean(VehicleIncident.class) +@AddBean(TestDB.class) public class InterfaceTypeOnlyAnnotatedIT extends AbstractGraphQLIT { - - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(Vehicle.class) - .addBeanClass(Car.class) - .addBeanClass(Motorbike.class) - .addBeanClass(Motorbike.class) - .addBeanClass(VehicleIncident.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - + /** * Test discovery of interfaces and subsequent unresolved type which has a Name annotation . */ diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java index 72acc27bc17..75048b18bac 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java @@ -7,26 +7,19 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; -import io.helidon.microprofile.graphql.server.AbstractGraphQLIT; -import io.helidon.microprofile.graphql.server.GraphQLCdiExtension; -import io.helidon.microprofile.graphql.server.Schema; -import io.helidon.microprofile.graphql.server.SchemaGenerator; import io.helidon.microprofile.graphql.server.test.types.Level0; -import io.helidon.microprofile.graphql.server.test.types.Person; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(Level0.class) public class Level0IT extends AbstractGraphQLIT { - - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(Level0.class) - .addExtension(new GraphQLCdiExtension())); - + @Test public void testLevel0() throws IOException, IntrospectionException, ClassNotFoundException { setupIndex(indexFileName, Level0.class); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java index fd23993a22b..eb2c9a1c46a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java @@ -30,26 +30,23 @@ import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries; import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; /** * Tests for Multi-level arrays. */ -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(MultiLevelListsAndArrays.class) +@AddBean(ArrayAndListQueries.class) +@AddBean(TestDB.class) public class MultiLevelArraysIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(MultiLevelListsAndArrays.class) - .addBeanClass(ArrayAndListQueries.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - - @Test public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassNotFoundException, IOException { setupIndex(indexFileName, MultiLevelListsAndArrays.class); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java index bf5ad07b06d..c8f7897c4c1 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java @@ -28,25 +28,25 @@ import io.helidon.microprofile.graphql.server.test.queries.QueriesAndMutationsWithNulls; import io.helidon.microprofile.graphql.server.test.types.NullPOJO; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; + +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; + /** * Tests for Nulls. */ -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(NullPOJO.class) +@AddBean(QueriesAndMutationsWithNulls.class) +@AddBean(TestDB.class) public class NullIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(NullPOJO.class) - .addBeanClass(QueriesAndMutationsWithNulls.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - @Test @SuppressWarnings("unchecked") public void testNulls() throws IOException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java index 223e960a019..63669829cf7 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java @@ -33,25 +33,23 @@ import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; /** * Tests for NUmber formats. */ -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(SimpleContactWithNumberFormats.class) +@AddBean(NumberFormatQueriesAndMutations.class) +@AddBean(TestDB.class) public class NumberFormatIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(SimpleContactWithNumberFormats.class) - .addBeanClass(NumberFormatQueriesAndMutations.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - @Test @SuppressWarnings("unchecked") public void testNumberFormats() throws IOException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java index e3356100826..1526d92560a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java @@ -19,11 +19,11 @@ import io.helidon.microprofile.graphql.server.test.types.Person; import io.helidon.microprofile.graphql.server.test.types.PersonWithName; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import java.beans.IntrospectionException; import java.io.IOException; @@ -35,14 +35,12 @@ /** * Tests for naming of Pojo's. */ -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(Person.class) public class PojoNamingIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(Person.class) - .addExtension(new GraphQLCdiExtension())); - /** * Test generation of Type with no-name. */ diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java index 003f126b165..5a9608645b7 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java @@ -26,23 +26,20 @@ import io.helidon.microprofile.graphql.server.test.queries.PropertyNameQueries; import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(PropertyNameQueries.class) +@AddBean(TypeWithNameAndJsonbProperty.class) +@AddBean(TestDB.class) public class PropertyNameIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(PropertyNameQueries.class) - .addBeanClass(TypeWithNameAndJsonbProperty.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - @Test @SuppressWarnings("unchecked") public void testDifferentPropertyNames() throws IOException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java index 6ef76233ca5..c76256dfb60 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java @@ -26,21 +26,19 @@ import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(SimpleMutations.class) +@AddBean(TestDB.class) public class SimpleMutationsIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(SimpleMutations.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - @Test @SuppressWarnings("unchecked") public void testSimpleMutations() throws IOException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java index 2946e534017..5a2cafa3a7a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java @@ -37,26 +37,24 @@ import io.helidon.microprofile.graphql.server.test.types.ContactRelationship; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; /** * Tests for simple queries with args. */ -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(SimpleQueriesWithArgs.class) +@AddBean(Car.class) +@AddBean(AbstractVehicle.class) +@AddBean(TestDB.class) public class SimpleQueriesWithArgsIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(SimpleQueriesWithArgs.class) - .addBeanClass(Car.class) - .addBeanClass(AbstractVehicle.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - @Test @SuppressWarnings("unchecked") public void testSimpleQueryGenerationWithArgs() throws IOException { @@ -160,6 +158,7 @@ public void testSimpleQueryGenerationWithArgs() throws IOException { + json + ") }")); assertThat(mapResults.size(), is(1)); + assertThat(mapResults.get("canFindContactRelationship"), is(false)); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java index b838b497842..1d158ad93dc 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java @@ -27,25 +27,24 @@ import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource; import io.helidon.microprofile.graphql.server.test.types.SimpleContact; -import org.jboss.weld.junit5.WeldInitiator; -import org.jboss.weld.junit5.WeldJunit5Extension; -import org.jboss.weld.junit5.WeldSetup; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; + /** * Tests for Source annotation. */ -@ExtendWith(WeldJunit5Extension.class) +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddBean(SimpleQueriesWithSource.class) +@AddBean(SimpleContact.class) +@AddBean(TestDB.class) public class SourceIT extends AbstractGraphQLIT { - @WeldSetup - private final WeldInitiator weld = WeldInitiator.of(WeldInitiator.createWeld() - .addBeanClass(SimpleQueriesWithSource.class) - .addBeanClass(SimpleContact.class) - .addBeanClass(TestDB.class) - .addExtension(new GraphQLCdiExtension())); - @Test @SuppressWarnings("unchecked") public void testSimpleQueriesWithSource() throws IOException { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java index fc7d891c3fc..09dbc3bef9f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java @@ -320,4 +320,19 @@ public boolean canFindSimpleContactWithNumberFormats(@Name("contact") SimpleCont return false; } + @Query + public List echoListOfStrings(@Name("value") List value) { + return value; + } + + @Query + public List echoListOfIntegers(@Name("value") List value) { + return value; + } + + @Query + public List echoListOfBigIntegers(@Name("value") List value) { + return value; + } + } From a6ad3070fe8d180b41f6015466757a174ee7af8e Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 22 Sep 2020 15:29:40 +0800 Subject: [PATCH 093/178] progress - some failing tests --- .../graphql/server/DataFetcherUtils.java | 25 +++-- .../graphql/server/FormattingHelper.java | 24 ++++- .../graphql/server/SchemaArgument.java | 29 +++++- .../graphql/server/SchemaGenerator.java | 30 ++++-- .../graphql/server/AbstractGraphQLIT.java | 3 - .../graphql/server/DataFetcherUtilsIT.java | 98 +++++++++++++------ .../graphql/server/JandexUtilsIT.java | 1 + .../graphql/server/NumberFormatIT.java | 39 +++++--- .../graphql/server/SchemaArgumentTest.java | 4 + .../NumberFormatQueriesAndMutations.java | 3 +- .../test/queries/SimpleQueriesWithArgs.java | 39 ++++++++ .../server/test/types/SimpleContact.java | 10 +- .../types/SimpleContactWithNumberFormats.java | 8 ++ 13 files changed, 249 insertions(+), 64 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index edf507e9b1e..e8df00d4409 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -107,8 +107,10 @@ public static DataFetcher newMethodDataFetcher(Schema schema, Class cl if (args.length > 0) { for (SchemaArgument argument : args) { + Class originalType = argument.isArrayReturnType() ? argument.getOriginalArrayType() + : argument.getOriginalType(); listArgumentValues.add(generateArgumentValue(schema, argument.getArgumentType(), - argument.getOriginalType(), + originalType, environment.getArgument(argument.getArgumentName()), argument.getFormat())); } @@ -173,16 +175,23 @@ protected static Object generateArgumentValue(Schema schema, String argumentType } else if (rawValue instanceof Collection) { SchemaInputType inputType = schema.getInputTypeByName(argumentType); + Collection colResults = originalType.equals(List.class) ? new ArrayList() : new TreeSet(); if (inputType != null) { // handle complex types - System.out.println(inputType); -// SchemaFieldDefinition fd = inputType.getFieldDefinitionByName(fdName); - throw new RuntimeException("Arg"); + for (Object value : (Collection) rawValue) { + colResults.add(generateArgumentValue(schema, inputType.getName(), + Class.forName(inputType.getValueClassName()), value, EMPTY_FORMAT)); + } + + return colResults; } else { - // ensure we preserve the order - return originalType.equals(List.class) - ? new ArrayList((Collection) rawValue) - : new TreeSet((Collection) rawValue); + // standard type or scalar so ensure we preserve the order and + // convert any values with formats + for (Object value : (Collection) rawValue) { + colResults.add(parseArgumentValue(originalType, argumentType, value, format)); + } + + return colResults; } } else { return parseArgumentValue(originalType, argumentType, rawValue, format); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java index 331bfe7b057..f768106d5b8 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java @@ -246,14 +246,34 @@ protected static String[] getFormattingAnnotation(AnnotatedElement annotatedElem : new String[] {NUMBER, numberFormat[0], numberFormat[1] }; } + /** + * Return any formatting on a field/ + * + * @param jandexUtils {@link JandexUtils} to use + * @param clazz {@link Class} to check for annotation + * @param fieldName field name to check + * @return a String[] representing the format and locale + */ + protected static String[] getFieldFormat(JandexUtils jandexUtils, String clazz, + String fieldName) { + if (jandexUtils.hasIndex()) { + AnnotationInstance dateFormat1 = jandexUtils.getFieldAnnotation(clazz, fieldName, JSONB_DATE_FORMAT); + AnnotationInstance dateFormat2 = jandexUtils.getFieldAnnotation(clazz, fieldName, DATE_FORMAT); + AnnotationInstance numberFormat1 = jandexUtils.getFieldAnnotation(clazz, fieldName, JSONB_NUMBER_FORMAT); + AnnotationInstance numberFormat2 = jandexUtils.getFieldAnnotation(clazz, fieldName, NUMBER_FORMAT); + return getFormatFromAnnotationInstance(dateFormat1, dateFormat2, numberFormat1, numberFormat2); + } + return NO_FORMATTING; + } + /** * Return the method parameter format using Jandex. * - * @param jandexUtils {@link JandexUtils} to use + * @param jandexUtils {@link JandexUtils} to use * @param clazz {@link Class} to check for annotation * @param methodName method name to check * @param paramNumber parameter number to check - * @return + * @return a String[] representing the format and locale */ protected static String[] getMethodParameterFormat(JandexUtils jandexUtils, String clazz, String methodName, int paramNumber) { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index 0614ad29745..6350c803144 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -76,6 +76,11 @@ public class SchemaArgument */ private boolean isArrayReturnTypeMandatory; + /** + * Original array inner type if it is array type. + */ + private Class originalArrayType; + /** * Construct a {@link SchemaArgument} instance. * @@ -275,6 +280,26 @@ public boolean isArrayReturnTypeMandatory() { public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { isArrayReturnTypeMandatory = arrayReturnTypeMandatory; } + + /** + * Sets the original array type. + * + * @param originalArrayType the original array type + */ + public void setOriginalArrayType(Class originalArrayType) { + this.originalArrayType = originalArrayType; + } + + /** + * Returns the original array type. + * + * @return the original array type + */ + public Class getOriginalArrayType() { + return originalArrayType; + } + + @Override public String toString() { return "Argument{" @@ -286,6 +311,7 @@ public String toString() { + ", sourceArgument=" + sourceArgument + ", isReturnTypeMandatory=" + isArrayReturnTypeMandatory + ", isArrayReturnType=" + isArrayReturnType + + ", originalArrayType=" + originalArrayType + ", arrayLevels=" + arrayLevels + ", format=" + Arrays.toString(format) + ", description='" + getDescription() + '\'' + '}'; @@ -309,6 +335,7 @@ public boolean equals(Object o) { && Objects.equals(originalType, schemaArgument.originalType) && Objects.equals(format, schemaArgument.format) && Objects.equals(sourceArgument, schemaArgument.sourceArgument) + && Objects.equals(originalArrayType, schemaArgument.originalArrayType) && Objects.equals(getDescription(), schemaArgument.getDescription()) && Objects.equals(defaultValue, schemaArgument.defaultValue); } @@ -316,6 +343,6 @@ public boolean equals(Object o) { @Override public int hashCode() { return Objects.hash(super.hashCode(), argumentName, argumentType, sourceArgument, - isMandatory, defaultValue, getDescription(), originalType, format); + isMandatory, defaultValue, getDescription(), originalType, format, originalArrayType); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 08d019aac42..efcd3ca63c4 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -70,7 +70,9 @@ import static io.helidon.microprofile.graphql.server.FormattingHelper.formatNumber; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectDateFormatter; import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectNumberFormat; +import static io.helidon.microprofile.graphql.server.FormattingHelper.getFieldFormat; import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormattingAnnotation; +import static io.helidon.microprofile.graphql.server.FormattingHelper.getMethodParameterFormat; import static io.helidon.microprofile.graphql.server.FormattingHelper.isJsonbAnnotationPresent; import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.MUTATION_TYPE; import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.QUERY_TYPE; @@ -783,8 +785,6 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth if (propertyName != null && !isFormatEmpty(format)) { if (!isGraphQLType(valueClassName)) { dataFetcher = retrieveFormattingDataFetcher(format, propertyName, graphQLType); - // TODO: Is the following required after refactor? - // context.addFormatter(discoveredMethod.method, propertyName, (FormattingProvider) dataFetcher); // if the format is a number format then set teh return type to String if (NUMBER.equals(format[0])) { @@ -1080,6 +1080,10 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, // check for format on the property but only override if it is null if (field != null && (format.length == 0 || format[0] == null)) { format = getFormattingAnnotation(field); + if (isFormatEmpty(format)) { + // check to see format of the inner most class. E.g. List<@DateFomrat("DD/MM") String> + format = getFieldFormat(jandexUtils, className, field.getName()); + } isJsonbFormat = isJsonbAnnotationPresent(field); } } else { @@ -1125,7 +1129,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, discoveredMethod.setOriginalArrayType(Class.forName(realReturnType.returnClass)); } discoveredMethod.setReturnType(realReturnType.getReturnClass()); - if (realReturnType.getFormat() != null) { + if (!isFormatEmpty(realReturnType.getFormat())) { discoveredMethod.setFormat(realReturnType.format); } } else { @@ -1165,7 +1169,7 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM Class paramType = parameter.getType(); - ReturnType returnType = getReturnType(paramType, genericParameterTypes[i], i++, method); + ReturnType returnType = getReturnType(paramType, genericParameterTypes[i], i, method); if (parameter.getAnnotation(Id.class) != null) { validateIDClass(returnType.getReturnClass()); @@ -1182,8 +1186,18 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM new SchemaArgument(parameterName, returnType.getReturnClass(), isMandatory, argumentDefaultValue, paramType); String argumentDescription = getDescription(parameter.getAnnotation(Description.class)); - String[] argumentFormat = FormattingHelper.getFormattingAnnotation(parameter); argument.setDescription(argumentDescription); + + String[] argumentFormat = FormattingHelper.getFormattingAnnotation(parameter); + String[] argumentTypeFormat = getMethodParameterFormat(jandexUtils, + method.getDeclaringClass().getName(), method.getName(),i); + + // The argument type format overrides any argument format. E.g. NumberFormat should apply below + // E.g. public List getListAsString(@Name("arg1") + // @JsonbNumberFormat("ignore 00.0000000") + // List> values) + argumentFormat = !isFormatEmpty(argumentTypeFormat) ? argumentTypeFormat : argumentFormat; + if (argumentFormat[0] != null) { argument.setFormat(new String[] { argumentFormat[1], argumentFormat[2] }); argument.setArgumentType(String.class.getName()); @@ -1206,10 +1220,14 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM } argument.setArrayReturnTypeMandatory(returnType.isReturnTypeMandatory); argument.setArrayReturnType(returnType.isArrayType); + if (returnType.isArrayType) { + argument.setOriginalArrayType(getSafeClass(returnType.returnClass)); + } argument.setArrayLevels(returnType.getArrayLevels()); } discoveredMethod.addArgument(argument); + i++; } } } @@ -1326,7 +1344,7 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp if (isParameter) { hasAnnotation = jandexUtils .methodParameterHasAnnotation(clazzName, methodName, parameterNumber, nonNullClazz); - format = FormattingHelper.getMethodParameterFormat(jandexUtils, clazzName, methodName, + format = getMethodParameterFormat(jandexUtils, clazzName, methodName, parameterNumber); } else { hasAnnotation = jandexUtils.methodHasAnnotation(clazzName, methodName, nonNullClazz); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java index 013a3c5f944..ee36b3e78ef 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -16,9 +16,6 @@ package io.helidon.microprofile.graphql.server; -import io.helidon.microprofile.tests.junit5.AddExtension; -import io.helidon.microprofile.tests.junit5.DisableDiscovery; -import io.helidon.microprofile.tests.junit5.HelidonTest; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java index 5e2be8c44e7..2c2560c7edb 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java @@ -16,6 +16,7 @@ package io.helidon.microprofile.graphql.server; +import static io.helidon.microprofile.graphql.server.JsonUtils.convertObjectToMap; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -38,10 +39,13 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** * Tests for {@link DataFetcherUtils} class. @@ -75,20 +79,6 @@ public void testSimpleContactWithNumberFormats() throws Exception { ExecutionContext executionContext = new ExecutionContext(defaultContext); Schema schema = executionContext.getSchema(); - // input SimpleContactWithNumberFormatsInput { - // "0 'years old'" - // age: String - // "¤ 000.00 en-AU" - // bankBalance: String - // "BigDecimal-##########" - // bigDecimal: String - // id: Int - // "LongValue-##########" - // longValue: String - // name: String - // "0 'value'" - // value: String! - //} Map mapContact = Map.of("age", "52 years old", "bankBalance", "$ 100.00", "bigDecimal", "BigDecimal-123", "longValue", "LongValue-321", "name", "Tim", "value", "1 value", "id", "100"); @@ -171,44 +161,74 @@ public void testSimpleTypesWithFormats() throws Exception { } @Test - public void testArrays() { + public void testArrays() throws Exception { + setupIndex(indexFileName, SimpleQueriesWithArgs.class, SimpleContact.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Schema schema = executionContext.getSchema(); - } + String[] stringArray = new String[] {"A", "B", "C"}; + assertArgumentResult(schema, "echoStringArray", "value", stringArray, stringArray); - @Test - public void testArraysAndObjects() { + int[] intArray = new int[] {1, 2, 3, 4}; + assertArgumentResult(schema, "echoIntArray", "value", intArray, intArray); + + Boolean[] booleanArray = new Boolean[] {true, false, true, true}; + assertArgumentResult(schema, "echoBooleanArray", "value", booleanArray, booleanArray); + + String[][] stringArray2 = new String[][] { + { "A", "B", "C"}, + { "D", "E", "F"} + }; + assertArgumentResult(schema, "echoStringArray2", "value", stringArray2, stringArray2); + SimpleContact[] contactArray = new SimpleContact[] { + new SimpleContact("c1", "Contact 1", 50), + new SimpleContact("c2", "Contact 2", 52) + }; + assertArgumentResult(schema, "echoSimpleContactArray", "value", contactArray, contactArray); + + // TODO: Test formatting of numbers and dates in arrays + } @Test @SuppressWarnings("unchecked") - public void testCollections() throws Exception { + public void testSimpleCollections() throws Exception { setupIndex(indexFileName, SimpleQueriesWithArgs.class, SimpleContact.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); Schema schema = executionContext.getSchema(); List listInteger = getList(1, 2, 3); - List listString = getList("A", "B", "C"); + List listString = getList("A", "B", "C"); Collection colBigInteger = getList(BigInteger.valueOf(1), BigInteger.valueOf(222), BigInteger.valueOf(333)); assertArgumentResult(schema, "echoListOfIntegers", "value", listInteger, listInteger); assertArgumentResult(schema, "echoListOfStrings", "value", listString, listString); assertArgumentResult(schema, "echoListOfBigIntegers", "value", colBigInteger, colBigInteger); - // TODO: Test formatting + // Test formatting for numbers and dates + List listFormattedIntegers = List.of("1 years old", "3 years old", "53 years old"); + assertArgumentResult(schema, "echoFormattedListOfIntegers", "value", listFormattedIntegers, + List.of(1, 3, 53)); } @Test @SuppressWarnings("unchecked") - public void testCollectionsAndObjects() throws IOException { + public void testCollectionsAndObjects() throws Exception { setupIndex(indexFileName, SimpleQueriesWithArgs.class, SimpleContact.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); Schema schema = executionContext.getSchema(); - // simple collections - List listContacts = getList(new SimpleContact("c1", "Contact 1", 50), - new SimpleContact("c2", "Contact 2", 52)); + SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50); + SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 52); + + Collection colContacts = List.of(contact1, contact2); + Collection> listOfMaps = List.of(convertObjectToMap(contact1), convertObjectToMap(contact2)); + assertArgumentResult(schema, "echoCollectionOfSimpleContacts", "value", + listOfMaps, colContacts); + + // test multi-level collections } @Test @@ -222,8 +242,8 @@ public void testObjectGraphs() throws Exception { SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 53); ContactRelationship relationship = new ContactRelationship(contact1, contact2, "married"); - Map contact1Map = JsonUtils.convertObjectToMap(contact1); - Map contact2Map = JsonUtils.convertObjectToMap(contact2); + Map contact1Map = convertObjectToMap(contact1); + Map contact2Map = convertObjectToMap(contact2); // Create a map representing the above contact relationship Map mapContactRel = Map.of("relationship", "married", @@ -233,8 +253,8 @@ public void testObjectGraphs() throws Exception { assertArgumentResult(schema, "canFindContactRelationship", "relationship", mapContactRel, relationship); } - @SuppressWarnings({"rawtypes","unchecked"}) - protected List getList(Object ... values) { + @SuppressWarnings( { "rawtypes", "unchecked" }) + protected List getList(Object... values) { ArrayList list = new ArrayList(); for (Object value : values) { list.add(value); @@ -252,13 +272,29 @@ protected List getList(Object ... values) { * @param expected the expected output * @throws Exception if any errors */ + @SuppressWarnings({ "rawtypes", "unchecked" }) protected void assertArgumentResult(Schema schema, String fdName, String argumentName, Object input, Object expected) throws Exception { SchemaArgument argument = getArgument(schema, "Query", fdName, argumentName); assertThat(argument, is(notNullValue())); + Class originalType = argument.isArrayReturnType() ? argument.getOriginalArrayType() + : argument.getOriginalType(); Object result = DataFetcherUtils.generateArgumentValue(schema, argument.getArgumentType(), - argument.getOriginalType(), input, argument.getFormat()); - assertThat(result, is(expected)); + originalType, input, argument.getFormat()); + + if (input instanceof Collection) { + // compare each value + Collection colExpected = (Collection) expected; + Collection colResult = (Collection) result; + for (Object value : colExpected) { + if (!colResult.contains(value)) { + throw new AssertionError("Cannot find expected value [" + + value + "] in result " + colResult.toString()); + } + } + } else { + assertThat(result, is(expected)); + } } protected SchemaArgument getArgument(Schema schema, String typeName, String fdName, String argumentName) { diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java index f19a56d4c1e..8e94a313b83 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java @@ -21,6 +21,7 @@ import io.helidon.microprofile.graphql.server.test.queries.QueriesAndMutationsWithNulls; import io.helidon.microprofile.graphql.server.test.types.NullPOJO; +import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; import org.eclipse.microprofile.graphql.NonNull; import org.junit.jupiter.api.Test; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java index 63669829cf7..d1a867e4b18 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING; import static org.hamcrest.CoreMatchers.is; @@ -31,6 +32,7 @@ import io.helidon.microprofile.graphql.server.test.db.TestDB; import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations; +import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs; import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats; import io.helidon.microprofile.tests.junit5.AddBean; @@ -47,13 +49,15 @@ @AddExtension(GraphQLCdiExtension.class) @AddBean(SimpleContactWithNumberFormats.class) @AddBean(NumberFormatQueriesAndMutations.class) +@AddBean(SimpleQueriesWithArgs.class) @AddBean(TestDB.class) public class NumberFormatIT extends AbstractGraphQLIT { @Test @SuppressWarnings("unchecked") public void testNumberFormats() throws IOException { - setupIndex(indexFileName, SimpleContactWithNumberFormats.class, NumberFormatQueriesAndMutations.class); + setupIndex(indexFileName, SimpleContactWithNumberFormats.class, + NumberFormatQueriesAndMutations.class, SimpleQueriesWithArgs.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); Map mapResults = getAndAssertResult(executionContext @@ -102,11 +106,10 @@ public void testNumberFormats() throws IOException { // List listBigDecimals = (List) mapResults.get("echoBigDecimalList"); // assertThat(mapResults.get("listBigDecimals"), is("Tim-123")); - - - // COH-21891 + + // TODO: COH-21891 mapResults = getAndAssertResult( - executionContext.execute("query { listAsString(arg1: [ [ \"value 12.12\", \"value 33.33\"] ] ) }")); + executionContext.execute("query { listAsString(arg1: [ \"value 12.12\", \"value 33.33\"] ) }")); assertThat(mapResults, is(notNullValue())); // create a new contact @@ -121,12 +124,21 @@ public void testNumberFormats() throws IOException { + "bigDecimal: \"BigDecimal-12345\"" + " } "; -// mapResults = getAndAssertResult( -// executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + -// ") { id name } }")); -// assertThat(mapResults.size(), is(1)); -// mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); -// assertThat(mapResults2, is(notNullValue())); + mapResults = getAndAssertResult( + executionContext.execute("mutation { createSimpleContactWithNumberFormats (" + contactInput + + ") { id name } }")); + assertThat(mapResults.size(), is(1)); + mapResults2 = (Map) mapResults.get("createSimpleContactWithNumberFormats"); + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.get("id"), is("1 id")); + assertThat(mapResults2.get("name"), is("Tim")); + + mapResults = getAndAssertResult(executionContext.execute("query { echoFormattedListOfIntegers(value: [ \"1 years old\", \"3 " + + "years old\", \"53 years old\" ]) }")); + assertThat(mapResults, is(notNullValue())); + + List listResults = (List) mapResults.get("echoFormattedListOfIntegers"); + assertThat(listResults.size(), is(3)); } @Test @@ -166,6 +178,11 @@ public void testCorrectNumberScalarTypesAndFormats() throws IOException { assertThat(fd, is(notNullValue())); assertThat(fd.getFormat()[0], is("0 'years old'")); assertThat(fd.getReturnType(), is(STRING)); + + fd = getFieldDefinition(inputType, "listDates"); + assertThat(fd, is(notNullValue())); + assertThat(fd.getFormat()[0], is("DD-MM-YYYY")); + assertThat(fd.getReturnType(), is(FORMATTED_DATE_SCALAR)); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index 1107f97f6cd..3307eda73ca 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -71,6 +71,10 @@ public void testConstructors() { schemaArgument.setDefaultValue("1"); assertThat(schemaArgument.getDefaultValue(), is("1")); + + assertThat(schemaArgument.getOriginalArrayType(), is(nullValue())); + schemaArgument.setOriginalArrayType(String.class); + assertThat(schemaArgument.getOriginalArrayType().getName(), is(String.class.getName())); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java index de0ed96fd30..f251d4b866d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java @@ -69,8 +69,9 @@ public BigDecimal echoBigDecimalUsingFormat(@Name("param1") @NumberFormat("BD-## @Query public List getListAsString(@Name("arg1") + // this should be ignored as NumberFormat is Below @JsonbNumberFormat("ignore 00.0000000") - List> values) { + List<@NumberFormat("'value' 00.0000000") BigDecimal> values) { if (values != null) { return values.stream().map(Object::toString).collect(Collectors.toList()); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java index 09dbc3bef9f..b665097eb2d 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java @@ -335,4 +335,43 @@ public List echoListOfBigIntegers(@Name("value") List va return value; } + @Query + public List echoListOfSimpleContacts(@Name("value") List value) { + return value; + } + + @Query + public Collection echoCollectionOfSimpleContacts(@Name("value") Collection value) { + return value; + } + + @Query + public String[] echoStringArray(@Name("value") String[] value) { + return value; + } + + @Query + public int[] echoIntArray(@Name("value") int[] value) { + return value; + } + + @Query + public Boolean[] echoBooleanArray(@Name("value") Boolean[] value) { + return value; + } + + @Query + public String[][] echoStringArray2(@Name("value") String[][] value) { + return value; + } + + @Query + public SimpleContact[] echoSimpleContactArray(@Name("value") SimpleContact[] value) { + return value; + } + + @Query + public List echoFormattedListOfIntegers(@Name("value") List<@NumberFormat("0 'years old'") Integer> value) { + return value; + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java index 113b6e6f654..0f067b4b760 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java @@ -21,7 +21,7 @@ /** * Defines a simple contact. */ -public class SimpleContact { +public class SimpleContact implements Comparable { private String id; private String name; private int age; @@ -77,4 +77,12 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(id, name, age); } + + @Override + /** + * Simple comparison by age. + */ + public int compareTo(SimpleContact other) { + return Integer.valueOf(age).compareTo(Integer.valueOf(other.getAge())); + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java index e7fd4ee14b3..65e4f9d8103 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java @@ -124,6 +124,14 @@ public Long getLongValue() { return longValue; } + public List getListDates() { + return listDates; + } + + public void setListDates(List listDates) { + this.listDates = listDates; + } + // this format should only be applied to the InputType and not the Type @NumberFormat("LongValue-##########") public void setLongValue(Long longValue) { From c13e7ce4ed668982a16c70cd212a6f3f9206efa9 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 22 Sep 2020 16:08:59 +0800 Subject: [PATCH 094/178] progress --- .../graphql/server/DataFetcherUtils.java | 14 +++++++++----- .../microprofile/graphql/server/JandexUtils.java | 8 +++++++- .../graphql/server/SchemaGenerator.java | 2 +- .../graphql/server/DataFetcherUtilsIT.java | 9 +++------ .../graphql/server/SimpleMutationsIT.java | 3 --- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index e8df00d4409..dda5e02d258 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -107,10 +107,9 @@ public static DataFetcher newMethodDataFetcher(Schema schema, Class cl if (args.length > 0) { for (SchemaArgument argument : args) { - Class originalType = argument.isArrayReturnType() ? argument.getOriginalArrayType() - : argument.getOriginalType(); listArgumentValues.add(generateArgumentValue(schema, argument.getArgumentType(), - originalType, + argument.getOriginalType(), + argument.getOriginalArrayType(), environment.getArgument(argument.getArgumentName()), argument.getFormat())); } @@ -130,6 +129,7 @@ public static DataFetcher newMethodDataFetcher(Schema schema, Class cl * @param schema {@link Schema} to introspect if needed * @param argumentType the type of the argument * @param originalType the original type of the argument as a class + * @param originalArrayType if this is non null this means the array was a Collection and this is the type in the collection * @param rawValue raw value of the argument * @param format argument format * @return the argument value @@ -137,6 +137,7 @@ public static DataFetcher newMethodDataFetcher(Schema schema, Class cl */ @SuppressWarnings({"unchecked", "rawtypes" }) protected static Object generateArgumentValue(Schema schema, String argumentType, Class originalType, + Class originalArrayType, Object rawValue, String[] format) throws Exception{ if (rawValue instanceof Map) { @@ -159,6 +160,7 @@ protected static Object generateArgumentValue(Schema schema, String argumentType Map mapInputType = (Map) value; mapConverted.put(fdName, generateArgumentValue(schema, inputFdInputType.getName(), Class.forName(inputFdInputType.getValueClassName()), + null, value, EMPTY_FORMAT)); } else { if (fd.isJsonbFormat()) { @@ -175,12 +177,14 @@ protected static Object generateArgumentValue(Schema schema, String argumentType } else if (rawValue instanceof Collection) { SchemaInputType inputType = schema.getInputTypeByName(argumentType); + Collection colResults = originalType.equals(List.class) ? new ArrayList() : new TreeSet(); if (inputType != null) { // handle complex types for (Object value : (Collection) rawValue) { colResults.add(generateArgumentValue(schema, inputType.getName(), - Class.forName(inputType.getValueClassName()), value, EMPTY_FORMAT)); + originalArrayType, null, + value, EMPTY_FORMAT)); } return colResults; @@ -188,7 +192,7 @@ protected static Object generateArgumentValue(Schema schema, String argumentType // standard type or scalar so ensure we preserve the order and // convert any values with formats for (Object value : (Collection) rawValue) { - colResults.add(parseArgumentValue(originalType, argumentType, value, format)); + colResults.add(parseArgumentValue(originalArrayType, argumentType, value, format)); } return colResults; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java index 0a4a36035fb..6de32f408b7 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java @@ -152,7 +152,13 @@ protected AnnotationInstance getMethodParameterAnnotation(String clazz, String m if (hasIndex()) { ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz)); if (classByName != null) { - MethodInfo methodInfo = classByName.firstMethod(methodName); + MethodInfo methodInfo = null; + for (MethodInfo info : classByName.methods()) { + if (info.name().equals(methodName) && info.parameters().size() >= paramNumber + 1) { + methodInfo = info; + break; + } + } if (methodInfo != null) { ClassType classType = retrieveInnerMostType(methodInfo.parameters().get(paramNumber)); return classType == null ? null diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index efcd3ca63c4..9c19dc6fdaf 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -1190,7 +1190,7 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM String[] argumentFormat = FormattingHelper.getFormattingAnnotation(parameter); String[] argumentTypeFormat = getMethodParameterFormat(jandexUtils, - method.getDeclaringClass().getName(), method.getName(),i); + method.getDeclaringClass().getName(), method.getName(), i); // The argument type format overrides any argument format. E.g. NumberFormat should apply below // E.g. public List getListAsString(@Name("arg1") diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java index 2c2560c7edb..f9e2607fa79 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java @@ -34,18 +34,14 @@ import io.helidon.microprofile.tests.junit5.HelidonTest; import org.junit.jupiter.api.Test; -import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; - -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; + /** * Tests for {@link DataFetcherUtils} class. @@ -280,7 +276,8 @@ protected void assertArgumentResult(Schema schema, String fdName, Class originalType = argument.isArrayReturnType() ? argument.getOriginalArrayType() : argument.getOriginalType(); Object result = DataFetcherUtils.generateArgumentValue(schema, argument.getArgumentType(), - originalType, input, argument.getFormat()); + argument.getOriginalType(), argument.getOriginalArrayType(), + input, argument.getFormat()); if (input instanceof Collection) { // compare each value diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java index c76256dfb60..a24429e15a8 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java @@ -74,8 +74,5 @@ public void testSimpleMutations() throws IOException { "mutation { testStringArrays(places: [\"place1\", \"place2\", \"place3\"]) }")); assertThat(mapResults.size(), is(1)); assertThat(mapResults.get("testStringArrays"), is("place1place2place3")); - - } - } From 60a7affa39db1a584dfbd111323d5131504b027f Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 22 Sep 2020 16:46:35 +0800 Subject: [PATCH 095/178] re-enabled GraphQLEndpointIT tests --- .../server/AbstractGraphQLEndpointIT.java | 3 -- .../graphql/server/GraphQLEndpointIT.java | 33 +++++++++++++++---- .../graphql/server/test/types/Person.java | 3 ++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java index 81d5b1e2ab7..d082502a1c8 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java @@ -23,8 +23,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import io.helidon.microprofile.graphql.server.test.types.Person; -import io.helidon.microprofile.server.Server; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.WebTarget; @@ -145,5 +143,4 @@ protected Map getJsonResponse(Response response) { assertThat(stringResponse, is(notNullValue())); return JsonUtils.convertJSONtoMap(stringResponse); } - } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java index ca784d622a1..b347531a10b 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java @@ -20,6 +20,14 @@ import java.io.UnsupportedEncodingException; import java.util.Map; +import io.helidon.microprofile.config.ConfigCdiExtension; +import io.helidon.microprofile.server.JaxRsCdiExtension; +import io.helidon.microprofile.server.ServerCdiExtension; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddConfig; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; @@ -27,6 +35,7 @@ import io.helidon.microprofile.graphql.server.test.types.Person; +import org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.BeforeAll; @@ -41,7 +50,20 @@ /** * Integration tests for microprofile-graphql implementation via /graphql endpoint. */ -public class GraphQLEndpointIT extends AbstractGraphQLEndpointIT { +@HelidonTest +@DisableDiscovery +@AddExtension(GraphQLCdiExtension.class) +@AddExtension(ServerCdiExtension.class) +@AddExtension(JaxRsCdiExtension.class) +@AddExtension(ConfigCdiExtension.class) +@AddExtension(CdiComponentProvider.class) +@AddBean(Person.class) +@AddBean(GraphQLResource.class) +@AddBean(GraphQLApplication.class) +@AddConfig(key = "server.static.classpath.context", value = "/ui") +@AddConfig(key = "server.static.classpath.location", value = "/web") +public class GraphQLEndpointIT + extends AbstractGraphQLEndpointIT { @BeforeAll public static void setup() throws IOException { @@ -49,8 +71,8 @@ public static void setup() throws IOException { } @Test - @Disabled("COH-21920") - public void basicEndpointTests() throws UnsupportedEncodingException { + public void basicEndpointTests() + throws UnsupportedEncodingException { // test /graphql endpoint WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL); Map mapRequest = generateJsonRequest(QUERY_INTROSPECT, null, null); @@ -80,7 +102,7 @@ public void basicEndpointTests() throws UnsupportedEncodingException { } @Test - public void testUIEndpoint() throws InterruptedException { + public void testUIEndpoint() { // test /ui endpoint WebTarget webTarget = getGraphQLWebTarget().path(UI).path("index.html"); Response response = webTarget.request().get(); @@ -89,10 +111,7 @@ public void testUIEndpoint() throws InterruptedException { } @Test - @Disabled("COH-21920") public void testGetSchema() { - // see https://github.com/eclipse/microprofile-graphql/issues/202 - // add text/plain WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL).path(SCHEMA_URL); Response response = webTarget.request(MediaType.TEXT_PLAIN).get(); assertThat(response, is(notNullValue())); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java index 96d11e6eb32..a7ee8f31659 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Person.java @@ -44,6 +44,9 @@ public class Person { private long longValue; private BigDecimal bigDecimal; + public Person() { + } + public Person(int personId, String name, Address homeAddress, From f03a743d96710ad10f19c2587b902366bfb09447 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 22 Sep 2020 16:55:59 +0800 Subject: [PATCH 096/178] progress --- .../helidon/microprofile/graphql/server/GraphQLEndpointIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java index b347531a10b..7053bd2a7bc 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java @@ -39,7 +39,6 @@ import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static io.helidon.microprofile.graphql.server.GraphQLResource.SCHEMA_URL; From 8781ba66dedda21e7ba2ba3f0c735f3c00085f64 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 23 Sep 2020 08:44:18 +0800 Subject: [PATCH 097/178] COH-20749 --- .../graphql/server/GraphQLCdiExtension.java | 27 +++++++ .../graphql/server/SchemaGenerator.java | 47 +++++++---- .../graphql/server/SchemaPreProcessor.java | 31 +++++++ .../graphql/server/SchemaProcessor.java | 46 +++++++++++ .../SchemaPreProcessorTestContainer.java | 80 +++++++++++++++++++ 5 files changed, 217 insertions(+), 14 deletions(-) create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java create mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java index d2ac4cf7584..03060e91eec 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java @@ -33,8 +33,17 @@ * A CDI {@link Extension} to collect the classes that are of interest Microprofile GraphQL. */ public class GraphQLCdiExtension implements Extension { + + /** + * The {@link List} of collected API's. + */ private final List> collectedApis = new ArrayList<>(); + /** + * The {@link List} of collected Schema processors. + */ + private final List> collectedProcessors = new ArrayList<>(); + /** * Collect the classes that have the following Microprofile GraphQL annotations. * @@ -47,6 +56,15 @@ void collectApis(@Observes @WithAnnotations({GraphQLApi.class, this.collectedApis.add(processAnnotatedType.getAnnotatedType().getJavaClass()); } + /** + * Collect the classes that have the {@link SchemaProcessor} annotation. + * + * @param processAnnotatedType annotation types to process + */ + void collectProcessors(@Observes @WithAnnotations(SchemaProcessor.class) ProcessAnnotatedType processAnnotatedType) { + this.collectedProcessors.add(processAnnotatedType.getAnnotatedType().getJavaClass()); + } + /** * Returns the collected API's. * @@ -55,4 +73,13 @@ void collectApis(@Observes @WithAnnotations({GraphQLApi.class, public Class[] collectedApis() { return collectedApis.toArray(new Class[0]); } + + /** + * Returns the collected Schema Processors. + * + * @return the collected Schema Processors. + */ + public Class[] collectedSchemaProcessors() { + return collectedProcessors.toArray(new Class[0]); + } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 9c19dc6fdaf..7589b7c7917 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -20,7 +20,9 @@ import java.beans.Introspector; import java.beans.MethodDescriptor; import java.beans.PropertyDescriptor; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; @@ -181,22 +183,30 @@ public SchemaGenerator(Context context) { * * @return a {@link Schema} */ + @SuppressWarnings("rawtypes") public Schema generateSchema() throws IntrospectionException, ClassNotFoundException { - Class[] classes = CDI.current() + GraphQLCdiExtension extension = CDI.current() .getBeanManager() - .getExtension(GraphQLCdiExtension.class) - .collectedApis(); - - return generateSchemaFromClasses(classes); - } - - /** - * Return the {@link JandexUtils} instance. - * - * @return the {@link JandexUtils} instance. - */ - protected JandexUtils getJandexUtils() { - return jandexUtils; + .getExtension(GraphQLCdiExtension.class); + Class[] classes = extension.collectedApis(); + Class[] schemaProcessors = extension.collectedSchemaProcessors(); + + Schema schema = generateSchemaFromClasses(classes); + + for (Class schemaProcessor : schemaProcessors) { + if (SchemaPreProcessor.class.isAssignableFrom(schemaProcessor)) { + try { + Constructor constructor = schemaProcessor.getDeclaredConstructor(); + SchemaPreProcessor processor = (SchemaPreProcessor) constructor.newInstance(); + LOGGER.info("Calling SchemaPreProcessor " + processor); + processor.processSchema(schema); + } catch (Exception e) { + LOGGER.warning("Unable to constructor or call SchemaPreProcessor " + + schemaProcessor + ": " + e.getMessage()); + } + } + } + return schema; } /** @@ -1363,6 +1373,15 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp } } + /** + * Return the {@link JandexUtils} instance. + * + * @return the {@link JandexUtils} instance. + */ + protected JandexUtils getJandexUtils() { + return jandexUtils; + } + /** * Defines discovered methods for a class. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java new file mode 100644 index 00000000000..cddb2a6735c --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; + +/** + * Describes a {@link Schema} pre-processor that will be called prior to + * the GraphQL schema being generated. This allows for modification of the + * {@link Schema} before it is parsed. + */ +public interface SchemaPreProcessor { + /** + * Pre-process a {@link Schema} before it is turned into a GraphQL Schema. + * + * @param schema {@link Schema} to preprocess + */ + void processSchema(Schema schema); +} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java new file mode 100644 index 00000000000..48af179ba2a --- /dev/null +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a class as potentially containing a {@link SchemaPreProcessor}. + * + *

+ * For example: + *
+ * {@literal @}SchemaProcessor
+ * public class MySchemaProcessor implements SchemaPreProcessor {
+ *
+ *     {@literal @}Override
+ *     public processSchema(Schema schema) {
+ *        ....
+ *     }
+ *
+ * }
+ * 
+ */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface SchemaProcessor { +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java new file mode 100644 index 00000000000..7cfa6697a84 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java @@ -0,0 +1,80 @@ +package io.helidon.microprofile.graphql.server; + +import java.beans.IntrospectionException; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import io.helidon.microprofile.graphql.server.test.types.Person; +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; +import org.junit.jupiter.api.Test; + +public class SchemaPreProcessorTestContainer { + + /** + * Test valid {@link SchemaPreProcessor}. + */ + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(Person.class) + @AddBean(SchemaPreProcessorTestContainer.CustomSchemaPreProcessor.class) + public static class ValidSchemaPreProcessorIT extends AbstractGraphQLIT { + + @Test + public void testCustomPreProcessor() throws IOException, IntrospectionException, ClassNotFoundException { + setupIndex(indexFileName, Person.class); + SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); + Schema schema = schemaGenerator.generateSchema(); + + assertThat(schema.getInputTypeByName("PersonInput"), is(notNullValue())); + } + } + + /** + * Test invalid {@link SchemaPreProcessor}. + */ + @HelidonTest + @DisableDiscovery + @AddExtension(GraphQLCdiExtension.class) + @AddBean(Person.class) + @AddBean(SchemaPreProcessorTestContainer.InvalidSchemaPreProcessor.class) + public static class InvalidSchemaPreProcessorIT extends AbstractGraphQLIT { + + @Test + public void testCustomPreProcessor() throws IOException, IntrospectionException, ClassNotFoundException { + setupIndex(indexFileName, Person.class); + SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); + Schema schema = schemaGenerator.generateSchema(); + + // should be null as an invalid class was found + assertThat(schema.getInputTypeByName("PersonInput"), is(nullValue())); + } + } + + @SchemaProcessor + public static class CustomSchemaPreProcessor implements SchemaPreProcessor { + + @Override + public void processSchema(Schema schema) { + // create a new input type from the Person type + SchemaInputType inputType = schema.getTypeByName("Person").createInputType("Input"); + schema.addInputType(inputType); + } + } + + /** + * A {@link SchemaProcessor} which is invalid because it does not implement + * {@link SchemaPreProcessor}. + */ + @SchemaProcessor + public static class InvalidSchemaPreProcessor { + + } +} From 42cea9d950c7d7c3295becec453590ff6cff2334 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 23 Sep 2020 13:17:49 +0800 Subject: [PATCH 098/178] fixup enums and more --- .../graphql/server/DataFetcherUtils.java | 28 +++++++++------ .../microprofile/graphql/server/Schema.java | 2 +- .../graphql/server/SchemaGenerator.java | 3 +- .../graphql/server/SchemaGeneratorHelper.java | 34 ++++++++++--------- .../graphql/server/SchemaPreProcessor.java | 4 +-- .../graphql/server/SchemaProcessor.java | 2 +- .../graphql/server/DataFetcherUtilsIT.java | 26 ++++++++------ .../graphql/server/DateTimeIT.java | 8 +++++ .../graphql/server/NumberFormatIT.java | 9 ++--- .../SchemaPreProcessorTestContainer.java | 2 +- .../server/SimpleQueriesWithArgsIT.java | 15 ++++---- .../microprofile/graphql/server/SourceIT.java | 3 +- .../graphql/server/test/db/TestDB.java | 6 ++-- .../NumberFormatQueriesAndMutations.java | 2 +- .../queries/SimpleQueriesAndMutations.java | 7 +++- .../test/queries/SimpleQueriesWithArgs.java | 7 ++++ .../server/test/types/SimpleContact.java | 27 +++++++++++---- 17 files changed, 122 insertions(+), 63 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index dda5e02d258..82cebc54713 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -35,6 +35,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeSet; import java.util.UUID; import java.util.logging.Logger; @@ -157,7 +158,6 @@ protected static Object generateArgumentValue(Schema schema, String argumentType // check to see if the Field Definition return type is an input type SchemaInputType inputFdInputType = schema.getInputTypeByName(fd.getReturnType()); if (inputFdInputType != null && value instanceof Map) { - Map mapInputType = (Map) value; mapConverted.put(fdName, generateArgumentValue(schema, inputFdInputType.getName(), Class.forName(inputFdInputType.getValueClassName()), null, @@ -177,12 +177,20 @@ protected static Object generateArgumentValue(Schema schema, String argumentType } else if (rawValue instanceof Collection) { SchemaInputType inputType = schema.getInputTypeByName(argumentType); + Object colResults = null; + Class castClass; + if (List.class.isAssignableFrom(originalType)) { + colResults = new ArrayList(); + castClass = List.class; + } else { + colResults = new TreeSet(); + castClass = Set.class; + } - Collection colResults = originalType.equals(List.class) ? new ArrayList() : new TreeSet(); if (inputType != null) { // handle complex types for (Object value : (Collection) rawValue) { - colResults.add(generateArgumentValue(schema, inputType.getName(), + ((Collection) colResults).add(generateArgumentValue(schema, inputType.getName(), originalArrayType, null, value, EMPTY_FORMAT)); } @@ -192,10 +200,10 @@ protected static Object generateArgumentValue(Schema schema, String argumentType // standard type or scalar so ensure we preserve the order and // convert any values with formats for (Object value : (Collection) rawValue) { - colResults.add(parseArgumentValue(originalArrayType, argumentType, value, format)); + ((Collection) colResults).add(parseArgumentValue(originalArrayType, argumentType, value, format)); } - return colResults; + return castClass.cast(colResults); } } else { return parseArgumentValue(originalType, argumentType, rawValue, format); @@ -355,15 +363,15 @@ private static Object getOriginalValue(Class originalType, Object key) { try { Constructor constructor = originalType.getDeclaredConstructor(String.class); numberKey = (Number) constructor.newInstance(key); - } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | - InvocationTargetException eIgnore) { + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException + | InvocationTargetException eIgnore) { // cannot find a constructor with String arg } } - + if (numberKey != null) { - return originalType.equals(Float.class) ? Float.valueOf(numberKey.floatValue()) - : originalType.equals(float.class) ? numberKey.floatValue() + return originalType.equals(Float.class) ? Float.valueOf(numberKey.floatValue()) + : originalType.equals(float.class) ? numberKey.floatValue() : originalType.equals(Integer.class) ? Integer.valueOf(numberKey.intValue()) : originalType.equals(int.class) ? numberKey.intValue() : originalType.equals(Long.class) ? Long.valueOf(numberKey.longValue()) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java index 8748d0a767c..b456f6cac0a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java @@ -231,7 +231,7 @@ public RuntimeWiring getRuntimeWiring() { // register the scalars getScalars().forEach(s -> { - LOGGER.info("Register Scalar: " + s); + LOGGER.finest("Register Scalar: " + s); builder.scalar(s.getGraphQLScalarType()); }); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 7589b7c7917..c095214a3b6 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -940,7 +940,8 @@ protected Map retrieveGetterBeanMethods(Class clazz if (optionalPdReadMethod.isPresent()) { PropertyDescriptor propertyDescriptor = optionalPdReadMethod.get(); - boolean ignoreWriteMethod = isInputType && shouldIgnoreMethod(propertyDescriptor.getWriteMethod(), true); + Method writeMethod = propertyDescriptor.getWriteMethod(); + boolean ignoreWriteMethod = isInputType && writeMethod != null && shouldIgnoreMethod(writeMethod, true); // only include if the field should not be ignored if (!shouldIgnoreField(clazz, propertyDescriptor.getName()) && !ignoreWriteMethod) { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index c1622ef2075..c22df1f5755 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -498,22 +498,24 @@ protected static String getFieldName(Class clazz, String fieldName) { * @return the field name or null if non exist */ protected static String getMethodName(Method method) { - Query queryAnnotation = method.getAnnotation(Query.class); - Mutation mutationAnnotation = method.getAnnotation(Mutation.class); - Name nameAnnotation = method.getAnnotation(Name.class); - JsonbProperty jsonbPropertyAnnotation = method.getAnnotation(JsonbProperty.class); - if (queryAnnotation != null && !queryAnnotation.value().isBlank()) { - return queryAnnotation.value(); - } - if (mutationAnnotation != null && !mutationAnnotation.value().isBlank()) { - return mutationAnnotation.value(); - } - if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { - // Name annotation is specified so use this and don't bother checking JsonbProperty - return nameAnnotation.value(); - } - if (jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank()) { - return jsonbPropertyAnnotation.value(); + if (method != null) { + Query queryAnnotation = method.getAnnotation(Query.class); + Mutation mutationAnnotation = method.getAnnotation(Mutation.class); + Name nameAnnotation = method.getAnnotation(Name.class); + JsonbProperty jsonbPropertyAnnotation = method.getAnnotation(JsonbProperty.class); + if (queryAnnotation != null && !queryAnnotation.value().isBlank()) { + return queryAnnotation.value(); + } + if (mutationAnnotation != null && !mutationAnnotation.value().isBlank()) { + return mutationAnnotation.value(); + } + if (nameAnnotation != null && !nameAnnotation.value().isBlank()) { + // Name annotation is specified so use this and don't bother checking JsonbProperty + return nameAnnotation.value(); + } + if (jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isBlank()) { + return jsonbPropertyAnnotation.value(); + } } return null; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java index cddb2a6735c..8f4b5323ada 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java @@ -18,8 +18,8 @@ /** * Describes a {@link Schema} pre-processor that will be called prior to - * the GraphQL schema being generated. This allows for modification of the - * {@link Schema} before it is parsed. + * the GraphQL schema being generated but after the {@link Schema} has been created. + * This allows for modification of the {@link Schema} before it is parsed. */ public interface SchemaPreProcessor { /** diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java index 48af179ba2a..10d65dc2dc1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java @@ -24,7 +24,7 @@ /** * Marks a class as potentially containing a {@link SchemaPreProcessor}. - * + * *

* For example: *
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
index f9e2607fa79..8c37d1d83b7 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
@@ -22,6 +22,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs;
 import io.helidon.microprofile.graphql.server.test.types.ContactRelationship;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
@@ -36,6 +37,7 @@
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -64,8 +66,8 @@ public void testSimpleContact() throws Exception {
         ExecutionContext executionContext = new ExecutionContext(defaultContext);
         Schema schema = executionContext.getSchema();
 
-        Map mapContact = Map.of("id", "1", "name", "Tim", "age", 52);
-        SimpleContact simpleContact = new SimpleContact("1", "Tim", 52);
+        Map mapContact = Map.of("id", "1", "name", "Tim", "age", 52, "tShirtSize", "L");
+        SimpleContact simpleContact = new SimpleContact("1", "Tim", 52, EnumTestWithEnumName.L);
         assertArgumentResult(schema, "canFindContact", "contact", mapContact, simpleContact);
     }
 
@@ -178,8 +180,8 @@ public void testArrays() throws Exception {
         assertArgumentResult(schema, "echoStringArray2", "value", stringArray2, stringArray2);
 
         SimpleContact[] contactArray = new SimpleContact[] {
-               new SimpleContact("c1", "Contact 1", 50),
-                new SimpleContact("c2", "Contact 2", 52)
+               new SimpleContact("c1", "Contact 1", 50, EnumTestWithEnumName.XL),
+                new SimpleContact("c2", "Contact 2", 52, EnumTestWithEnumName.XL)
         };
         assertArgumentResult(schema, "echoSimpleContactArray", "value", contactArray, contactArray);
 
@@ -206,6 +208,12 @@ public void testSimpleCollections() throws Exception {
         List listFormattedIntegers = List.of("1 years old", "3 years old", "53 years old");
         assertArgumentResult(schema, "echoFormattedListOfIntegers", "value", listFormattedIntegers,
                              List.of(1, 3, 53));
+
+        LocalDate localDate1 = LocalDate.of(2020, 9, 23);
+        LocalDate localDate2 = LocalDate.of(2020, 9, 22);
+        List listLocalDates = List.of("23-09-2020", "22-09-2020");
+        assertArgumentResult(schema, "echoFormattedLocalDate", "value", listLocalDates,
+                     List.of(localDate1, localDate2));
     }
 
     @Test
@@ -215,8 +223,8 @@ public void testCollectionsAndObjects() throws Exception {
         ExecutionContext executionContext = new ExecutionContext(defaultContext);
         Schema schema = executionContext.getSchema();
 
-        SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50);
-        SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 52);
+        SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50, EnumTestWithEnumName.XL);
+        SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 52, EnumTestWithEnumName.XXL);
 
         Collection colContacts = List.of(contact1, contact2);
         Collection> listOfMaps = List.of(convertObjectToMap(contact1), convertObjectToMap(contact2));
@@ -234,8 +242,8 @@ public void testObjectGraphs() throws Exception {
         ExecutionContext executionContext = new ExecutionContext(defaultContext);
         Schema schema = executionContext.getSchema();
 
-        SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50);
-        SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 53);
+        SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50, EnumTestWithEnumName.M);
+        SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 53, EnumTestWithEnumName.L);
         ContactRelationship relationship = new ContactRelationship(contact1, contact2, "married");
 
         Map contact1Map = convertObjectToMap(contact1);
@@ -273,8 +281,6 @@ protected void assertArgumentResult(Schema schema, String fdName,
                                         String argumentName, Object input, Object expected) throws Exception {
         SchemaArgument argument = getArgument(schema, "Query", fdName, argumentName);
         assertThat(argument, is(notNullValue()));
-        Class originalType = argument.isArrayReturnType() ? argument.getOriginalArrayType()
-                            : argument.getOriginalType();
         Object result = DataFetcherUtils.generateArgumentValue(schema, argument.getArgumentType(),
                                                                argument.getOriginalType(), argument.getOriginalArrayType(),
                                                                input, argument.getFormat());
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
index 91688d001fe..20da1da1e8c 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
@@ -153,6 +153,14 @@ public void testDateAndTime() throws IOException {
         assertThat(fd.getDescription(), is(nullValue()));
         assertThat(fd.isDefaultFormatApplied(), is(true));
         assertThat(fd.getReturnType(), is(DATE_SCALAR));
+
+        mapResults = getAndAssertResult(
+        executionContext.execute("query { echoFormattedLocalDateWithReturnFormat(value: [ \"23-09-2020\", \"22-09-2020\" ]) }"));
+        assertThat(mapResults, is(notNullValue()));
+        listDates = (ArrayList) mapResults.get("echoFormattedLocalDateWithReturnFormat");
+        assertThat(listDates.size(), is(2));
+        assertThat(listDates.get(0), is("23/09"));
+        assertThat(listDates.get(1), is("22/09"));
     }
 
     @Test
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
index d1a867e4b18..0dbaa89a892 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
@@ -101,10 +101,11 @@ public void testNumberFormats() throws IOException {
         assertThat(mapResults.get("idNumber"), is("Tim-123"));
 
         // TODO: Fix
-//        mapResults = getAndAssertResult(executionContext.execute("mutation { echoBigDecimalList(coordinates: [ 10.0123, -23.000 ]) }"));
-//        assertThat(mapResults, is(notNullValue()));
-//        List listBigDecimals = (List) mapResults.get("echoBigDecimalList");
-//        assertThat(mapResults.get("listBigDecimals"), is("Tim-123"));
+        mapResults = getAndAssertResult(executionContext.execute("mutation { echoBigDecimalList(coordinates: [ 10.0123, -23.000 ]) }"));
+        assertThat(mapResults, is(notNullValue()));
+        List listBigDecimals = (List) mapResults.get("echoBigDecimalList");
+        assertThat(listBigDecimals.get(0), is(BigDecimal.valueOf(10.0123)));
+        assertThat(listBigDecimals.get(1), is(BigDecimal.valueOf(-23.000)));
 
         
         // TODO: COH-21891
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java
index 7cfa6697a84..605a4fdad40 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java
@@ -33,6 +33,7 @@ public void testCustomPreProcessor() throws IOException, IntrospectionException,
             SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext);
             Schema schema = schemaGenerator.generateSchema();
 
+            // SchemaPreProcessor should have been called and generated InputType for Person
             assertThat(schema.getInputTypeByName("PersonInput"), is(notNullValue()));
         }
     }
@@ -75,6 +76,5 @@ public void processSchema(Schema schema) {
      */
     @SchemaProcessor
     public static class InvalidSchemaPreProcessor {
-
     }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
index 5a2cafa3a7a..e1b45fa59dd 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
@@ -30,11 +30,11 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation;
 import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs;
 import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle;
 import io.helidon.microprofile.graphql.server.test.types.Car;
-import io.helidon.microprofile.graphql.server.test.types.ContactRelationship;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
@@ -138,16 +138,13 @@ public void testSimpleQueryGenerationWithArgs() throws IOException {
         assertThat(mapResults.get("findPeopleFromState"), is(notNullValue()));
         ArrayList> arrayList = (ArrayList>) mapResults.get("findPeopleFromState");
         assertThat(arrayList, is(notNullValue()));
-        // since its random data we can't be sure if anyone was created in MA
-        assertThat(arrayList.size() >= 0, is(true));
 
         mapResults = getAndAssertResult(executionContext.execute(
                 "query { findPeopleFromState(state: \"MA\") { personId creditLimit workAddress { city state zipCode } } }"));
         assertThat(mapResults.size(), is(1));
 
-        SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50);
-        SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 53);
-        ContactRelationship relationship = new ContactRelationship(contact1, contact2, "married");
+        SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50, EnumTestWithEnumName.L);
+        SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 53, EnumTestWithEnumName.S);
 
         String json = "relationship: {"
                 + "   contact1: " + getContactAsQueryInput(contact1)
@@ -159,6 +156,12 @@ public void testSimpleQueryGenerationWithArgs() throws IOException {
                                                                          ") }"));
         assertThat(mapResults.size(), is(1));
         assertThat(mapResults.get("canFindContactRelationship"), is(false));
+
+        // validate that the name for the enum for SimpleContact is correct
+        Schema schema = executionContext.getSchema();
+        SchemaType type = schema.getTypeByName("SimpleContact");
+        assertThat(type, is(notNullValue()));
+        assertThat(type.getFieldDefinitionByName("tShirtSize"), is(notNullValue()));
     }
 
     @Test
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
index 1d158ad93dc..41b130a2cc8 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
@@ -24,6 +24,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 
@@ -62,7 +63,7 @@ public void testSimpleQueriesWithSource() throws IOException {
         assertThat(mapResults2.get("idAndName"), is(notNullValue()));
 
         // test the query at the top level
-        SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50);
+        SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50, EnumTestWithEnumName.XL);
 
         String json = "contact: " + getContactAsQueryInput(contact1);
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java
index 14aaee0a654..a5874783e1c 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/db/TestDB.java
@@ -33,6 +33,7 @@
 import java.util.Random;
 import java.util.UUID;
 
+import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.types.DateTimePojo;
 import io.helidon.microprofile.graphql.server.test.types.NullPOJO;
 import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty;
@@ -135,11 +136,12 @@ public MultiLevelListsAndArrays getMultiLevelListsAndArrays() {
     }
 
     public SimpleContact createRandomContact() {
-        return new SimpleContact(UUID.randomUUID().toString(), "Name-" + RANDOM.nextInt(10000), RANDOM.nextInt(100) + 1);
+        return new SimpleContact(UUID.randomUUID().toString(), "Name-" + RANDOM.nextInt(10000),
+                                 RANDOM.nextInt(100) + 1, EnumTestWithEnumName.XL);
     }
 
     public SimpleContact createContactWithName(String name) {
-        return new SimpleContact(UUID.randomUUID().toString(), name, RANDOM.nextInt(100) + 1);
+        return new SimpleContact(UUID.randomUUID().toString(), name, RANDOM.nextInt(100) + 1, EnumTestWithEnumName.XL);
     }
 
     /**
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java
index f251d4b866d..bfc2470af2e 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java
@@ -103,7 +103,7 @@ public String idNumber(@Name("name") String name,
     }
 
     @Mutation
-    public List echoBigDecimalList(@Name("coordinates") LinkedList coordinates) {
+    public List echoBigDecimalList(@Name("coordinates") List coordinates) {
         return coordinates;
     }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
index b3abf7fbc5d..7494a12527e 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
@@ -223,5 +223,10 @@ public LocalDateTime testDefaultFormatLocalDateTime(@Name("dateTime") LocalDateT
     public LocalTime echoLocalTime(@Name("time") LocalTime localTime) {
         return localTime;
     }
-
+    
+    @Query
+    @JsonbDateFormat("dd/MM")
+    public List echoFormattedLocalDateWithReturnFormat(@Name("value") List<@DateFormat("dd-MM-yyyy") LocalDate> value) {
+        return value;
+    }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java
index b665097eb2d..ce9b959dccd 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java
@@ -30,6 +30,7 @@
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
+import javax.json.bind.annotation.JsonbDateFormat;
 import javax.json.bind.annotation.JsonbNumberFormat;
 import javax.json.bind.annotation.JsonbProperty;
 
@@ -38,6 +39,7 @@
 import io.helidon.microprofile.graphql.server.test.types.ContactRelationship;
 import io.helidon.microprofile.graphql.server.test.types.Person;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+import org.eclipse.microprofile.graphql.DateFormat;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Id;
 import org.eclipse.microprofile.graphql.Name;
@@ -374,4 +376,9 @@ public SimpleContact[] echoSimpleContactArray(@Name("value") SimpleContact[] val
     public List echoFormattedListOfIntegers(@Name("value") List<@NumberFormat("0 'years old'") Integer> value) {
         return value;
     }
+
+    @Query
+    public List echoFormattedLocalDate(@Name("value") List<@DateFormat("dd-MM-yyyy") LocalDate> value) {
+        return value;
+    }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java
index 0f067b4b760..2f62d81c63d 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java
@@ -16,6 +16,8 @@
 
 package io.helidon.microprofile.graphql.server.test.types;
 
+import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
+import javax.json.bind.annotation.JsonbProperty;
 import java.util.Objects;
 
 /**
@@ -25,11 +27,13 @@ public class SimpleContact implements Comparable {
     private String id;
     private String name;
     private int age;
+    private EnumTestWithEnumName tShirtSize;
 
-    public SimpleContact(String id, String name, int age) {
+    public SimpleContact(String id, String name, int age, EnumTestWithEnumName tShirtSize) {
         this.id = id;
         this.name = name;
         this.age = age;
+        this.tShirtSize = tShirtSize;
     }
 
     public SimpleContact() {
@@ -59,6 +63,16 @@ public void setAge(int age) {
         this.age = age;
     }
 
+    @JsonbProperty("tShirtSize")
+    public EnumTestWithEnumName getTShirtSize() {
+        return tShirtSize;
+    }
+
+    @JsonbProperty("tShirtSize")
+    public void setTShirtSize(EnumTestWithEnumName tShirtSize) {
+        this.tShirtSize = tShirtSize;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -67,15 +81,16 @@ public boolean equals(Object o) {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        SimpleContact that = (SimpleContact) o;
-        return age == that.age
-                && Objects.equals(id, that.id)
-                && Objects.equals(name, that.name);
+        SimpleContact contact = (SimpleContact) o;
+        return age == contact.age &&
+                Objects.equals(id, contact.id) &&
+                Objects.equals(name, contact.name) &&
+                tShirtSize == contact.tShirtSize;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(id, name, age);
+        return Objects.hash(id, name, age, tShirtSize);
     }
 
     @Override

From 8e7c968a6b6aa4f1076402abcec083b20b7552cc Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Thu, 24 Sep 2020 14:22:15 +0800
Subject: [PATCH 099/178] progress

---
 .../graphql/server/DataFetcherUtils.java      |  3 ++-
 .../graphql/server/SchemaGenerator.java       |  3 ++-
 .../graphql/server/DateTimeIT.java            | 18 ++++++++++++++++++
 .../graphql/server/SimpleMutationsIT.java     | 12 ++++++++++++
 .../queries/SimpleQueriesAndMutations.java    | 19 ++++++++++++-------
 .../test/queries/SimpleQueriesWithArgs.java   |  2 +-
 6 files changed, 47 insertions(+), 10 deletions(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 82cebc54713..921f8e88026 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -177,7 +177,7 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
 
         } else if (rawValue instanceof Collection) {
             SchemaInputType inputType = schema.getInputTypeByName(argumentType);
-            Object colResults = null;
+            Object colResults;
             Class castClass;
             if (List.class.isAssignableFrom(originalType)) {
                 colResults = new ArrayList();
@@ -238,6 +238,7 @@ protected static Object parseArgumentValue(Class originalType, String argumen
                     if (isDateTimeClass(originalType)) {
                         DateTimeFormatter dateFormatter = getCorrectDateFormatter(originalType.getName(),
                                                                                   format[1], format[0]);
+                        LOGGER.info("Parsing [" + rawValue + "] with " + dateFormatter + " " + format[1] + ":" + format[0]);
                         return getOriginalDateTimeValue(originalType, dateFormatter.parse(rawValue.toString()));
                     } else {
                         NumberFormat numberFormat = getCorrectNumberFormat(originalType.getName(),
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index c095214a3b6..be342e33f40 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -22,7 +22,6 @@
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Parameter;
@@ -191,6 +190,8 @@ public Schema generateSchema() throws IntrospectionException, ClassNotFoundExcep
         Class[] classes = extension.collectedApis();
         Class[] schemaProcessors = extension.collectedSchemaProcessors();
 
+        CDI current = CDI.current();
+
         Schema schema = generateSchemaFromClasses(classes);
 
         for (Class schemaProcessor : schemaProcessors) {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
index 20da1da1e8c..f270532e69a 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
@@ -21,6 +21,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATETIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR;
@@ -161,6 +162,23 @@ public void testDateAndTime() throws IOException {
         assertThat(listDates.size(), is(2));
         assertThat(listDates.get(0), is("23/09"));
         assertThat(listDates.get(1), is("22/09"));
+
+        SchemaType typeMutation = schema.getTypeByName("Mutation");
+        fd = getFieldDefinition(typeMutation, "echoFormattedDateWithJsonB");
+        assertThat(fd, is(notNullValue()));
+        Optional argument = fd.getArguments()
+                .stream().filter(a -> a.getArgumentName().equals("dates")).findFirst();
+        assertThat(argument.isPresent(), is(true));
+        SchemaArgument a = argument.get();
+        assertThat(a.getFormat()[0], is("MM/dd/yyyy"));
+//        assertThat(a.getArgumentType(), is(FORMATTED_DATE_SCALAR));
+        mapResults = getAndAssertResult(
+        executionContext.execute("mutation { echoFormattedDateWithJsonB(dates: [ \"09/22/2020\", \"09/23/2020\" ]) }"));
+        assertThat(mapResults, is(notNullValue()));
+        listDates = (ArrayList) mapResults.get("echoFormattedDateWithJsonB");
+        assertThat(listDates.size(), is(2));
+        assertThat(listDates.get(0), is("22/09/2020"));
+        assertThat(listDates.get(1), is("23/09/2020"));
     }
 
     @Test
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
index a24429e15a8..770900e8fc7 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
@@ -24,6 +24,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
@@ -74,5 +75,16 @@ public void testSimpleMutations() throws IOException {
                 "mutation { testStringArrays(places: [\"place1\", \"place2\", \"place3\"]) }"));
         assertThat(mapResults.size(), is(1));
         assertThat(mapResults.get("testStringArrays"), is("place1place2place3"));
+
+       mapResults = getAndAssertResult(
+                executionContext.execute("mutation { createAndReturnNewContact(newContact: { name: \"tim\", age: 22, id: \"1\", tShirtSize: XL } ) { id name age tShirtSize } }"));
+        assertThat(mapResults.size(), is(1));
+        mapResults2 = (Map) mapResults.get("createAndReturnNewContact");
+        assertThat(mapResults2, is(notNullValue()));
+        assertThat(mapResults2.get("name"), is("tim"));
+        assertThat(mapResults2.get("age"), is(22));
+        assertThat(mapResults2.get("id"), is("1"));
+        assertThat(mapResults2.get("tShirtSize"), is("XL"));
+
     }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
index 7494a12527e..12b98d04c7e 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
@@ -20,15 +20,12 @@
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
-import java.time.OffsetDateTime;
-import java.time.OffsetTime;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Locale;
+
 import java.util.UUID;
 
 import javax.enterprise.context.ApplicationScoped;
@@ -40,12 +37,12 @@
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.types.DateTimePojo;
 import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays;
-import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods;
+
 import io.helidon.microprofile.graphql.server.test.types.Person;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf;
 import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs;
+
 import org.eclipse.microprofile.graphql.DateFormat;
-import org.eclipse.microprofile.graphql.Description;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Id;
 import org.eclipse.microprofile.graphql.Mutation;
@@ -229,4 +226,12 @@ public LocalTime echoLocalTime(@Name("time") LocalTime localTime) {
     public List echoFormattedLocalDateWithReturnFormat(@Name("value") List<@DateFormat("dd-MM-yyyy") LocalDate> value) {
         return value;
     }
+
+    @Mutation
+    @DateFormat("dd/MM/yyyy")
+    public List echoFormattedDateWithJsonB(@Name("dates")
+                                                      @JsonbDateFormat("yy dd MM") // should be ignored
+                                                      List<@DateFormat("MM/dd/yyyy") LocalDate> localDates) {
+        return localDates;
+    }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java
index ce9b959dccd..d02e47a3007 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java
@@ -30,7 +30,7 @@
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
-import javax.json.bind.annotation.JsonbDateFormat;
+
 import javax.json.bind.annotation.JsonbNumberFormat;
 import javax.json.bind.annotation.JsonbProperty;
 

From fa4614fb51c89feba01bedb217dc6f30ac4caee1 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Fri, 25 Sep 2020 14:35:17 +0800
Subject: [PATCH 100/178] remove relience on jandex for annotation discovery

---
 .../graphql/server/FormattingHelper.java      | 198 +++++++++---------
 .../graphql/server/SchemaGenerator.java       |  51 ++---
 .../graphql/server/SchemaGeneratorHelper.java |  95 ++++++++-
 .../graphql/server/NumberFormatIT.java        |   4 +-
 .../graphql/server/SchemaGeneratorTest.java   | 154 +++++++++++++-
 5 files changed, 369 insertions(+), 133 deletions(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
index f768106d5b8..87b6cfb6a15 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
@@ -16,7 +16,11 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
 import java.text.DecimalFormat;
 import java.text.NumberFormat;
 import java.time.format.DateTimeFormatter;
@@ -30,7 +34,7 @@
 import javax.json.bind.annotation.JsonbNumberFormat;
 
 import org.eclipse.microprofile.graphql.DateFormat;
- 
+
 import org.jboss.jandex.AnnotationInstance;
 import org.jboss.jandex.AnnotationValue;
 
@@ -59,6 +63,10 @@
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.SUPPORTED_SCALARS;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ZONED_DATE_TIME_CLASS;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getAnnotationValue;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldAnnotations;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getMethodAnnotations;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getParameterAnnotations;
 
 /**
  * Helper class for number formatting.
@@ -88,7 +96,7 @@ public class FormattingHelper {
     /**
      * Indicates no formatting applied.
      */
-    protected static final String[] NO_FORMATTING = new String[] {null, null, null };
+    protected static final String[] NO_FORMATTING = new String[] { null, null, null };
 
     /**
      * JsonbDateFormat class name.
@@ -109,7 +117,7 @@ public class FormattingHelper {
      * NumberFormat class name.
      */
     private static final String NUMBER_FORMAT = org.eclipse.microprofile.graphql.NumberFormat.class.getName();
-    
+
     /**
      * No-args constructor.
      */
@@ -126,7 +134,7 @@ private FormattingHelper() {
     protected static String[] getDefaultDateTimeFormat(String scalarName, String clazzName) {
         for (SchemaScalar scalar : SUPPORTED_SCALARS.values()) {
             if (scalarName.equals(scalar.getName()) && scalar.getActualClass().equals(clazzName)) {
-                return new String[] {scalar.getDefaultFormat(), DEFAULT_LOCALE };
+                return new String[] { scalar.getDefaultFormat(), DEFAULT_LOCALE };
             }
         }
         return NO_DEFAULT_FORMAT;
@@ -174,7 +182,7 @@ protected static NumberFormat getCorrectNumberFormat(String type, String locale,
     }
 
     /**
-     * Returna {@link DateTimeFormatter} for the given type, locale and format.
+     * Return a {@link DateTimeFormatter} for the given type, locale and format.
      *
      * @param type   the GraphQL type or scalar
      * @param locale the locale, either "" or the correct locale
@@ -242,110 +250,110 @@ protected static String[] getFormattingAnnotation(AnnotatedElement annotatedElem
         }
 
         return dateFormat.length == 2
-                ? new String[] {DATE, dateFormat[0], dateFormat[1] }
-                : new String[] {NUMBER, numberFormat[0], numberFormat[1] };
+                ? new String[] { DATE, dateFormat[0], dateFormat[1] }
+                : new String[] { NUMBER, numberFormat[0], numberFormat[1] };
     }
 
+
     /**
-     * Return any formatting on a field/
+     * Return the field format with the given index.
      *
-     * @param jandexUtils     {@link JandexUtils} to use
-     * @param clazz           {@link Class} to check for annotation
-     * @param fieldName       field name to check
-     * @return a String[] representing the format and locale
+     * @param field {@link Field} to introspect
+     * @param index index of generic type. 0 = List/Collection, 1 = Map
+     * @return the field format with the given index
      */
-    protected static String[] getFieldFormat(JandexUtils jandexUtils, String clazz,
-                                                 String fieldName) {
-         if (jandexUtils.hasIndex()) {
-             AnnotationInstance dateFormat1 = jandexUtils.getFieldAnnotation(clazz, fieldName, JSONB_DATE_FORMAT);
-             AnnotationInstance dateFormat2 = jandexUtils.getFieldAnnotation(clazz, fieldName, DATE_FORMAT);
-             AnnotationInstance numberFormat1 = jandexUtils.getFieldAnnotation(clazz, fieldName, JSONB_NUMBER_FORMAT);
-             AnnotationInstance numberFormat2 = jandexUtils.getFieldAnnotation(clazz, fieldName, NUMBER_FORMAT);
-             return getFormatFromAnnotationInstance(dateFormat1, dateFormat2, numberFormat1, numberFormat2);
-         }
-         return NO_FORMATTING;
+    protected static String[] getFieldFormat(Field field, int index) {
+        Annotation[] annotations = getFieldAnnotations(field, index);
+        if (annotations == null || annotations.length == 0) {
+            // try to get standard parameter annotation.
+            annotations = field.getAnnotatedType().getAnnotations();
+        }
+
+        return getFormatFromAnnotations(annotations);
     }
 
     /**
-     * Return the method parameter format using Jandex.
+     * Return the method format with the given index.
      *
-     * @param jandexUtils     {@link JandexUtils} to use
-     * @param clazz           {@link Class} to check for annotation
-     * @param methodName      method name to check
-     * @param paramNumber     parameter number to check
-     * @return a String[] representing the format and locale
+     * @param method {@link Method} to introspect
+     * @param index     index of generic type. 0 = List/Collection, 1 = Map
+     * @return the method format with the given index
      */
-    protected static String[] getMethodParameterFormat(JandexUtils jandexUtils, String clazz, String methodName,
-                                                       int paramNumber) {
-
-        if (jandexUtils.hasIndex()) {
-            AnnotationInstance dateFormat1 =
-                    jandexUtils.getMethodParameterAnnotation(clazz, methodName, paramNumber, JSONB_DATE_FORMAT);
-            AnnotationInstance dateFormat2 =
-                    jandexUtils.getMethodParameterAnnotation(clazz, methodName, paramNumber, DATE_FORMAT);
-            AnnotationInstance numberFormat1 =
-                    jandexUtils.getMethodParameterAnnotation(clazz, methodName, paramNumber, JSONB_NUMBER_FORMAT);
-            AnnotationInstance numberFormat2 =
-                    jandexUtils.getMethodParameterAnnotation(clazz, methodName, paramNumber, NUMBER_FORMAT);
-
-            return getFormatFromAnnotationInstance(dateFormat1, dateFormat2, numberFormat1, numberFormat2);
+    protected static String[] getMethodFormat(Method method, int index) {
+        Annotation[] annotations = getMethodAnnotations(method, index);
+        if (annotations == null || annotations.length == 0) {
+            // try to get standard parameter annotation.
+            annotations = method.getAnnotatedReturnType().getAnnotations();
         }
-        return NO_FORMATTING;
+        return getFormatFromAnnotations(annotations);
     }
 
     /**
-     * Return a format given a {@link AnnotationInstance}.
-     * @param dateFormat1    {@link AnnotationInstance} date format from {@link JsonbDateFormat}
-     * @param dateFormat2    {@link AnnotationInstance} date format from {@link DateFormat}
-     * @param numberFormat1  {@link AnnotationInstance} number format from {@link JsonbNumberFormat}
-     * @param numberFormat2  {@link AnnotationInstance} number format from {@link org.eclipse.microprofile.graphql.NumberFormat}
-     * @return a String[] representing the format and locale
+     * Return the method parameter format with the given index.
+     *
+     * @param parameter {@link Parameter} to introspect
+     * @param index     index of generic type. 0 = List/Collection, 1 = Map
+     * @return the method parameter format with the given index
      */
-    private static String[] getFormatFromAnnotationInstance(AnnotationInstance dateFormat1,
-                                                            AnnotationInstance dateFormat2,
-                                                            AnnotationInstance numberFormat1,
-                                                            AnnotationInstance numberFormat2) {
-        // ensure that both date and number formatting are not present
-        if ((dateFormat1 != null || dateFormat2 != null)
-            && (numberFormat1 != null || numberFormat2 != null)) {
-            ensureRuntimeException(LOGGER, "Cannot have date and number formatting on the same method");
+    protected static String[] getMethodParameterFormat(Parameter parameter, int index) {
+        Annotation[] annotations = getParameterAnnotations(parameter, index);
+        if (annotations == null || annotations.length == 0) {
+            // try to get standard parameter annotation.
+            annotations = parameter.getAnnotatedType().getAnnotations();
         }
-        AnnotationInstance formatInstance = dateFormat1 != null ? dateFormat1
-                : dateFormat2 != null ? dateFormat2
-                        : numberFormat1 != null ? numberFormat1
-                                : numberFormat2;
-        if (formatInstance == null) {
-            return NO_FORMATTING;
-        }
-
-        String type = dateFormat1 != null || dateFormat2 != null ? DATE
-                : NUMBER;
 
-        AnnotationValue format = formatInstance.value("value");
-        AnnotationValue locale = formatInstance.value("locale");
-        return new String[] {type, format.asString(), locale == null ? DEFAULT_LOCALE : locale.asString() };
+        return getFormatFromAnnotations(annotations);
     }
 
     /**
-     * Return the method format using Jandex.
+     * Return the formatting from the given annotations.
      *
-     * @param jandexUtils     {@link JandexUtils} to use
-     * @param clazz           {@link Class} to check for annotation
-     * @param methodName      method name to check
-     * @return
+     * @param annotations {@link Annotation}s to retrieve formatting from
+     * @return the formatting from the given annotations
      */
-    protected static String[] getMethodFormat(JandexUtils jandexUtils, String clazz, String methodName) {
-        if (jandexUtils.hasIndex()) {
-            AnnotationInstance dateFormat1 = jandexUtils.getMethodAnnotation(clazz, methodName, JSONB_DATE_FORMAT);
-            AnnotationInstance dateFormat2 = jandexUtils.getMethodAnnotation(clazz, methodName, DATE_FORMAT);
-            AnnotationInstance numberFormat1 = jandexUtils.getMethodAnnotation(clazz, methodName, JSONB_NUMBER_FORMAT);
-            AnnotationInstance numberFormat2 = jandexUtils.getMethodAnnotation(clazz, methodName, NUMBER_FORMAT);
-
-            return getFormatFromAnnotationInstance(dateFormat1, dateFormat2, numberFormat1, numberFormat2);
+    protected static String[] getFormatFromAnnotations(Annotation[] annotations) {
+        if (annotations != null && annotations.length > 0) {
+            JsonbDateFormat dateFormat1 = (JsonbDateFormat) getAnnotationValue(annotations, JsonbDateFormat.class);
+            DateFormat dateFormat2 = (DateFormat) getAnnotationValue(annotations, DateFormat.class);
+            JsonbNumberFormat numberFormat1 = (JsonbNumberFormat) getAnnotationValue(annotations, JsonbNumberFormat.class);
+            org.eclipse.microprofile.graphql.NumberFormat numberFormat2 =
+                    (org.eclipse.microprofile.graphql.NumberFormat)
+                            getAnnotationValue(annotations, org.eclipse.microprofile.graphql.NumberFormat.class);
+
+            // ensure that both date and number formatting are not present
+            if ((dateFormat1 != null || dateFormat2 != null)
+                    && (numberFormat1 != null || numberFormat2 != null)) {
+                ensureRuntimeException(LOGGER, "Cannot have date and number formatting on the same method");
+            }
+            String format = null;
+            String locale = null;
+            if (dateFormat1 != null) {
+                format = dateFormat1.value();
+                locale = dateFormat1.locale();
+            } else if (dateFormat2 != null) {
+                format = dateFormat2.value();
+                locale = dateFormat2.locale();
+            } else if (numberFormat1 != null) {
+                format = numberFormat1.value();
+                locale = numberFormat1.locale();
+            } else if (numberFormat2 != null) {
+                format = numberFormat2.value();
+                locale = numberFormat2.locale();
+            }
+
+            if (format == null) {
+                return NO_FORMATTING;
+            }
+
+            String type = dateFormat1 != null || dateFormat2 != null ? DATE
+                    : NUMBER;
+
+            return new String[] { type, format, locale.equals("") ? DEFAULT_LOCALE : locale };
+
         }
         return NO_FORMATTING;
     }
-
+    
     /**
      * Indicates if either {@link JsonbNumberFormat} or {@link JsonbDateFormat} are present.
      *
@@ -380,10 +388,10 @@ private static String[] getNumberFormatAnnotationInternal(JsonbNumberFormat json
                                                               org.eclipse.microprofile.graphql.NumberFormat numberFormat) {
         // check @NumberFormat first as this takes precedence
         if (numberFormat != null) {
-            return new String[] {numberFormat.value(), numberFormat.locale() };
+            return new String[] { numberFormat.value(), numberFormat.locale() };
         }
         if (jsonbNumberFormat != null) {
-            return new String[] {jsonbNumberFormat.value(), jsonbNumberFormat.locale() };
+            return new String[] { jsonbNumberFormat.value(), jsonbNumberFormat.locale() };
         }
         return new String[0];
     }
@@ -411,21 +419,22 @@ private static String[] getDateFormatAnnotationInternal(JsonbDateFormat jsonbDat
                                                             DateFormat dateFormat) {
         // check @DateFormat first as this takes precedence
         if (dateFormat != null) {
-            return new String[] {dateFormat.value(), dateFormat.locale() };
+            return new String[] { dateFormat.value(), dateFormat.locale() };
         }
         if (jsonbDateFormat != null) {
-            return new String[] {jsonbDateFormat.value(), jsonbDateFormat.locale() };
+            return new String[] { jsonbDateFormat.value(), jsonbDateFormat.locale() };
         }
         return new String[0];
     }
 
     /**
      * Format a date value given a {@link DateTimeFormatter}.
+     *
      * @param originalResult    original result
      * @param dateTimeFormatter {@link DateTimeFormatter} to format with
      * @return formatted value
      */
-    @SuppressWarnings({"unchecked", "rawtypes"})
+    @SuppressWarnings( { "unchecked", "rawtypes" })
     public static Object formatDate(Object originalResult, DateTimeFormatter dateTimeFormatter) {
         if (originalResult == null) {
             return null;
@@ -434,7 +443,7 @@ public static Object formatDate(Object originalResult, DateTimeFormatter dateTim
             Collection formattedResult = new ArrayList();
             Collection originalCollection = (Collection) originalResult;
             originalCollection.forEach(e -> formattedResult.add(e instanceof TemporalAccessor ? dateTimeFormatter
-                                                       .format((TemporalAccessor) e) : e)
+                    .format((TemporalAccessor) e) : e)
             );
             return formattedResult;
         } else {
@@ -445,12 +454,13 @@ public static Object formatDate(Object originalResult, DateTimeFormatter dateTim
 
     /**
      * Format a number value with a a given {@link NumberFormat}.
-     * @param originalResult  original result
-     * @param isScalar        indicates if it is a scalar value
-     * @param numberFormat    {@link NumberFormat} to format with
+     *
+     * @param originalResult original result
+     * @param isScalar       indicates if it is a scalar value
+     * @param numberFormat   {@link NumberFormat} to format with
      * @return formatted value
      */
-    @SuppressWarnings({"unchecked", "rawtypes"})
+    @SuppressWarnings( { "unchecked", "rawtypes" })
     public static Object formatNumber(Object originalResult, boolean isScalar, NumberFormat numberFormat) {
         if (originalResult == null) {
             return null;
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index be342e33f40..323d04eccaa 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -20,6 +20,9 @@
 import java.beans.Introspector;
 import java.beans.MethodDescriptor;
 import java.beans.PropertyDescriptor;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.AnnotatedType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -71,9 +74,7 @@
 import static io.helidon.microprofile.graphql.server.FormattingHelper.formatNumber;
 import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectDateFormatter;
 import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectNumberFormat;
-import static io.helidon.microprofile.graphql.server.FormattingHelper.getFieldFormat;
 import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormattingAnnotation;
-import static io.helidon.microprofile.graphql.server.FormattingHelper.getMethodParameterFormat;
 import static io.helidon.microprofile.graphql.server.FormattingHelper.isJsonbAnnotationPresent;
 import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.MUTATION_TYPE;
 import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.QUERY_TYPE;
@@ -89,12 +90,14 @@
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureFormat;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureValidName;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getAnnotationValue;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getArrayLevels;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDefaultValueAnnotationValue;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDescription;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldName;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getGraphQLType;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getMethodName;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getParameterAnnotations;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootArrayClass;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getSafeClass;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getScalar;
@@ -190,8 +193,6 @@ public Schema generateSchema() throws IntrospectionException, ClassNotFoundExcep
         Class[] classes = extension.collectedApis();
         Class[] schemaProcessors = extension.collectedSchemaProcessors();
 
-        CDI current = CDI.current();
-
         Schema schema = generateSchemaFromClasses(classes);
 
         for (Class schemaProcessor : schemaProcessors) {
@@ -352,7 +353,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes)
      *
      * @param schema {@link Schema} to update
      */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @SuppressWarnings( { "unchecked", "rawtypes" })
     private void processDefaultDateTimeValues(Schema schema) {
         // concatenate both the SchemaType and SchemaInputType
         Stream streamInputTypes = schema.getInputTypes().stream().map(it -> (SchemaType) it);
@@ -821,7 +822,6 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth
         fd.setArrayReturnTypeMandatory(discoveredMethod.isArrayReturnTypeMandatory());
         fd.setOriginalArrayType(isArrayReturnType ? discoveredMethod.getOriginalArrayType() : null);
 
-
         if (format != null && format.length == 3) {
             fd.setFormat(new String[] { format[1], format[2] });
         }
@@ -1093,8 +1093,8 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
             if (field != null && (format.length == 0 || format[0] == null)) {
                 format = getFormattingAnnotation(field);
                 if (isFormatEmpty(format)) {
-                    // check to see format of the inner most class. E.g. List<@DateFomrat("DD/MM") String>
-                    format = getFieldFormat(jandexUtils, className, field.getName());
+                    // check to see format of the inner most class. E.g. List<@DateFormat("DD/MM") String>
+                    format = FormattingHelper.getFieldFormat(field, 0);
                 }
                 isJsonbFormat = isJsonbAnnotationPresent(field);
             }
@@ -1200,10 +1200,9 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM
                 String argumentDescription = getDescription(parameter.getAnnotation(Description.class));
                 argument.setDescription(argumentDescription);
 
-                String[] argumentFormat = FormattingHelper.getFormattingAnnotation(parameter);
-                String[] argumentTypeFormat = getMethodParameterFormat(jandexUtils,
-                                                              method.getDeclaringClass().getName(), method.getName(), i);
-
+                String[] argumentFormat = getFormattingAnnotation(parameter);
+                String[] argumentTypeFormat = FormattingHelper.getMethodParameterFormat(parameter, 0);
+                
                 // The argument type format overrides any argument format. E.g. NumberFormat should apply below
                 // E.g.  public List getListAsString(@Name("arg1")
                 //                                           @JsonbNumberFormat("ignore 00.0000000")
@@ -1274,10 +1273,11 @@ private static void validateIDClass(String returnClazz) {
      * @param returnClazz       return type
      * @param genericReturnType generic return {@link java.lang.reflect.Type} may be null
      * @param parameterNumber   the parameter number for the parameter
+     * @param method            {@link Method} to find parameter for
      * @return a {@link ReturnType}
      */
-    private ReturnType getReturnType(Class returnClazz, java.lang.reflect.Type genericReturnType,
-                                     int parameterNumber, Method method) {
+    protected ReturnType getReturnType(Class returnClazz, java.lang.reflect.Type genericReturnType,
+                                       int parameterNumber, Method method) {
         ReturnType actualReturnType = new ReturnType();
         RootTypeResult rootTypeResult;
         String returnClazzName = returnClazz.getName();
@@ -1333,7 +1333,6 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp
                                              int parameterNumber, Method method) {
         int level = 1;
         boolean isParameter = parameterNumber != -1;
-        String nonNullClazz = NonNull.class.getName();
         String[] format = NO_FORMATTING;
 
         boolean isReturnTypeMandatory;
@@ -1349,19 +1348,12 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp
 
             Class clazz = actualTypeArgument.getClass();
             boolean hasAnnotation = false;
-            AnnotationInstance formatInstance = null;
-            String clazzName = method.getDeclaringClass().getName();
-            String methodName = method.getName();
-            if (jandexUtils.hasIndex()) {
-                if (isParameter) {
-                    hasAnnotation = jandexUtils
-                            .methodParameterHasAnnotation(clazzName, methodName, parameterNumber, nonNullClazz);
-                    format = getMethodParameterFormat(jandexUtils, clazzName, methodName,
-                                                                       parameterNumber);
-                } else {
-                    hasAnnotation = jandexUtils.methodHasAnnotation(clazzName, methodName, nonNullClazz);
-                    format = FormattingHelper.getMethodFormat(jandexUtils, clazzName, methodName);
-                }
+            if (isParameter) {
+                // check for the NonNull
+                Parameter parameter = method.getParameters()[parameterNumber];
+                hasAnnotation = getAnnotationValue(getParameterAnnotations(parameter, 0), NonNull.class) != null;
+            } else {
+                format = FormattingHelper.getMethodFormat(method, 0);
             }
 
             isReturnTypeMandatory = hasAnnotation || isPrimitive(clazz.getName());
@@ -1370,7 +1362,6 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp
             Class clazz = genericReturnType.getClass();
             isReturnTypeMandatory = clazz.getAnnotation(NonNull.class) != null
                     || isPrimitive(clazz.getName());
-            //            format = FormattingHelper.getMethodFormat(jandexUtils, clazzName, methodName);
             return new RootTypeResult(((Class) genericReturnType).getName(), level, isReturnTypeMandatory, format);
         }
     }
@@ -1838,6 +1829,7 @@ public Class getOriginalArrayType() {
 
         /**
          * Set if the format is of type JsonB.
+         *
          * @param isJsonbFormat if the format is of type JsonB
          */
         public void setJsonbFormat(boolean isJsonbFormat) {
@@ -1846,6 +1838,7 @@ public void setJsonbFormat(boolean isJsonbFormat) {
 
         /**
          * Returns true if the format is of type JsonB.
+         *
          * @return true if the format is of type JsonB
          */
         public boolean isJsonbFormat() {
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
index c22df1f5755..b018c9fed93 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
@@ -16,11 +16,15 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.AnnotatedType;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Parameter;
+import java.lang.reflect.ParameterizedType;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.time.LocalDate;
@@ -30,6 +34,7 @@
 import java.time.OffsetTime;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -248,6 +253,11 @@ public final class SchemaGeneratorHelper {
      */
     private static final Logger LOGGER = Logger.getLogger(SchemaGeneratorHelper.class.getName());
 
+    /**
+     * Indicates empty annotations.
+     */
+    private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
+
     /**
      * List of supported scalars keyed by the full class name.
      */
@@ -380,7 +390,7 @@ protected static boolean isPrimitive(Class clazz) {
      */
     protected static boolean isPrimitive(String clazz) {
         return JAVA_PRIMITIVE_TYPES.contains(clazz)
-                || PRIMITIVE_ARRAY_MAP.values().stream().filter(v -> v.contains(clazz)).count() > 0L;
+                || PRIMITIVE_ARRAY_MAP.values().stream().anyMatch(v -> v.contains(clazz));
     }
 
     /**
@@ -690,6 +700,89 @@ protected static String getTypeName(Class clazz) {
         return getTypeName(clazz, false);
     }
 
+    /**
+     * Return the array of {@link Annotation}s on a {@link Parameter} that are parameterized types.
+     * @param field  {@link Field} to introspect
+     * @param index  index of type generic type. 0 = List/Collection 1 = Map
+     * @return the array of {@link Annotation}s on a {@link Parameter}
+     */
+    protected static Annotation[] getFieldAnnotations(Field field, int index) {
+        if (field.getAnnotatedType() instanceof AnnotatedParameterizedType) {
+            return getAnnotationsFromType((AnnotatedParameterizedType) field.getAnnotatedType(), index);
+        }
+
+        return EMPTY_ANNOTATIONS;
+    }
+
+    /**
+     * Return the array of {@link Annotation}s on a {@link Method} that are parameterized types.
+     * @param method  {@link Method} to introspect
+     * @param index  index of type generic type. 0 = List/Collection 1 = Map
+     * @return the array of {@link Annotation}s on a {@link Parameter}
+     */
+    protected static Annotation[] getMethodAnnotations(Method method, int index) {
+        if (method.getAnnotatedReturnType() instanceof AnnotatedParameterizedType) {
+            return getAnnotationsFromType((AnnotatedParameterizedType) method.getAnnotatedReturnType(), index);
+        }
+
+        return EMPTY_ANNOTATIONS;
+    }
+
+    /**
+     * Return the array of {@link Annotation}s on a {@link Parameter} that are parameterized types.
+     * @param parameter   {@link Parameter} to introspect
+     * @param index       index of type generic type. 0 = List/Collection 1 = Map
+     * @return the array of {@link Annotation}s on a {@link Parameter}
+     */
+    protected static Annotation[] getParameterAnnotations(Parameter parameter, int index) {
+
+        if (parameter.getAnnotatedType() instanceof AnnotatedParameterizedType) {
+            return getAnnotationsFromType((AnnotatedParameterizedType) parameter.getAnnotatedType(), index);
+        }
+
+        return EMPTY_ANNOTATIONS;
+    }
+
+    /**
+     * Returns the annotations from the given {@link AnnotatedParameterizedType}.
+     * @param apt   {@link AnnotatedParameterizedType}
+     * @param index  index of type generic type. 0 = List/Collection 1 = Map
+     * @return  the annotations from the given {@link AnnotatedParameterizedType}
+     */
+    private static Annotation[] getAnnotationsFromType(AnnotatedParameterizedType apt, int index) {
+        if (apt != null) {
+
+            // loop until we find the root annotated type
+            AnnotatedType annotatedActualTypeArgument = apt.getAnnotatedActualTypeArguments()[index];
+            while (annotatedActualTypeArgument instanceof AnnotatedParameterizedType) {
+                AnnotatedParameterizedType parameterizedType2 = (AnnotatedParameterizedType) annotatedActualTypeArgument;
+                annotatedActualTypeArgument = parameterizedType2.getAnnotatedActualTypeArguments()[index];
+            }
+
+            if (annotatedActualTypeArgument != null) {
+                return annotatedActualTypeArgument.getAnnotations();
+            }
+        }
+        return EMPTY_ANNOTATIONS;
+    }
+
+    /**
+     * Return the annotation that matches the type.
+     * @param annotations array of {@link Annotation}s to search
+     * @param type the {@link Type} to find
+     * @return the annotation that matches the type
+     */
+    protected static Annotation getAnnotationValue(Annotation[] annotations, java.lang.reflect.Type type) {
+        if (annotations != null) {
+            for (Annotation annotation : annotations) {
+                if (annotation.annotationType().equals(type)) {
+                    return annotation;
+                }
+            }
+        }
+        return null;
+    }
+
     /**
      * Return correct name for a Type or Enum based upon the value of the annotation or the {@link Name}.
      *
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
index 0dbaa89a892..660945ee3b2 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
@@ -99,8 +99,7 @@ public void testNumberFormats() throws IOException {
         mapResults = getAndAssertResult(executionContext.execute("mutation { idNumber(name: \"Tim\", id: 123) }"));
         assertThat(mapResults, is(notNullValue()));
         assertThat(mapResults.get("idNumber"), is("Tim-123"));
-
-        // TODO: Fix
+        
         mapResults = getAndAssertResult(executionContext.execute("mutation { echoBigDecimalList(coordinates: [ 10.0123, -23.000 ]) }"));
         assertThat(mapResults, is(notNullValue()));
         List listBigDecimals = (List) mapResults.get("echoBigDecimalList");
@@ -140,6 +139,7 @@ public void testNumberFormats() throws IOException {
 
         List listResults = (List) mapResults.get("echoFormattedListOfIntegers");
         assertThat(listResults.size(), is(3));
+
    }
 
     @Test
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java
index e43cb2684e8..3df45297685 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java
@@ -17,8 +17,10 @@
 package io.helidon.microprofile.graphql.server;
 
 import java.beans.IntrospectionException;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
 import java.lang.reflect.ParameterizedType;
 
 import java.math.BigDecimal;
@@ -59,16 +61,28 @@
 
 import io.helidon.microprofile.graphql.server.test.types.Vehicle;
 import io.helidon.microprofile.graphql.server.test.types.VehicleIncident;
+
+import javax.json.bind.annotation.JsonbNumberFormat;
+import org.eclipse.microprofile.graphql.DateFormat;
+import org.eclipse.microprofile.graphql.Name;
+import org.eclipse.microprofile.graphql.NonNull;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import static io.helidon.microprofile.graphql.server.FormattingHelper.DATE;
+import static io.helidon.microprofile.graphql.server.FormattingHelper.NUMBER;
+import static io.helidon.microprofile.graphql.server.FormattingHelper.getFieldFormat;
+
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_DECIMAL;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_INTEGER;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DEFAULT_LOCALE;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FLOAT;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT;
 import static io.helidon.microprofile.graphql.server.FormattingHelper.getNumberFormatAnnotation;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getAnnotationValue;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDefaultDescription;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldAnnotations;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getParameterAnnotations;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.stripMethodName;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
@@ -90,8 +104,15 @@ public class SchemaGeneratorTest extends AbstractGraphQLTest {
 
     private List listStringArray = new ArrayList<>();
     private List listString = new ArrayList<>();
+    private List<@org.eclipse.microprofile.graphql.NumberFormat("0 'number'") Integer> listIntegerFormatted = new ArrayList<>();
+    private List<@DateFormat("dd/mm/yyyy") LocalDate> listDateFormatted = new ArrayList<>();
+    private List<@org.eclipse.microprofile.graphql.NumberFormat(value = "0 'number'", locale = "en-AU") Integer>
+            listIntegerFormattedWithLocale = new ArrayList<>();
     private List>> listListString = new ArrayList<>();
 
+    @org.eclipse.microprofile.graphql.NumberFormat("0 'number'")
+    private List listIntegerFormat2;
+
     public List getListStringArray() {
         return listStringArray;
     }
@@ -104,6 +125,44 @@ public List>> getListListString() {
         return listListString;
     }
 
+    @org.eclipse.microprofile.graphql.NumberFormat("0 'method'")
+    public List getListIntegerWithFormat(
+            @Name("name") List<@org.eclipse.microprofile.graphql.NumberFormat("0 'number'") @NonNull Integer> value) {
+        return value;
+    }
+
+    public List> getListListIntegerWithFormat(
+            @Name("name") List> value) {
+        return value;
+    }
+
+    @JsonbNumberFormat("0 'jsonb'")
+    @org.eclipse.microprofile.graphql.NumberFormat("0 'number'")
+    public List> getListListIntegerWith2Formats(
+            @Name("name") List> value) {
+        return value;
+    }
+
+    public List> getListListLocalDateWithFormat(
+            @Name("name") List> value) {
+        return value;
+    }
+
+    public List> getListListLocalDateWithFormatAndLocale(
+            @Name("name") List> value) {
+        return value;
+    }
+
+    public Collection> getCollectionListIntegerWithFormat(
+            @Name("name") Collection> value) {
+        return value;
+    }
+
+    @org.eclipse.microprofile.graphql.NumberFormat("0 'number'")
+    public List getListIntegerFormatted2(List value) {
+        return value;
+    }
+
     private SchemaGenerator schemaGenerator;
     private Context defaultContext;
 
@@ -113,6 +172,59 @@ public void beforeEach() {
         schemaGenerator = new SchemaGenerator(defaultContext);
     }
 
+    @Test
+    public void testFieldFormats() throws NoSuchFieldException {
+       Field field = SchemaGeneratorTest.class.getDeclaredField("listString");
+       assertThat(field, is(notNullValue()));
+       Annotation[] annotations = getFieldAnnotations(field, 0);
+       assertThat(annotations, is(notNullValue()));
+
+       assertFieldFormat(SchemaGeneratorTest.class.getDeclaredField("listString"),
+                         new String[] {null, null, null});
+       assertFieldFormat(SchemaGeneratorTest.class.getDeclaredField("listIntegerFormatted"),
+                         new String[] {NUMBER, "0 'number'", DEFAULT_LOCALE});
+       assertFieldFormat(SchemaGeneratorTest.class.getDeclaredField("listIntegerFormattedWithLocale"),
+                         new String[] {NUMBER, "0 'number'", "en-AU"});
+       assertFieldFormat(SchemaGeneratorTest.class.getDeclaredField("listDateFormatted"),
+                         new String[] {DATE, "dd/mm/yyyy", DEFAULT_LOCALE});
+       assertFieldFormat(SchemaGeneratorTest.class.getDeclaredField("listIntegerFormat2"),
+                         new String[] {NUMBER, "0 'number'", DEFAULT_LOCALE});
+    }
+
+    @Test
+    public void testMethodFormats() throws NoSuchMethodException {
+       assertMethodFormat(SchemaGeneratorTest.class.getMethod("getListIntegerWithFormat", List.class),
+                          new String[] {NUMBER, "0 'method'", DEFAULT_LOCALE});
+       assertMethodFormat(SchemaGeneratorTest.class.getMethod("getListListIntegerWithFormat", List.class),
+                          new String[] {NUMBER, "0 'number'", DEFAULT_LOCALE});
+       assertMethodFormat(SchemaGeneratorTest.class.getMethod("getListListIntegerWith2Formats", List.class),
+                          new String[] {NUMBER, "0 'number'", DEFAULT_LOCALE});
+    }
+
+    @Test
+    public void testParameterFormatsAndNulls() throws NoSuchMethodException {
+        Method method = SchemaGeneratorTest.class.getMethod("getListIntegerWithFormat", List.class);
+        assertThat(method, is(notNullValue()));
+        Parameter parameter = method.getParameters()[0];
+
+        Annotation[] parameterAnnotations = getParameterAnnotations(parameter, 0);
+        assertThat(parameterAnnotations, is(notNullValue()));
+        assertThat(parameterAnnotations.length, is(2));
+        assertThat(getAnnotationValue(parameterAnnotations, NonNull.class), is(notNullValue()));
+
+        assertParameterFormat(SchemaGeneratorTest.class.getMethod("getListIntegerWithFormat", List.class), 0,
+                              new String[] { NUMBER, "0 'number'", DEFAULT_LOCALE });
+        assertParameterFormat(SchemaGeneratorTest.class.getMethod("getListListIntegerWithFormat", List.class), 0,
+                              new String[] { NUMBER, "0 'number'", DEFAULT_LOCALE });
+        assertParameterFormat(SchemaGeneratorTest.class.getMethod("getCollectionListIntegerWithFormat", Collection.class), 0,
+                              new String[] { NUMBER, "0 'number'", DEFAULT_LOCALE });
+        assertParameterFormat(SchemaGeneratorTest.class.getMethod("getListListLocalDateWithFormat", List.class), 0,
+                              new String[] { DATE, "dd/mm/yyyy", DEFAULT_LOCALE });
+        assertParameterFormat(SchemaGeneratorTest.class.getMethod("getListListLocalDateWithFormatAndLocale", List.class), 0,
+                              new String[] { DATE, "dd/mm/yyyy", "en-AU" });
+
+    }
+
     @Test
     public void testEnumGeneration() throws IntrospectionException, ClassNotFoundException {
         testEnum(EnumTestNoEnumName.class, EnumTestNoEnumName.class.getSimpleName());
@@ -188,7 +300,8 @@ public void testTypeWithNameAndJsonbProperty() throws IntrospectionException, Cl
 
     @Test
     public void testInterfaceDiscovery() throws IntrospectionException, ClassNotFoundException {
-        Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Vehicle.class, false);
+        Map mapMethods = schemaGenerator
+                .retrieveGetterBeanMethods(Vehicle.class, false);
         assertThat(mapMethods, is(notNullValue()));
         assertThat(mapMethods.size(), is(6));
         assertDiscoveredMethod(mapMethods.get("plate"), "plate", STRING, null, false, false, false);
@@ -217,7 +330,8 @@ public void testInterfaceImplementorDiscovery1() throws IntrospectionException,
 
     @Test
     public void testInterfaceImplementorDiscovery2() throws IntrospectionException, ClassNotFoundException {
-        Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Motorbike.class, false);
+        Map mapMethods = schemaGenerator
+                .retrieveGetterBeanMethods(Motorbike.class, false);
         assertThat(mapMethods, is(notNullValue()));
         assertThat(mapMethods.size(), is(7));
         assertDiscoveredMethod(mapMethods.get("plate"), "plate", STRING, null, false, false, false);
@@ -322,9 +436,9 @@ public void testStripMethodName() throws NoSuchMethodException {
     @Test
     public void testDefaultDescription() {
         assertThat(getDefaultDescription(null, null), is(nullValue()));
-        assertThat(getDefaultDescription(new String[] {"format","locale"} , null), is("format locale"));
+        assertThat(getDefaultDescription(new String[] { "format", "locale" }, null), is("format locale"));
         assertThat(getDefaultDescription(null, "desc"), is("desc"));
-        assertThat(getDefaultDescription(new String[] {"format","locale"}, "desc"), is("desc (format locale)"));
+        assertThat(getDefaultDescription(new String[] { "format", "locale" }, "desc"), is("desc (format locale)"));
     }
 
     @Test
@@ -389,6 +503,7 @@ public void testArrayLevelsAndRootArray() {
     @Test
     public void testGetRootType() throws NoSuchFieldException, NoSuchMethodException {
         SchemaGenerator schemaGenerator = new SchemaGenerator(ExecutionContext.getDefaultContext());
+
         ParameterizedType stringArrayListType = getParameterizedType("listStringArray");
         SchemaGenerator.RootTypeResult rootTypeName =
                 schemaGenerator.getRootTypeName(stringArrayListType.getActualTypeArguments()[0], 0,
@@ -400,15 +515,15 @@ public void testGetRootType() throws NoSuchFieldException, NoSuchMethodException
         ParameterizedType stringListType = getParameterizedType("listString");
         rootTypeName = schemaGenerator.getRootTypeName(stringListType.getActualTypeArguments()[0], 0,
                                                        -1,
-                                                        SchemaGeneratorTest.class.getMethod("getListStringArray"));
-        assertThat(rootTypeName.getRootTypeName(), is(String.class.getName()));
+                                                       SchemaGeneratorTest.class.getMethod("getListStringArray"));
+        assertThat(rootTypeName.getRootTypeName(), is(STRING));
         assertThat(rootTypeName.getLevels(), is(1));
 
         ParameterizedType listListStringType = getParameterizedType("listListString");
         rootTypeName = schemaGenerator.getRootTypeName(listListStringType.getActualTypeArguments()[0], 0,
                                                        -1,
                                                        SchemaGeneratorTest.class.getMethod("getListListString"));
-        assertThat(rootTypeName.getRootTypeName(), is(String.class.getName()));
+        assertThat(rootTypeName.getRootTypeName(), is(STRING));
         assertThat(rootTypeName.getLevels(), is(2));
     }
 
@@ -465,6 +580,31 @@ public void testFormatAnnotationFromSchema() throws IntrospectionException, Clas
         assertThat(schema, is(notNullValue()));
     }
 
+    private void assertParameterFormat(Method method, int paramNumber, String[] expectedFormat) {
+        assertThat(method, is(notNullValue()));
+        Parameter parameter = method.getParameters()[paramNumber];
+        String[] format = FormattingHelper.getMethodParameterFormat(parameter, 0);
+        assertThat(format, is(notNullValue()));
+        assertThat(format.length, is(3));
+        assertThat(format, is(expectedFormat));
+    }
+
+    private void assertFieldFormat(Field field, String[] expectedFormat) {
+        assertThat(field, is(notNullValue()));
+        String[] format = getFieldFormat(field, 0);
+        assertThat(format, is(notNullValue()));
+        assertThat(format.length, is(3));
+        assertThat(format, is(expectedFormat));
+    }
+
+    private void assertMethodFormat(Method method, String[] expectedFormat) {
+        assertThat(method, is(notNullValue()));
+        String[] format = FormattingHelper.getMethodFormat(method, 0);
+        assertThat(format, is(notNullValue()));
+        assertThat(format.length, is(3));
+        assertThat(format, is(expectedFormat));
+    }
+
     /**
      * Assert that a {@link org.eclipse.microprofile.graphql.NumberFormat} or {@link javax.json.bind.annotation.JsonbNumberFormat}
      * annotation is correctly applied.

From 07523da1bbf9009de2ae15932687be441697fe16 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Fri, 25 Sep 2020 15:21:47 +0800
Subject: [PATCH 101/178] Remove unwanted Jandex bits

---
 .../graphql/server/JandexUtilsIT.java         | 55 -------------------
 1 file changed, 55 deletions(-)
 delete mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java

diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java
deleted file mode 100644
index 8e94a313b83..00000000000
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsIT.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2020 Oracle and/or its affiliates.
- *
- * 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 io.helidon.microprofile.graphql.server;
-
-import java.io.IOException;
-
-import io.helidon.microprofile.graphql.server.test.queries.QueriesAndMutationsWithNulls;
-import io.helidon.microprofile.graphql.server.test.types.NullPOJO;
-
-import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats;
-import org.eclipse.microprofile.graphql.NonNull;
-
-import org.junit.jupiter.api.Test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-/**
- * Integration tests for {@link SchemaGeneratorTest}.
- */
-public class JandexUtilsIT extends AbstractGraphQLIT {
-
-    @Test
-    public void testJandexUtils() throws IOException {
-        setupIndex(indexFileName, NullPOJO.class, QueriesAndMutationsWithNulls.class);
-        SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext);
-        String queriesWithNulls = QueriesAndMutationsWithNulls.class.getName();
-        String nullPOJO = NullPOJO.class.getName();
-        String noNull = NonNull.class.getName();
-
-        JandexUtils jandexUtils = schemaGenerator.getJandexUtils();
-
-        assertThat(jandexUtils.methodParameterHasAnnotation(queriesWithNulls, "query1", 0, noNull), is(false));
-
-        assertThat(jandexUtils.fieldHasAnnotation(nullPOJO, "listNonNullStrings", noNull), is(true));
-        assertThat(jandexUtils.fieldHasAnnotation(nullPOJO, "listOfListOfNonNullStrings", noNull), is(true));
-        assertThat(jandexUtils.fieldHasAnnotation(nullPOJO, "listOfListOfNullStrings", noNull), is(false));
-
-        assertThat(jandexUtils.methodHasAnnotation(nullPOJO, "getListOfListOfNonNullStrings", noNull), is(false));
-    }
-}

From 1b044c1a2e24042afaf6801dabbaf8c1a7fd1b8b Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Fri, 25 Sep 2020 15:37:39 +0800
Subject: [PATCH 102/178] Checkstyle fixes

---
 .../microprofile/graphql/server/Context.java  |  29 ----
 .../graphql/server/CustomScalars.java         |   3 -
 .../graphql/server/DataFetcherUtils.java      |   5 +-
 .../graphql/server/DefaultContext.java        |  41 -----
 .../graphql/server/FormattingHelper.java      |  28 ++--
 .../graphql/server/JandexUtils.java           | 152 ------------------
 .../microprofile/graphql/server/Pair.java     |  55 -------
 .../graphql/server/SchemaArgument.java        |   2 +-
 .../graphql/server/SchemaFieldDefinition.java |   2 +-
 .../graphql/server/SchemaGenerator.java       |  36 ++---
 .../graphql/server/SchemaGeneratorHelper.java |  12 +-
 11 files changed, 39 insertions(+), 326 deletions(-)
 delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Pair.java

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java
index cbb63dcec4e..9c6b1ac873f 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java
@@ -16,38 +16,9 @@
 
 package io.helidon.microprofile.graphql.server;
 
-import java.lang.reflect.Method;
 
 /**
  * A Default Context to be supplied to {@link ExecutionContext}.
  */
 public interface Context {
-
-    /**
-     * Return the formatter for the given method and argument.
-     *
-     * @param method       {@link Method} method
-     * @param argumentName argument name
-     * @return the formatter for the given method and argument
-     */
-    public Object getFormatter(Method method, String argumentName);
-
-    /**
-     * Add a formatter for the given method and argument.
-     * @param method       {@link Method} method
-     * @param argumentName argument name
-     * @param formatter    formatter
-     */
-    public void addFormatter(Method method, String argumentName, FormattingProvider formatter);
-
-    /**
-     * Generate a {@link Pair} from the given method and argument.
-     * @param method       {@link Method} method
-     * @param argumentName argument name
-     * @return a new {@link Pair}
-     */
-    private Pair generatePair(Method method, String argumentName) {
-        return new Pair<>(method.getClass() + "_" + method.getName(), argumentName);
-    }
-
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
index 7fb27b5549e..8d2466195f3 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
@@ -18,8 +18,6 @@
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.text.NumberFormat;
-import java.text.ParseException;
 import java.time.OffsetDateTime;
 import java.time.OffsetTime;
 
@@ -35,7 +33,6 @@
 import static graphql.Scalars.GraphQLBigInteger;
 import static graphql.Scalars.GraphQLFloat;
 import static graphql.Scalars.GraphQLInt;
-
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATETIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATETIME_SCALAR;
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 921f8e88026..42e3af05ff7 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -22,7 +22,6 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.text.NumberFormat;
-
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
@@ -69,7 +68,7 @@ public class DataFetcherUtils {
     /**
      * Empty format.
      */
-    private static final String[] EMPTY_FORMAT = new String[] { null, null };
+    private static final String[] EMPTY_FORMAT = new String[] {null, null };
 
     /**
      * Private constructor for utilities class.
@@ -172,7 +171,7 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
                     }
                 }
             }
-            
+
             return JsonUtils.convertFromJson(JsonUtils.convertMapToJson(mapConverted), originalType);
 
         } else if (rawValue instanceof Collection) {
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java
index 1f2e6ca1490..8bcdffb3473 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java
@@ -16,51 +16,10 @@
 
 package io.helidon.microprofile.graphql.server;
 
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * A default implementation of {@link Context} to be supplied to {@link ExecutionContext}.
  * Any other implementations should extend this.
  */
 public class DefaultContext
         implements Context {
-
-    /**
-     * Defines a {@link Map} keyed by a {@link Pair} of full method name and argument name with value the formatter.
-     */
-    private Map, FormattingProvider> mapFormatting = new HashMap<>();
-
-    /**
-     * No-args constructor.
-     */
-    public DefaultContext() {
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Object getFormatter(Method method, String argumentName) {
-        return mapFormatting.get(generatePair(method, argumentName));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void addFormatter(Method method, String argumentName, FormattingProvider formatter) {
-        mapFormatting.put(generatePair(method, argumentName), formatter);
-    }
-
-    /**
-     * Generate a {@link Pair} from the given method and argument.
-     *
-     * @param method       {@link Method} method
-     * @param argumentName argument name
-     * @return a new {@link Pair}
-     */
-    private Pair generatePair(Method method, String argumentName) {
-        return new Pair<>(method.getDeclaringClass() + "_" + method.getName(), argumentName);
-    }
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
index 87b6cfb6a15..6dda2fce91b 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
@@ -35,9 +35,6 @@
 
 import org.eclipse.microprofile.graphql.DateFormat;
 
-import org.jboss.jandex.AnnotationInstance;
-import org.jboss.jandex.AnnotationValue;
-
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_DECIMAL;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_DECIMAL_CLASS;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.BIG_INTEGER;
@@ -96,7 +93,7 @@ public class FormattingHelper {
     /**
      * Indicates no formatting applied.
      */
-    protected static final String[] NO_FORMATTING = new String[] { null, null, null };
+    protected static final String[] NO_FORMATTING = new String[] {null, null, null };
 
     /**
      * JsonbDateFormat class name.
@@ -134,7 +131,7 @@ private FormattingHelper() {
     protected static String[] getDefaultDateTimeFormat(String scalarName, String clazzName) {
         for (SchemaScalar scalar : SUPPORTED_SCALARS.values()) {
             if (scalarName.equals(scalar.getName()) && scalar.getActualClass().equals(clazzName)) {
-                return new String[] { scalar.getDefaultFormat(), DEFAULT_LOCALE };
+                return new String[] {scalar.getDefaultFormat(), DEFAULT_LOCALE };
             }
         }
         return NO_DEFAULT_FORMAT;
@@ -250,11 +247,10 @@ protected static String[] getFormattingAnnotation(AnnotatedElement annotatedElem
         }
 
         return dateFormat.length == 2
-                ? new String[] { DATE, dateFormat[0], dateFormat[1] }
-                : new String[] { NUMBER, numberFormat[0], numberFormat[1] };
+                ? new String[] {DATE, dateFormat[0], dateFormat[1] }
+                : new String[] {NUMBER, numberFormat[0], numberFormat[1] };
     }
 
-
     /**
      * Return the field format with the given index.
      *
@@ -348,12 +344,12 @@ protected static String[] getFormatFromAnnotations(Annotation[] annotations) {
             String type = dateFormat1 != null || dateFormat2 != null ? DATE
                     : NUMBER;
 
-            return new String[] { type, format, locale.equals("") ? DEFAULT_LOCALE : locale };
+            return new String[] {type, format, locale.equals("") ? DEFAULT_LOCALE : locale };
 
         }
         return NO_FORMATTING;
     }
-    
+
     /**
      * Indicates if either {@link JsonbNumberFormat} or {@link JsonbDateFormat} are present.
      *
@@ -388,10 +384,10 @@ private static String[] getNumberFormatAnnotationInternal(JsonbNumberFormat json
                                                               org.eclipse.microprofile.graphql.NumberFormat numberFormat) {
         // check @NumberFormat first as this takes precedence
         if (numberFormat != null) {
-            return new String[] { numberFormat.value(), numberFormat.locale() };
+            return new String[] {numberFormat.value(), numberFormat.locale() };
         }
         if (jsonbNumberFormat != null) {
-            return new String[] { jsonbNumberFormat.value(), jsonbNumberFormat.locale() };
+            return new String[] {jsonbNumberFormat.value(), jsonbNumberFormat.locale() };
         }
         return new String[0];
     }
@@ -419,10 +415,10 @@ private static String[] getDateFormatAnnotationInternal(JsonbDateFormat jsonbDat
                                                             DateFormat dateFormat) {
         // check @DateFormat first as this takes precedence
         if (dateFormat != null) {
-            return new String[] { dateFormat.value(), dateFormat.locale() };
+            return new String[] {dateFormat.value(), dateFormat.locale() };
         }
         if (jsonbDateFormat != null) {
-            return new String[] { jsonbDateFormat.value(), jsonbDateFormat.locale() };
+            return new String[] {jsonbDateFormat.value(), jsonbDateFormat.locale() };
         }
         return new String[0];
     }
@@ -434,7 +430,7 @@ private static String[] getDateFormatAnnotationInternal(JsonbDateFormat jsonbDat
      * @param dateTimeFormatter {@link DateTimeFormatter} to format with
      * @return formatted value
      */
-    @SuppressWarnings( { "unchecked", "rawtypes" })
+    @SuppressWarnings({"unchecked", "rawtypes" })
     public static Object formatDate(Object originalResult, DateTimeFormatter dateTimeFormatter) {
         if (originalResult == null) {
             return null;
@@ -460,7 +456,7 @@ public static Object formatDate(Object originalResult, DateTimeFormatter dateTim
      * @param numberFormat   {@link NumberFormat} to format with
      * @return formatted value
      */
-    @SuppressWarnings( { "unchecked", "rawtypes" })
+    @SuppressWarnings({"unchecked", "rawtypes" })
     public static Object formatNumber(Object originalResult, boolean isScalar, NumberFormat numberFormat) {
         if (originalResult == null) {
             return null;
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java
index 6de32f408b7..e988a61670f 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java
@@ -22,22 +22,13 @@
 import java.net.URL;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
 import java.util.Set;
 import java.util.logging.Logger;
-import java.util.stream.Collectors;
 
-import org.jboss.jandex.AnnotationInstance;
 import org.jboss.jandex.ClassInfo;
-import org.jboss.jandex.ClassType;
 import org.jboss.jandex.DotName;
-import org.jboss.jandex.FieldInfo;
 import org.jboss.jandex.Index;
 import org.jboss.jandex.IndexReader;
-import org.jboss.jandex.MethodInfo;
-import org.jboss.jandex.ParameterizedType;
-import org.jboss.jandex.Type;
 
 /**
  * Utilities for working with Jandex indexes.
@@ -125,149 +116,6 @@ public Collection> getKnownImplementors(String clazz, boolean includeAb
         return setResults;
     }
 
-    /**
-     * Return true if the given class, method and parameter has the specified annotation class name for a generic type.
-     *
-     * @param clazz           {@link Class} to check for annotation
-     * @param methodName      method name to check
-     * @param paramNumber     parameter number to check
-     * @param annotationClazz the annotation {@link Class} to check
-     * @return true if the given class, method and parameter has the specified annotation class name
-     */
-    protected boolean methodParameterHasAnnotation(String clazz, String methodName, int paramNumber, String annotationClazz) {
-        return getMethodParameterAnnotation(clazz, methodName, paramNumber, annotationClazz) != null;
-    }
-
-    /**
-     * Return the {@link AnnotationInstance} for the given class, method and parameter has the specified annotation class name for a generic type.
-     *
-     * @param clazz           {@link Class} to check for annotation
-     * @param methodName      method name to check
-     * @param paramNumber     parameter number to check
-     * @param annotationClazz the annotation {@link Class} to check
-     * @return {@link AnnotationInstance} or null if none present
-     */
-    protected AnnotationInstance getMethodParameterAnnotation(String clazz, String methodName,
-                                                              int paramNumber, String annotationClazz) {
-        if (hasIndex()) {
-            ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz));
-            if (classByName != null) {
-                MethodInfo methodInfo = null;
-                for (MethodInfo info : classByName.methods()) {
-                    if (info.name().equals(methodName) && info.parameters().size() >= paramNumber + 1) {
-                        methodInfo = info;
-                        break;
-                    }
-                }
-                if (methodInfo != null) {
-                    ClassType classType = retrieveInnerMostType(methodInfo.parameters().get(paramNumber));
-                    return classType == null ? null
-                            : classType.annotations().stream()
-                                    .filter(a -> a.name().equals(DotName.createSimple(annotationClazz)))
-                                    .findFirst().orElse(null);
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Return true if the given field in a class has the specified annotation class name for a generic type.
-     *
-     * @param clazz           {@link Class} to check for annotation
-     * @param fieldName       field name to check
-     * @param annotationClazz the annotation {@link Class} to check
-     * @return true if the given class, method and parameter has the specified annotation class name
-     */
-    protected boolean fieldHasAnnotation(String clazz, String fieldName, String annotationClazz) {
-        return getFieldAnnotation(clazz, fieldName, annotationClazz) != null;
-    }
-
-    /**
-     * Return the {@link AnnotationInstance} for the given field in a class has the specified annotation class name for a generic type.
-     *
-     * @param clazz           {@link Class} to check for annotation
-     * @param fieldName       field name to check
-     * @param annotationClazz the annotation {@link Class} to check
-     * @return {@link AnnotationInstance} or null if none present
-     */
-    protected AnnotationInstance getFieldAnnotation(String clazz, String fieldName, String annotationClazz) {
-        if (hasIndex()) {
-            ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz));
-            if (classByName != null) {
-                FieldInfo field = classByName.field(fieldName);
-                if (field != null) {
-                    ClassType classType = retrieveInnerMostType(field.type());
-                    return classType == null ? null
-                            : classType.annotations().stream()
-                                .filter(a -> a.name().equals(DotName.createSimple(annotationClazz)))
-                                .findFirst().orElse(null);
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Return true if the given method in a class has the specified annotation class name or a generic.
-     *
-     * @param clazz           {@link Class} to check for annotation
-     * @param methodName      method name to check
-     * @param annotationClazz the annotation {@link Class} to check
-     * @return true if the given class, method and parameter has the specified annotation class name
-     */
-    protected boolean methodHasAnnotation(String clazz, String methodName, String annotationClazz) {
-        return getMethodAnnotation(clazz, methodName, annotationClazz) != null;
-    }
-
-    /**
-     * Return the {@link AnnotationInstance} for the given method in a class has the specified annotation class name or a generic.
-     *
-     * @param clazz           {@link Class} to check for annotation
-     * @param methodName      method name to check
-     * @param annotationClazz the annotation {@link Class} to check
-     * @return {@link AnnotationInstance} or null if none present
-     */
-    protected AnnotationInstance getMethodAnnotation(String clazz, String methodName, String annotationClazz) {
-        if (hasIndex()) {
-            ClassInfo classByName = index.getClassByName(DotName.createSimple(clazz));
-            if (classByName != null) {
-                MethodInfo method = classByName.firstMethod(methodName);
-                if (method != null) {
-                    ClassType classType = retrieveInnerMostType(method.returnType());
-                        if (classType == null) {
-                            return null;
-                        }
-                        return classType.annotations().stream()
-                                .filter(a -> a.name().equals(DotName.createSimple(annotationClazz)))
-                                .findFirst().orElse(null);
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Retrieve the inner most type for a generic type.
-     *
-     * @param initialType the {@link Type} to add
-     * @return a {@link ClassType} representing the inner most type for a generic type
-     */
-    private ClassType retrieveInnerMostType(Type initialType) {
-        Type type = initialType;
-        while (type instanceof ParameterizedType) {
-            ParameterizedType pType = (ParameterizedType) type;
-            List arguments = pType.arguments();
-            int argumentSize = arguments.size();
-            Type newType = arguments.size() > 0 ? arguments.get(argumentSize - 1) : null;
-            if (newType instanceof ClassType) {
-                return (ClassType) newType;
-            }
-            type = newType;
-        }
-        return null;
-    }
-
     /**
      * Return a {@link Collection} of {@link Class}es which are implementors of a given class/interface and are not abstract.
      *
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Pair.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Pair.java
deleted file mode 100644
index a7398c87043..00000000000
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Pair.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates.
- *
- * 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 io.helidon.microprofile.graphql.server;
-
-import java.util.Objects;
-
-public class Pair {
-    private final A valueA;
-    private final B valueB;
-
-    public Pair(A valueA, B valueB) {
-        this.valueA = valueA;
-        this.valueB = valueB;
-    }
-
-    public A getValueA() {
-        return valueA;
-    }
-
-    public B getValueB() {
-        return valueB;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        Pair pair = (Pair) o;
-        return Objects.equals(valueA, pair.valueA) &&
-                Objects.equals(valueB, pair.valueB);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(valueA, valueB);
-    }
-}
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java
index 6350c803144..5f71c892850 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java
@@ -286,7 +286,7 @@ public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) {
      *
      * @param originalArrayType the original array type
      */
-    public void setOriginalArrayType(Class  originalArrayType) {
+    public void setOriginalArrayType(Class originalArrayType) {
         this.originalArrayType = originalArrayType;
     }
 
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java
index ae12ea84fab..95a258f96a7 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java
@@ -309,7 +309,7 @@ public Class getOriginalType() {
      *
      * @param originalArrayType the original array type
      */
-    public void setOriginalArrayType(Class  originalArrayType) {
+    public void setOriginalArrayType(Class originalArrayType) {
         this.originalArrayType = originalArrayType;
     }
 
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index 323d04eccaa..cc563a3f984 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -20,9 +20,6 @@
 import java.beans.Introspector;
 import java.beans.MethodDescriptor;
 import java.beans.PropertyDescriptor;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.AnnotatedParameterizedType;
-import java.lang.reflect.AnnotatedType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -45,11 +42,11 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import graphql.schema.GraphQLScalarType;
 import javax.enterprise.inject.spi.CDI;
 
 import graphql.schema.DataFetcher;
 import graphql.schema.DataFetcherFactories;
+import graphql.schema.GraphQLScalarType;
 import graphql.schema.PropertyDataFetcher;
 import org.eclipse.microprofile.graphql.Description;
 import org.eclipse.microprofile.graphql.GraphQLApi;
@@ -62,7 +59,6 @@
 import org.eclipse.microprofile.graphql.Query;
 import org.eclipse.microprofile.graphql.Source;
 import org.eclipse.microprofile.graphql.Type;
-import org.jboss.jandex.AnnotationInstance;
 
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_TIME_SCALAR;
@@ -94,8 +90,10 @@
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getArrayLevels;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDefaultValueAnnotationValue;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getDescription;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldAnnotations;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getFieldName;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getGraphQLType;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getMethodAnnotations;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getMethodName;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getParameterAnnotations;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getRootArrayClass;
@@ -353,7 +351,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes)
      *
      * @param schema {@link Schema} to update
      */
-    @SuppressWarnings( { "unchecked", "rawtypes" })
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     private void processDefaultDateTimeValues(Schema schema) {
         // concatenate both the SchemaType and SchemaInputType
         Stream streamInputTypes = schema.getInputTypes().stream().map(it -> (SchemaType) it);
@@ -375,7 +373,7 @@ private void processDefaultDateTimeValues(Schema schema) {
                         if (fd.getDataFetcher() == null) {
                             // create the raw array to pass to the retrieveFormattingDataFetcher method
                             DataFetcher dataFetcher = retrieveFormattingDataFetcher(
-                                    new String[] { DATE, newFormat[0], newFormat[1] },
+                                    new String[] {DATE, newFormat[0], newFormat[1] },
                                     fd.getName(), clazzOriginalType.getName());
                             fd.setDataFetcher(dataFetcher);
                         }
@@ -590,7 +588,7 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType,
                     String[] newFormat = ensureFormat(dateScalar.getName(),
                                                       originalType.getName(), new String[2]);
                     if (newFormat.length == 2) {
-                        format = new String[] { DATE, newFormat[0], newFormat[1] };
+                        format = new String[] {DATE, newFormat[0], newFormat[1] };
                     }
                 }
                 if (!isFormatEmpty(format)) {
@@ -599,7 +597,7 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType,
                     final DataFetcher methodDataFetcher = DataFetcherUtils.newMethodDataFetcher(schema, clazz, method, null,
                                                                                                 fd.getArguments().toArray(
                                                                                                         new SchemaArgument[0]));
-                    final String[] newFormat = new String[] { format[0], format[1], format[2] };
+                    final String[] newFormat = new String[] {format[0], format[1], format[2] };
 
                     if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) {
                         dataFetcher = DataFetcherFactories.wrapDataFetcher(methodDataFetcher,
@@ -823,7 +821,7 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth
         fd.setOriginalArrayType(isArrayReturnType ? discoveredMethod.getOriginalArrayType() : null);
 
         if (format != null && format.length == 3) {
-            fd.setFormat(new String[] { format[1], format[2] });
+            fd.setFormat(new String[] {format[1], format[2] });
         }
 
         fd.setDescription(discoveredMethod.getDescription());
@@ -990,7 +988,6 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
         boolean isArrayReturnTypeMandatory = false;
         boolean isJsonbFormat = false;
         String defaultValue = null;
-        String className = clazz.getName();
 
         // retrieve the method name
         String varName = stripMethodName(method, !isQueryOrMutation);
@@ -1029,7 +1026,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
                 // default values only make sense for input types
                 defaultValue = isInputType ? getDefaultValueAnnotationValue(field) : null;
                 NonNull nonNullAnnotation = field.getAnnotation(NonNull.class);
-                isArrayReturnTypeMandatory = jandexUtils.fieldHasAnnotation(className, field.getName(), NON_NULL_CLASS);
+                isArrayReturnTypeMandatory = getAnnotationValue(getFieldAnnotations(field, 0), NonNull.class) != null;
 
                 if (isInputType) {
                     Method writeMethod = pd.getWriteMethod();
@@ -1051,8 +1048,9 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
                         }
 
                         // the annotation on the set method parameter will override for the input type if it's present
-                        boolean isSetArrayMandatory = jandexUtils.methodParameterHasAnnotation(className, writeMethod.getName(),
-                                                                                               0, NON_NULL_CLASS);
+                        boolean isSetArrayMandatory =
+                                getAnnotationValue(getParameterAnnotations(writeMethod.getParameters()[0], 0),
+                                                   NonNull.class) != null;
                         if (isSetArrayMandatory && !isArrayReturnTypeMandatory) {
                             isArrayReturnTypeMandatory = true;
                         }
@@ -1072,16 +1070,16 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
                         nonNullAnnotation = methodAnnotation;
                     }
                     if (!isArrayReturnTypeMandatory) {
-                        isArrayReturnTypeMandatory = jandexUtils.methodHasAnnotation(className, method.getName(), NON_NULL_CLASS);
+                        isArrayReturnTypeMandatory =
+                                getAnnotationValue(getMethodAnnotations(method, 0), NonNull.class) != null;
                     }
                 }
 
-                // if the return type is annotated as NotNull or it is a primitive then it is mandatory
+                // if the return type is annotated as NonNull or it is a primitive then it is mandatory
                 isReturnTypeMandatory = (isPrimitive(returnClazzName) && defaultValue == null)
                         || nonNullAnnotation != null && defaultValue == null;
 
             } catch (NoSuchFieldException e) {
-                // TODO: ?
             }
 
             if (fieldHasIdAnnotation || method.getAnnotation(Id.class) != null) {
@@ -1202,7 +1200,7 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM
 
                 String[] argumentFormat = getFormattingAnnotation(parameter);
                 String[] argumentTypeFormat = FormattingHelper.getMethodParameterFormat(parameter, 0);
-                
+
                 // The argument type format overrides any argument format. E.g. NumberFormat should apply below
                 // E.g.  public List getListAsString(@Name("arg1")
                 //                                           @JsonbNumberFormat("ignore 00.0000000")
@@ -1210,7 +1208,7 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM
                 argumentFormat = !isFormatEmpty(argumentTypeFormat) ? argumentTypeFormat : argumentFormat;
 
                 if (argumentFormat[0] != null) {
-                    argument.setFormat(new String[] { argumentFormat[1], argumentFormat[2] });
+                    argument.setFormat(new String[] {argumentFormat[1], argumentFormat[2] });
                     argument.setArgumentType(String.class.getName());
                 }
 
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
index b018c9fed93..6ef4dd87c41 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
@@ -24,7 +24,6 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Parameter;
-import java.lang.reflect.ParameterizedType;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.time.LocalDate;
@@ -34,7 +33,6 @@
 import java.time.OffsetTime;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -58,10 +56,10 @@
 
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_BIGDECIMAL_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_BIGINTEGER_SCALAR;
-import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_DATE_SCALAR;
-import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_DATE_TIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_FLOAT_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_INT_SCALAR;
+import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_DATE_SCALAR;
+import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_DATE_TIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_TIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.ElementGenerator.OPEN_SQUARE;
 import static io.helidon.microprofile.graphql.server.FormattingHelper.getDefaultDateTimeFormat;
@@ -279,10 +277,12 @@ public final class SchemaGeneratorHelper {
             new SchemaScalar(FORMATTED_DATETIME_SCALAR, ZONED_DATE_TIME_CLASS, FORMATTED_CUSTOM_DATE_TIME_SCALAR,
                              "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'"));
         put(LOCAL_DATE_TIME_CLASS,
-            new SchemaScalar(FORMATTED_DATETIME_SCALAR, LOCAL_DATE_TIME_CLASS, FORMATTED_CUSTOM_DATE_TIME_SCALAR, "yyyy-MM-dd'T'HH:mm:ss"));
+            new SchemaScalar(FORMATTED_DATETIME_SCALAR, LOCAL_DATE_TIME_CLASS, FORMATTED_CUSTOM_DATE_TIME_SCALAR,
+                             "yyyy-MM-dd'T'HH:mm:ss"));
 
         // Date scalar
-        put(LOCAL_DATE_CLASS, new SchemaScalar(FORMATTED_DATE_SCALAR, LOCAL_DATE_CLASS, FORMATTED_CUSTOM_DATE_SCALAR, "yyyy-MM-dd"));
+        put(LOCAL_DATE_CLASS, new SchemaScalar(FORMATTED_DATE_SCALAR, LOCAL_DATE_CLASS, FORMATTED_CUSTOM_DATE_SCALAR,
+                                               "yyyy-MM-dd"));
 
         // BigDecimal scalars
         put(BIG_DECIMAL_CLASS, new SchemaScalar(BIG_DECIMAL, BIG_DECIMAL_CLASS, CUSTOM_BIGDECIMAL_SCALAR, null));

From 890fe30f30f4fd5c738209ed8617e18d78d93027 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Tue, 29 Sep 2020 10:11:48 +0800
Subject: [PATCH 103/178] Fixup empheral port start

---
 .../graphql/server/AbstractGraphQLEndpointIT.java           | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
index d082502a1c8..b691cee7e00 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
@@ -23,6 +23,8 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import io.helidon.microprofile.server.ServerCdiExtension;
+import javax.enterprise.inject.spi.CDI;
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
 import javax.ws.rs.client.WebTarget;
@@ -86,7 +88,9 @@ public static void _startupTest(Class... clazzes) throws IOException {
 
         Main.main(new String[0]);
 
-        graphQLUrl= "http://127.0.0.1:7001/";
+        ServerCdiExtension current = CDI.current().getBeanManager().getExtension(ServerCdiExtension.class);
+
+        graphQLUrl= "http://127.0.0.1:" + current.port() + "/";
         
         System.out.println("GraphQL URL: " + graphQLUrl);
 

From 1fbb90bb78130686edd7bd110393b0bdb4c27e7e Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Wed, 30 Sep 2020 16:29:06 +0800
Subject: [PATCH 104/178] Fixup location array failure

---
 .../graphql/server/DataFetcherUtils.java      | 35 +++++++++++++------
 .../graphql/server/DataFetcherUtilsIT.java    | 32 +++++++++--------
 .../graphql/server/MultiLevelArraysIT.java    | 15 ++++++++
 .../test/queries/ArrayAndListQueries.java     | 13 +++++++
 4 files changed, 69 insertions(+), 26 deletions(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 42e3af05ff7..9def2bccb85 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -128,8 +128,8 @@ public static  DataFetcher newMethodDataFetcher(Schema schema, Class cl
      *
      * @param schema    {@link Schema} to introspect if needed
      * @param argumentType the type of the argument
-     * @param originalType the original type of the argument as a class
-     * @param originalArrayType if this is non null this means the array was a Collection and this is the type in the collection
+     * @param originalType if this is non null this means the array was a Collection and this is the type in the collection
+     * @param originalArrayType the original type of the argument as a class
      * @param rawValue  raw value of the argument
      * @param format argument format
      * @return the argument value
@@ -176,14 +176,21 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
 
         } else if (rawValue instanceof Collection) {
             SchemaInputType inputType = schema.getInputTypeByName(argumentType);
-            Object colResults;
-            Class castClass;
-            if (List.class.isAssignableFrom(originalType)) {
-                colResults = new ArrayList();
-                castClass = List.class;
-            } else {
-                colResults = new TreeSet();
-                castClass = Set.class;
+
+            Object colResults = null;
+            try {
+                if (originalType.equals(List.class)) {
+                    colResults = new ArrayList<>();
+                } else if (originalType.equals(Set.class)
+                           || originalType.equals(Collection.class)) {
+                    colResults = new TreeSet<>();
+                } else {
+                    Constructor constructor = originalType.getDeclaredConstructor();
+                    colResults = constructor.newInstance();
+                }
+            } catch (Exception e) {
+                ensureRuntimeException(LOGGER, "Unable to construct a List of type " + originalType
+                        + " using Collection constructor", e);
             }
 
             if (inputType != null) {
@@ -202,7 +209,13 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
                     ((Collection) colResults).add(parseArgumentValue(originalArrayType, argumentType, value, format));
                 }
 
-                return castClass.cast(colResults);
+                if (originalType.equals(List.class)) {
+                    return (List) colResults;
+                }
+                if (originalType.equals(Collection.class)) {
+                    return (Collection) colResults;
+                }
+                return colResults;
             }
         } else {
             return parseArgumentValue(originalType, argumentType, rawValue, format);
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
index 8c37d1d83b7..740da7a4d75 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
@@ -40,11 +40,11 @@
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
-
 /**
  * Tests for {@link DataFetcherUtils} class.
  */
@@ -164,29 +164,29 @@ public void testArrays() throws Exception {
         ExecutionContext executionContext = new ExecutionContext(defaultContext);
         Schema schema = executionContext.getSchema();
 
-        String[] stringArray = new String[] {"A", "B", "C"};
+        String[] stringArray = new String[] { "A", "B", "C" };
         assertArgumentResult(schema, "echoStringArray", "value", stringArray, stringArray);
 
-        int[] intArray = new int[] {1, 2, 3, 4};
+        int[] intArray = new int[] { 1, 2, 3, 4 };
         assertArgumentResult(schema, "echoIntArray", "value", intArray, intArray);
 
-        Boolean[] booleanArray = new Boolean[] {true, false, true, true};
+        Boolean[] booleanArray = new Boolean[] { true, false, true, true };
         assertArgumentResult(schema, "echoBooleanArray", "value", booleanArray, booleanArray);
 
         String[][] stringArray2 = new String[][] {
-                { "A", "B", "C"},
-                { "D", "E", "F"}
+                { "A", "B", "C" },
+                { "D", "E", "F" }
         };
         assertArgumentResult(schema, "echoStringArray2", "value", stringArray2, stringArray2);
 
         SimpleContact[] contactArray = new SimpleContact[] {
-               new SimpleContact("c1", "Contact 1", 50, EnumTestWithEnumName.XL),
+                new SimpleContact("c1", "Contact 1", 50, EnumTestWithEnumName.XL),
                 new SimpleContact("c2", "Contact 2", 52, EnumTestWithEnumName.XL)
         };
         assertArgumentResult(schema, "echoSimpleContactArray", "value", contactArray, contactArray);
 
         // TODO: Test formatting of numbers and dates in arrays
-        
+
     }
 
     @Test
@@ -205,7 +205,7 @@ public void testSimpleCollections() throws Exception {
         assertArgumentResult(schema, "echoListOfBigIntegers", "value", colBigInteger, colBigInteger);
 
         // Test formatting for numbers and dates
-        List listFormattedIntegers = List.of("1 years old", "3 years old", "53 years old");
+        List listFormattedIntegers = new ArrayList<>(List.of("1 years old", "3 years old", "53 years old"));
         assertArgumentResult(schema, "echoFormattedListOfIntegers", "value", listFormattedIntegers,
                              List.of(1, 3, 53));
 
@@ -213,7 +213,7 @@ public void testSimpleCollections() throws Exception {
         LocalDate localDate2 = LocalDate.of(2020, 9, 22);
         List listLocalDates = List.of("23-09-2020", "22-09-2020");
         assertArgumentResult(schema, "echoFormattedLocalDate", "value", listLocalDates,
-                     List.of(localDate1, localDate2));
+                             List.of(localDate1, localDate2));
     }
 
     @Test
@@ -226,11 +226,13 @@ public void testCollectionsAndObjects() throws Exception {
         SimpleContact contact1 = new SimpleContact("c1", "Contact 1", 50, EnumTestWithEnumName.XL);
         SimpleContact contact2 = new SimpleContact("c2", "Contact 2", 52, EnumTestWithEnumName.XXL);
 
-        Collection colContacts = List.of(contact1, contact2);
-        Collection> listOfMaps = List.of(convertObjectToMap(contact1), convertObjectToMap(contact2));
+        Collection colContacts = new HashSet<>(List.of(contact1, contact2));
+        Collection> colOfMaps = new HashSet<>();
+        colOfMaps.add(convertObjectToMap(contact1));
+        colOfMaps.add(convertObjectToMap(contact2));
 
         assertArgumentResult(schema, "echoCollectionOfSimpleContacts", "value",
-                             listOfMaps, colContacts);
+                             colOfMaps, colContacts);
 
         // test multi-level collections
     }
@@ -276,7 +278,7 @@ protected List getList(Object... values) {
      * @param expected     the expected output
      * @throws Exception if any errors
      */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @SuppressWarnings( { "rawtypes", "unchecked" })
     protected void assertArgumentResult(Schema schema, String fdName,
                                         String argumentName, Object input, Object expected) throws Exception {
         SchemaArgument argument = getArgument(schema, "Query", fdName, argumentName);
@@ -292,7 +294,7 @@ protected void assertArgumentResult(Schema schema, String fdName,
             for (Object value : colExpected) {
                 if (!colResult.contains(value)) {
                     throw new AssertionError("Cannot find expected value [" +
-                                             value + "] in result " + colResult.toString());
+                                                     value + "] in result " + colResult.toString());
                 }
             }
         } else {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
index eb2c9a1c46a..6fc8d14f564 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
@@ -18,6 +18,7 @@
 
 import java.beans.IntrospectionException;
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -96,6 +97,20 @@ public void testMultiLevelListsAndArraysQueries() throws IOException {
         assertThat(stringList2.contains("three"), is(true));
         assertThat(stringList2.contains("four"), is(true));
         assertThat(stringList2.contains("five"), is(true));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoLinkedListBigDecimals(param: [-25.926804, 28.203392]) }"));
+        assertThat(mapResults.size(), is(1));
+        List bigDecimalList = (List) mapResults.get("echoLinkedListBigDecimals");
+        assertThat(bigDecimalList, is(notNullValue()));
+        assertThat(bigDecimalList.get(0), is(BigDecimal.valueOf(-25.926804)));
+        assertThat(bigDecimalList.get(1), is(new BigDecimal("28.203392")));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoListBigDecimal(param: [-25.926804, 28.203392]) }"));
+        assertThat(mapResults.size(), is(1));
+        bigDecimalList = (List) mapResults.get("echoListBigDecimal");
+        assertThat(bigDecimalList, is(notNullValue()));
+        assertThat(bigDecimalList.get(0), is(BigDecimal.valueOf(-25.926804)));
+        assertThat(bigDecimalList.get(1), is(new BigDecimal("28.203392")));
     }
 
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java
index 45b2492109f..e8f1370ce9c 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java
@@ -16,8 +16,11 @@
 
 package io.helidon.microprofile.graphql.server.test.queries;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
@@ -54,4 +57,14 @@ public Collection getListOfStringArrays() {
         arrayList.add(new String[] { "three", "four", "five"});
         return arrayList;
     }
+
+    @Query
+    public List echoLinkedListBigDecimals(@Name("param") LinkedList value) {
+        return value;
+    }
+
+    @Query
+    public List echoListBigDecimal(@Name("param") List value) {
+        return value;
+    }
 }

From 51eceb44ed072ae9d585a6670a440b6fb5633cf9 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Thu, 1 Oct 2020 17:41:50 +0800
Subject: [PATCH 105/178] progress on arrays

---
 .../graphql/server/DataFetcherUtils.java      |  93 +++++++++++++-
 .../graphql/server/GraphQLCdiExtension.java   |   2 +-
 .../graphql/server/SchemaGenerator.java       |   2 +-
 .../graphql/server/SchemaGeneratorHelper.java |  22 +++-
 .../graphql/server/MultiLevelArraysIT.java    | 121 +++++++++++++++++-
 .../graphql/server/SchemaGeneratorTest.java   |   5 +
 .../test/queries/ArrayAndListQueries.java     |  58 +++++++++
 7 files changed, 297 insertions(+), 6 deletions(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 9def2bccb85..6a6c958f5a1 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -16,6 +16,7 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -37,6 +38,7 @@
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.UUID;
+import java.util.function.Function;
 import java.util.logging.Logger;
 
 import javax.enterprise.inject.spi.CDI;
@@ -54,6 +56,7 @@
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isDateTimeClass;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isPrimitiveArray;
 
 /**
  * Utilities for working with {@link DataFetcher}s.
@@ -178,8 +181,9 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
             SchemaInputType inputType = schema.getInputTypeByName(argumentType);
 
             Object colResults = null;
+            boolean isArray = originalType.isArray();
             try {
-                if (originalType.equals(List.class)) {
+                if (originalType.equals(List.class) || isArray) {
                     colResults = new ArrayList<>();
                 } else if (originalType.equals(Set.class)
                            || originalType.equals(Collection.class)) {
@@ -201,7 +205,11 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
                                                          value, EMPTY_FORMAT));
                 }
 
-                return colResults;
+                if (isArray) {
+                    return ((List) colResults).toArray((Object[]) Array.newInstance(originalType.getComponentType(), 0));
+                } else {
+                    return colResults;
+                }
             } else {
                 // standard type or scalar so ensure we preserve the order and
                 // convert any values with formats
@@ -209,6 +217,15 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
                     ((Collection) colResults).add(parseArgumentValue(originalArrayType, argumentType, value, format));
                 }
 
+                if (isArray) {
+                    if (isPrimitiveArray(originalType)) {
+                        // array of primitives
+                        return generatePrimitiveArray((List) colResults, originalType, argumentType, format);
+                    } else {
+                        // array of Objects
+                        return ((List) colResults).toArray((Object[]) Array.newInstance(originalType.getComponentType(), 0));
+                    }
+                }
                 if (originalType.equals(List.class)) {
                     return (List) colResults;
                 }
@@ -222,6 +239,74 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
         }
     }
 
+    /**
+     * Return an array of primitives of the correct type from the given {@link List} of primitives.
+     * @param results       results to process
+     * @param originalType  the original type
+     * @param argumentType  argument type
+     * @param format format
+     * @return an array of primitives of the correct type
+     */
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    protected static Object generatePrimitiveArray(List results, Class originalType, String argumentType, String[] format) {
+        Class componentType = originalType.getComponentType();
+        try {
+            int i = 0;
+            if (componentType.equals(byte.class)) {
+                byte[] result = new byte[results.size()];
+                for (Object value : results) {
+                    result[i++] = Byte.parseByte(value.toString());  // this will come in as an Integer
+                }
+                return result;
+            } else if (componentType.equals(char.class)) {
+                char[] result = new char[results.size()];
+                for (Object value : results) {
+                    result[i++] = value.toString().charAt(0);
+                }
+                return result;
+            } else if (componentType.equals(boolean.class)) {
+                boolean[] result = new boolean[results.size()];
+                for (Object value : results) {
+                    result[i++] = Boolean.parseBoolean(value.toString());
+                }
+                return result;
+            } else if (componentType.equals(short.class)) {
+                short[] result = new short[results.size()];
+                for (Object value : results) {
+                    result[i++] = (short) parseArgumentValue(componentType, argumentType, value, format);
+                }
+                return result;
+            } else if (componentType.equals(float.class)) {
+                float[] result = new float[results.size()];
+                for (Object value : results) {
+                    result[i++] = (float) parseArgumentValue(componentType, argumentType, value, format);
+                }
+                return result;
+            } else if (componentType.equals(int.class)) {
+                int[] result = new int[results.size()];
+                for (Object value : results) {
+                    result[i++] = (int) parseArgumentValue(componentType, argumentType, value, format);
+                }
+                return result;
+            } else if (componentType.equals(long.class)) {
+                long[] result = new long[results.size()];
+                for (Object value : results) {
+                    result[i++] = (long) parseArgumentValue(componentType, argumentType, value, format);
+                }
+                return result;
+            } else if (componentType.equals(double.class)) {
+               double[] result = new double[results.size()];
+                for (Object value : results) {
+                    result[i++] = (double) parseArgumentValue(componentType, argumentType, value, format);
+                }
+                return result;
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return null;
+    }
+
     /**
      * Parse the given {@link SchemaArgument} and key and return the correct value to match the method argument.
      *
@@ -235,6 +320,10 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
     protected static Object parseArgumentValue(Class originalType, String argumentType, Object rawValue, String[] format)
             throws Exception {
 
+        if (originalType == null) {
+            // is array type
+            originalType = rawValue.getClass();
+        }
         if (originalType.isEnum()) {
             Class enumClass = (Class) originalType;
             return Enum.valueOf(enumClass, rawValue.toString());
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java
index 03060e91eec..e72f7b32c0a 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java
@@ -30,7 +30,7 @@
 import org.eclipse.microprofile.graphql.Type;
 
 /**
- * A CDI {@link Extension} to collect the classes that are of interest Microprofile GraphQL.
+ * A CDI {@link Extension} to collect the classes that are of interest to Microprofile GraphQL.
  */
 public class GraphQLCdiExtension implements Extension {
 
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index cc563a3f984..0a49c46d12e 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -338,7 +338,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes)
         processDefaultDateTimeValues(schema);
 
         // process the @GraphQLApi annotated classes
-        if (rootQueryType.getFieldDefinitions().size() == 0) {
+        if (rootQueryType.getFieldDefinitions().size() == 0 && rootMutationType.getFieldDefinitions().size() == 0) {
             LOGGER.warning("Unable to find any classes with @GraphQLApi annotation."
                                    + "Unable to build schema");
         }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
index 6ef4dd87c41..0dbdaf0bc10 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
@@ -386,13 +386,33 @@ protected static boolean isPrimitive(Class clazz) {
      * Return true of the class name is a primitive or array of primitives.
      *
      * @param clazz class name to check
-     * @return true of the class name is a primitive or array of primitives.
+     * @return true if the class name is a primitive or array of primitives.
      */
     protected static boolean isPrimitive(String clazz) {
         return JAVA_PRIMITIVE_TYPES.contains(clazz)
                 || PRIMITIVE_ARRAY_MAP.values().stream().anyMatch(v -> v.contains(clazz));
     }
 
+    /**
+     * Return true of the class name is an array of primitives.
+     *
+     * @param clazz class name to check
+     * @return true true of the class name is an array of primitives.
+     */
+    protected static boolean isPrimitiveArray(String clazz) {
+        return PRIMITIVE_ARRAY_MAP.values().stream().anyMatch(v -> v.contains(clazz));
+    }
+
+    /**
+     * Return true of the class name is an array of primitives.
+     *
+     * @param clazz {@link Class} to check
+     * @return true true of the class name is an array of primitives.
+     */
+    protected static boolean isPrimitiveArray(Class clazz) {
+        return PRIMITIVE_ARRAY_MAP.containsKey(clazz.getName());
+    }
+
     /**
      * Return the simple name from a given class as a String. This takes into account any annotations that may be present.
      *
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
index 6fc8d14f564..b1cf1186667 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
@@ -19,18 +19,23 @@
 import java.beans.IntrospectionException;
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.queries.ArrayAndListQueries;
 import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays;
 
+import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
 import io.helidon.microprofile.tests.junit5.AddExtension;
 import io.helidon.microprofile.tests.junit5.DisableDiscovery;
@@ -110,7 +115,121 @@ public void testMultiLevelListsAndArraysQueries() throws IOException {
         bigDecimalList = (List) mapResults.get("echoListBigDecimal");
         assertThat(bigDecimalList, is(notNullValue()));
         assertThat(bigDecimalList.get(0), is(BigDecimal.valueOf(-25.926804)));
-        assertThat(bigDecimalList.get(1), is(new BigDecimal("28.203392")));
+        assertThat(bigDecimalList.get(1), is(BigDecimal.valueOf(28.203392)));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoBigDecimalArray(param: [-25.926804, 28.203392]) }"));
+        assertThat(mapResults.size(), is(1));
+        bigDecimalList = (List) mapResults.get("echoBigDecimalArray");
+        assertThat(bigDecimalList, is(notNullValue()));
+        assertThat(bigDecimalList.get(0), is(BigDecimal.valueOf(-25.926804)));
+        assertThat(bigDecimalList.get(1), is(BigDecimal.valueOf(28.203392)));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoIntArray(param: [1, 2]) }"));
+        assertThat(mapResults.size(), is(1));
+        List integerList = (List) mapResults.get("echoIntArray");
+        assertThat(integerList, is(notNullValue()));
+        assertThat(integerList.get(0), is(1));
+        assertThat(integerList.get(1), is(2));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoShortArray(param: [1, 2]) }"));
+        assertThat(mapResults.size(), is(1));
+        integerList = (List) mapResults.get("echoShortArray");
+        assertThat(integerList, is(notNullValue()));
+        assertThat(integerList.get(0), is(1));
+        assertThat(integerList.get(1), is(2));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoLongArray(param: [1, 2]) }"));
+        assertThat(mapResults.size(), is(1));
+        List listBigInteger = (List) mapResults.get("echoLongArray");
+        assertThat(listBigInteger, is(notNullValue()));
+        assertThat(listBigInteger.get(0), is(BigInteger.valueOf(1)));
+        assertThat(listBigInteger.get(1), is(BigInteger.valueOf(2)));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoDoubleArray(param: [1.1, 2.2]) }"));
+        assertThat(mapResults.size(), is(1));
+        List listDouble = (List) mapResults.get("echoDoubleArray");
+        assertThat(listDouble, is(notNullValue()));
+        assertThat(listDouble.get(0), is(Double.valueOf(1.1)));
+        assertThat(listDouble.get(1), is(Double.valueOf(2.2)));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoBooleanArray(param: [true, false]) }"));
+        assertThat(mapResults.size(), is(1));
+        List listBoolean = (List) mapResults.get("echoBooleanArray");
+        assertThat(listBoolean, is(notNullValue()));
+        assertThat(listBoolean.get(0), is(true));
+        assertThat(listBoolean.get(1), is(false));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoCharArray(param: [\"A\", \"B\"]) }"));
+        assertThat(mapResults.size(), is(1));
+        List listString = (List) mapResults.get("echoCharArray");
+        assertThat(listString, is(notNullValue()));
+        assertThat(listString.get(0), is("A"));
+        assertThat(listString.get(1), is("B"));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoFloatArray(param: [1.01, 2.02]) }"));
+        assertThat(mapResults.size(), is(1));
+        listDouble = (List) mapResults.get("echoFloatArray");
+        assertThat(listDouble, is(notNullValue()));
+        assertThat(listDouble.get(0), is(1.01d));
+        assertThat(listDouble.get(1), is(2.02d));
+
+        mapResults = getAndAssertResult(executionContext.execute("query { echoByteArray(param: [0, 1]) }"));
+        assertThat(mapResults.size(), is(1));
+        List listInteger = (List) mapResults.get("echoByteArray");
+        assertThat(listInteger, is(notNullValue()));
+        assertThat(listInteger.get(0), is(0));
+        assertThat(listInteger.get(1), is(1));
+
+        SimpleContact contact1 = new SimpleContact("id1", "name1", 1, EnumTestWithEnumName.XL);
+        SimpleContact contact2 = new SimpleContact("id2", "name2", 2, EnumTestWithEnumName.S);
+
+        mapResults = getAndAssertResult(executionContext.execute(
+                "query { echoSimpleContactArray(param: [ "
+                        + generateInput(contact1) + ", "
+                        + generateInput(contact2)
+                        + " ]) { id name age tShirtSize } }"));
+        assertThat(mapResults.size(), is(1));
+        List> listContacts = (List>) mapResults.get("echoSimpleContactArray");
+        assertThat(listContacts, is(notNullValue()));
+        assertThat(listContacts.size(), is(2));
+        mapResults2 = listContacts.get(0);
+        assertThat(mapResults2, is(not(nullValue())));
+        assertThat(mapResults2.get("id"), is(contact1.getId()));
+        assertThat(mapResults2.get("name"), is(contact1.getName()));
+        assertThat(mapResults2.get("age"), is(contact1.getAge()));
+        assertThat(mapResults2.get("tShirtSize"), is(contact1.getTShirtSize().toString()));
+
+        mapResults2 = listContacts.get(1);
+        assertThat(mapResults2, is(not(nullValue())));
+        assertThat(mapResults2.get("id"), is(contact2.getId()));
+        assertThat(mapResults2.get("name"), is(contact2.getName()));
+        assertThat(mapResults2.get("age"), is(contact2.getAge()));
+        assertThat(mapResults2.get("tShirtSize"), is(contact2.getTShirtSize().toString()));
+
+        mapResults = getAndAssertResult(executionContext.execute(
+                "query { processListListBigDecimal(param :[[\"-25.926804 "
+                + "longlat\", \"28.203392 longlat\"],[\"-26.926804 longlat\", "
+                + " \"27.203392 longlat\"],[\"-27.926804 longlat\", \"26.203392 longlat\"]] ) }"));
+        assertThat(mapResults.size(), is(1));
+        String result =  (String) mapResults.get("processListListBigDecimal");
+        assertThat(result, is(notNullValue()));
+
+
+    }
+
+    protected String generateInput(SimpleContact contact) {
+        return new StringBuilder("{")
+                .append("id: ").append(quote(contact.getId())).append(" ")
+                .append("name: ").append(quote(contact.getName())).append(" ")
+                .append("age: ").append(contact.getAge()).append(" ")
+                .append("tShirtSize: ").append(contact.getTShirtSize())
+                .append("}")
+                .toString();
+
+    }
+
+    protected String quote(String s) {
+        return "\"" + s + "\"";
     }
 
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java
index 3df45297685..e507304e37e 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java
@@ -580,6 +580,11 @@ public void testFormatAnnotationFromSchema() throws IntrospectionException, Clas
         assertThat(schema, is(notNullValue()));
     }
 
+    @Test
+    public void testPrimitiveArrays() {
+        ArrayList a;
+    }
+
     private void assertParameterFormat(Method method, int paramNumber, String[] expectedFormat) {
         assertThat(method, is(notNullValue()));
         Parameter parameter = method.getParameters()[paramNumber];
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java
index e8f1370ce9c..572ab963dc6 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java
@@ -22,6 +22,7 @@
 import java.util.LinkedList;
 import java.util.List;
 
+import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 
@@ -29,6 +30,7 @@
 import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Name;
+import org.eclipse.microprofile.graphql.NumberFormat;
 import org.eclipse.microprofile.graphql.Query;
 
 /**
@@ -67,4 +69,60 @@ public List echoLinkedListBigDecimals(@Name("param") LinkedList echoListBigDecimal(@Name("param") List value) {
         return value;
     }
+
+    @Query
+    public BigDecimal[] echoBigDecimalArray(@Name("param") BigDecimal[] value) {
+        return value;
+    }
+
+    @Query
+    public int[] echoIntArray(@Name("param") int[] value) {
+        return value;
+    }
+
+    @Query
+    public short[] echoShortArray(@Name("param") short[] value) {
+        return value;
+    }
+
+    @Query
+    public long[] echoLongArray(@Name("param") long[] value) {
+        return value;
+    }
+
+    @Query
+    public double[] echoDoubleArray(@Name("param") double[] value) {
+        return value;
+    }
+
+    @Query
+    public boolean[] echoBooleanArray(@Name("param") boolean[] value) {
+        return value;
+    }
+
+    @Query
+    public char[] echoCharArray(@Name("param") char[] value) {
+        return value;
+    }
+
+    @Query
+    public float[] echoFloatArray(@Name("param") float[] value) {
+        return value;
+    }
+
+    @Query
+    public byte[] echoByteArray(@Name("param") byte[] value) {
+        return value;
+    }
+
+    @Query
+    public SimpleContact[] echoSimpleContactArray(@Name("param") SimpleContact[] value) {
+        return value;
+    }
+
+    @Query
+    public String processListListBigDecimal(@Name("param") List> value) {
+        return value.toString();
+    }
 }

From 14c85934fb6c38db4097076d73afdf1efcde986d Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Fri, 2 Oct 2020 09:50:49 +0800
Subject: [PATCH 106/178] Fixup arrays

---
 .../microprofile/graphql/server/DataFetcherUtils.java      | 7 ++++++-
 .../microprofile/graphql/server/MultiLevelArraysIT.java    | 3 +--
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 6a6c958f5a1..930ea2743a0 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -214,7 +214,12 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
                 // standard type or scalar so ensure we preserve the order and
                 // convert any values with formats
                 for (Object value : (Collection) rawValue) {
-                    ((Collection) colResults).add(parseArgumentValue(originalArrayType, argumentType, value, format));
+                    if (value instanceof Collection) {
+                        ((Collection) colResults).add(generateArgumentValue(schema, argumentType,
+                                                                            originalType, originalArrayType, value, format));
+                    } else {
+                       ((Collection) colResults).add(parseArgumentValue(originalArrayType, argumentType, value, format));
+                    }
                 }
 
                 if (isArray) {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
index b1cf1186667..a11afe57f5b 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
@@ -213,8 +213,7 @@ public void testMultiLevelListsAndArraysQueries() throws IOException {
         assertThat(mapResults.size(), is(1));
         String result =  (String) mapResults.get("processListListBigDecimal");
         assertThat(result, is(notNullValue()));
-
-
+        assertThat(result, is("[[-25.926804, 28.203392], [-26.926804, 27.203392], [-27.926804, 26.203392]]"));
     }
 
     protected String generateInput(SimpleContact contact) {

From 74d54686f44b0b138fff42483360aca4f81bdb7e Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Wed, 7 Oct 2020 14:26:07 +0800
Subject: [PATCH 107/178] Progress. TCK not running

---
 .../graphql/server/CustomScalars.java         | 128 +++++++++++++++++-
 .../graphql/server/DataFetcherUtils.java      |   5 +-
 .../graphql/server/FormattingHelper.java      |   2 +-
 .../microprofile/graphql/server/Schema.java   |   2 +-
 .../graphql/server/SchemaGenerator.java       |  10 ++
 .../graphql/server/SchemaGeneratorHelper.java |  31 +++--
 .../graphql/server/DateTimeIT.java            |  26 +++-
 .../graphql/server/DateTimeScalarIT.java      |  71 ++++++++++
 .../test/queries/DateTimeScalarQueries.java   |  58 ++++++++
 .../queries/SimpleQueriesAndMutations.java    |  16 +++
 .../server/test/types/DateTimePojo.java       |   2 +-
 .../server/test/types/SimpleDateTimePojo.java |  50 +++++++
 12 files changed, 375 insertions(+), 26 deletions(-)
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DateTimeScalarQueries.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTimePojo.java

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
index 8d2466195f3..545af454328 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
@@ -18,8 +18,12 @@
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.OffsetDateTime;
 import java.time.OffsetTime;
+import java.time.ZonedDateTime;
 
 import graphql.Scalars;
 import graphql.language.StringValue;
@@ -37,7 +41,9 @@
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATETIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_OFFSET_DATETIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_TIME_SCALAR;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_ZONED_DATETIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.TIME_SCALAR;
 
 /**
@@ -91,6 +97,16 @@ private CustomScalars() {
      */
     public static final GraphQLScalarType CUSTOM_DATE_TIME_SCALAR = newDateTimeScalar(DATETIME_SCALAR);
 
+    /**
+     * An instance of a custom offset date/time scalar (with default formatting).
+     */
+    public static final GraphQLScalarType CUSTOM_OFFSET_DATE_TIME_SCALAR = newOffsetDateTimeScalar(FORMATTED_OFFSET_DATETIME_SCALAR);
+
+    /**
+     * An instance of a custom offset date/time scalar (with default formatting).
+     */
+    public static final GraphQLScalarType CUSTOM_ZONED_DATE_TIME_SCALAR = newZonedDateTimeScalar(FORMATTED_ZONED_DATETIME_SCALAR);
+
     /**
      * An instance of a custom time scalar (with default formatting).
      */
@@ -110,6 +126,50 @@ private CustomScalars() {
      */
     @SuppressWarnings("unchecked")
     public static GraphQLScalarType newDateTimeScalar(String name) {
+        GraphQLScalarType originalScalar = ExtendedScalars.DateTime;
+        Coercing originalCoercing = originalScalar.getCoercing();
+        return GraphQLScalarType.newScalar().coercing(new Coercing() {
+            public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
+                if (dataFetcherResult instanceof String) {
+                    return (String) dataFetcherResult;
+                } else {
+                    return originalCoercing.serialize(dataFetcherResult);
+                }
+            }
+
+            @Override
+            public Object parseValue(Object input) throws CoercingParseValueException {
+                return input instanceof StringValue ? ((StringValue) input).getValue()
+                        : originalCoercing.parseValue(input);
+            }
+
+            @Override
+            public Object parseLiteral(Object input) throws CoercingParseLiteralException {
+                if (!(input instanceof StringValue)) {
+                    throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
+                              + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
+                }
+                try {
+                    return LocalDateTime.parse(((StringValue) input).getValue());
+                } catch (Exception e) {
+                    throw new CoercingParseLiteralException(e);
+                }
+            }
+        })
+        .name(name)
+        .description("Custom: " + originalScalar.getDescription())
+        .build();
+    }
+
+    /**
+     * Return a new custom offset date/time scalar.
+     *
+     * @param name the name of the scalar
+     *
+     * @return a new custom date/time scalar
+     */
+    @SuppressWarnings("unchecked")
+    public static GraphQLScalarType newOffsetDateTimeScalar(String name) {
         GraphQLScalarType originalScalar = ExtendedScalars.DateTime;
         Coercing originalCoercing = originalScalar.getCoercing();
         return GraphQLScalarType.newScalar().coercing(new Coercing() {
@@ -129,8 +189,58 @@ public Object parseValue(Object input) throws CoercingParseValueException {
 
             @Override
             public Object parseLiteral(Object input) throws CoercingParseLiteralException {
+                if (!(input instanceof StringValue)) {
+                    throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
+                              + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
+                }
+                try {
+                    return OffsetDateTime.parse(((StringValue) input).getValue());
+                } catch (Exception e) {
+                    throw new CoercingParseLiteralException(e);
+                }
+            }
+        })
+        .name(name)
+        .description("Custom: " + originalScalar.getDescription())
+        .build();
+    }
+    /**
+     * Return a new custom zoned date/time scalar.
+     *
+     * @param name the name of the scalar
+     *
+     * @return a new custom date/time scalar
+     */
+    @SuppressWarnings("unchecked")
+    public static GraphQLScalarType newZonedDateTimeScalar(String name) {
+        GraphQLScalarType originalScalar = ExtendedScalars.DateTime;
+        Coercing originalCoercing = originalScalar.getCoercing();
+        return GraphQLScalarType.newScalar().coercing(new Coercing() {
+            public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
+                if (dataFetcherResult instanceof String) {
+                    return (String) dataFetcherResult;
+                } else {
+                    return originalCoercing.serialize(dataFetcherResult);
+                }
+            }
+
+            @Override
+            public Object parseValue(Object input) throws CoercingParseValueException {
                 return input instanceof StringValue ? ((StringValue) input).getValue()
-                        : originalCoercing.parseLiteral(input);
+                        : originalCoercing.parseValue(input);
+            }
+
+            @Override
+            public Object parseLiteral(Object input) throws CoercingParseLiteralException {
+                if (!(input instanceof StringValue)) {
+                    throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
+                              + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
+                }
+                try {
+                    return ZonedDateTime.parse(((StringValue) input).getValue());
+                } catch (Exception e) {
+                    throw new CoercingParseLiteralException(e);
+                }
             }
         })
         .name(name)
@@ -164,8 +274,15 @@ public Object parseValue(Object input) throws CoercingParseValueException {
 
             @Override
             public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                return input instanceof StringValue ? ((StringValue) input).getValue()
-                        : originalCoercing.parseLiteral(input);
+                if (!(input instanceof StringValue)) {
+                    throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
+                              + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
+                }
+                try {
+                    return LocalTime.parse(((StringValue) input).getValue());
+                } catch (Exception e) {
+                    throw new CoercingParseLiteralException(e);
+                }
             }
         })
         .name(name)
@@ -183,7 +300,7 @@ public Object parseLiteral(Object input) throws CoercingParseLiteralException {
     @SuppressWarnings("unchecked")
     public static GraphQLScalarType newDateScalar(String name) {
         GraphQLScalarType originalScalar = ExtendedScalars.Date;
-        Coercing originalCoercing = originalScalar.getCoercing();
+        Coercing originalCoercing = originalScalar.getCoercing();
         return GraphQLScalarType.newScalar().coercing(new Coercing() {
             public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
                 return dataFetcherResult instanceof String
@@ -199,8 +316,7 @@ public Object parseValue(Object input) throws CoercingParseValueException {
 
             @Override
             public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                return input instanceof StringValue ? ((StringValue) input).getValue()
-                        : originalCoercing.parseLiteral(input);
+                   return originalCoercing.parseLiteral(input);
             }
         })
         .name(name)
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 930ea2743a0..89f60bd9023 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -38,7 +38,6 @@
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.UUID;
-import java.util.function.Function;
 import java.util.logging.Logger;
 
 import javax.enterprise.inject.spi.CDI;
@@ -416,7 +415,7 @@ public static class DateFormattingDataFetcher
         private DateTimeFormatter dateTimeFormatter;
 
         /**
-         * Construct a new NumberFormattingDataFetcher.
+         * Construct a new DateFormattingDataFetcher.
          *
          * @param propertyName property to extract
          * @param type         GraphQL type of the property
@@ -430,6 +429,8 @@ public DateFormattingDataFetcher(String propertyName, String type, String valueF
 
         @Override
         public Object get(DataFetchingEnvironment environment) {
+            LOGGER.info("Property=" + getPropertyName() + ", Original Value=" + super.get(environment) + ", dateTimeFormatter=" + dateTimeFormatter
+                       + ", formattedResult=" + formatDate(super.get(environment), dateTimeFormatter));
             return formatDate(super.get(environment), dateTimeFormatter);
         }
     }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
index 6dda2fce91b..07c1e94a426 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
@@ -196,7 +196,7 @@ protected static DateTimeFormatter getCorrectDateFormatter(String type, String l
         if (format != null) {
             formatter = DateTimeFormatter.ofPattern(format, actualLocale);
         } else {
-            // handle defaults if not format specified
+            // handle defaults of not format specified
             if (OFFSET_TIME_CLASS.equals(type)) {
                 formatter = DateTimeFormatter.ISO_OFFSET_TIME.withLocale(actualLocale);
             } else if (LOCAL_TIME_CLASS.equals(type)) {
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java
index b456f6cac0a..8748d0a767c 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java
@@ -231,7 +231,7 @@ public RuntimeWiring getRuntimeWiring() {
 
         // register the scalars
         getScalars().forEach(s -> {
-            LOGGER.finest("Register Scalar: " + s);
+            LOGGER.info("Register Scalar: " + s);
             builder.scalar(s.getGraphQLScalarType());
         });
 
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index 0a49c46d12e..9792ad856e4 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -62,7 +62,9 @@
 
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_DATE_TIME_SCALAR;
+import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_OFFSET_DATE_TIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_TIME_SCALAR;
+import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_ZONED_DATE_TIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.FormattingHelper.DATE;
 import static io.helidon.microprofile.graphql.server.FormattingHelper.NO_FORMATTING;
 import static io.helidon.microprofile.graphql.server.FormattingHelper.NUMBER;
@@ -78,7 +80,9 @@
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATETIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_OFFSET_DATETIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_TIME_SCALAR;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_ZONED_DATETIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ID;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.TIME_SCALAR;
@@ -389,6 +393,12 @@ private void processDefaultDateTimeValues(Schema schema) {
                         } else if (fd.getReturnType().equals(FORMATTED_DATETIME_SCALAR)) {
                             fd.setReturnType(DATETIME_SCALAR);
                             newScalarType = CUSTOM_DATE_TIME_SCALAR;
+                        } else if (fd.getReturnType().equals(FORMATTED_OFFSET_DATETIME_SCALAR)) {
+                            fd.setReturnType(FORMATTED_OFFSET_DATETIME_SCALAR);
+                            newScalarType = CUSTOM_OFFSET_DATE_TIME_SCALAR;
+                        } else if (fd.getReturnType().equals(FORMATTED_ZONED_DATETIME_SCALAR)) {
+                            fd.setReturnType(FORMATTED_ZONED_DATETIME_SCALAR);
+                            newScalarType = CUSTOM_ZONED_DATE_TIME_SCALAR;
                         }
 
                         // clone the scalar with the new scalar name
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
index 0dbdaf0bc10..7330c12cb1f 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
@@ -58,6 +58,8 @@
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_BIGINTEGER_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_FLOAT_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_INT_SCALAR;
+import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_OFFSET_DATE_TIME_SCALAR;
+import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_ZONED_DATE_TIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_DATE_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_DATE_TIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.CustomScalars.FORMATTED_CUSTOM_TIME_SCALAR;
@@ -182,6 +184,16 @@ public final class SchemaGeneratorHelper {
      */
     public static final String FORMATTED_DATETIME_SCALAR = "FormattedDateTime";
 
+    /**
+     * Formatted DateTime scalar.
+     */
+    public static final String FORMATTED_OFFSET_DATETIME_SCALAR = "FormattedOffsetDateTime";
+
+    /**
+     * Formatted DateTime scalar.
+     */
+    public static final String FORMATTED_ZONED_DATETIME_SCALAR = "FormattedZonedDateTime";
+
     /**
      * Formatted Time Scalar.
      */
@@ -265,20 +277,20 @@ public final class SchemaGeneratorHelper {
 
         // Time scalars
         put(OffsetTime.class.getName(),
-            new SchemaScalar(FORMATTED_TIME_SCALAR, OFFSET_TIME_CLASS, FORMATTED_CUSTOM_TIME_SCALAR, "HH:mm:ssZ"));
+            new SchemaScalar(FORMATTED_TIME_SCALAR, OFFSET_TIME_CLASS, FORMATTED_CUSTOM_TIME_SCALAR, "HH[:mm][:ss]Z"));
         put(LocalTime.class.getName(),
-            new SchemaScalar(FORMATTED_TIME_SCALAR, LOCAL_TIME_CLASS, FORMATTED_CUSTOM_TIME_SCALAR, "HH:mm:ss"));
+            new SchemaScalar(FORMATTED_TIME_SCALAR, LOCAL_TIME_CLASS, FORMATTED_CUSTOM_TIME_SCALAR, "HH[:mm][:ss]"));
 
         // DateTime scalars
         put(OFFSET_DATE_TIME_CLASS,
-            new SchemaScalar(FORMATTED_DATETIME_SCALAR, OFFSET_DATE_TIME_CLASS, FORMATTED_CUSTOM_DATE_TIME_SCALAR,
-                             "yyyy-MM-dd'T'HH:mm:ssZ"));
+            new SchemaScalar(FORMATTED_OFFSET_DATETIME_SCALAR, OFFSET_DATE_TIME_CLASS, CUSTOM_OFFSET_DATE_TIME_SCALAR,
+                             "yyyy-MM-dd'T'HH[:mm][:ss]Z"));
         put(ZONED_DATE_TIME_CLASS,
-            new SchemaScalar(FORMATTED_DATETIME_SCALAR, ZONED_DATE_TIME_CLASS, FORMATTED_CUSTOM_DATE_TIME_SCALAR,
-                             "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'"));
+            new SchemaScalar(FORMATTED_ZONED_DATETIME_SCALAR, ZONED_DATE_TIME_CLASS, CUSTOM_ZONED_DATE_TIME_SCALAR,
+                             "yyyy-MM-dd'T'HH[:mm][:ss]Z'['VV']'"));
         put(LOCAL_DATE_TIME_CLASS,
             new SchemaScalar(FORMATTED_DATETIME_SCALAR, LOCAL_DATE_TIME_CLASS, FORMATTED_CUSTOM_DATE_TIME_SCALAR,
-                             "yyyy-MM-dd'T'HH:mm:ss"));
+                             "yyyy-MM-dd'T'HH[:mm][:ss]"));
 
         // Date scalar
         put(LOCAL_DATE_CLASS, new SchemaScalar(FORMATTED_DATE_SCALAR, LOCAL_DATE_CLASS, FORMATTED_CUSTOM_DATE_SCALAR,
@@ -476,11 +488,13 @@ protected static String getGraphQLType(String className) {
      * Return true of the name is a Date, DateTime, or Time scalar.
      *
      * @param scalarName scalar name
-     * @return rue of the name is a Date, DateTime, or Time scalar
+     * @return true of the name is a Date, DateTime, or Time scalar
      */
     protected static boolean isDateTimeScalar(String scalarName) {
         return FORMATTED_DATE_SCALAR.equals(scalarName)
                 || FORMATTED_TIME_SCALAR.equals(scalarName)
+                || FORMATTED_OFFSET_DATETIME_SCALAR.equals(scalarName)
+                || FORMATTED_ZONED_DATETIME_SCALAR.equals(scalarName)
                 || FORMATTED_DATETIME_SCALAR.equals(scalarName);
     }
     /**
@@ -576,6 +590,7 @@ protected static boolean isDateTimeClass(Class clazz) {
                         || clazz.equals(LocalTime.class)
                         || clazz.equals(LocalDateTime.class)
                         || clazz.equals(OffsetTime.class)
+                        || clazz.equals(ZonedDateTime.class)
                         || clazz.equals(OffsetDateTime.class));
     }
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
index f270532e69a..95ec33580b5 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
@@ -18,6 +18,7 @@
 
 import java.io.IOException;
 
+import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -83,11 +84,11 @@ public void testDateAndTime() throws IOException {
         assertThat(fd.getReturnType(), is(FORMATTED_DATE_SCALAR));
 
         // test default values for date and time
-        assertDefaultFormat(type, "offsetTime", "HH:mm:ssZ", true);
+        assertDefaultFormat(type, "offsetTime", "HH[:mm][:ss]Z", true);
         assertDefaultFormat(type, "localTime", "hh:mm:ss", false);
-        assertDefaultFormat(type, "localDateTime", "yyyy-MM-dd'T'HH:mm:ss", true);
-        assertDefaultFormat(type, "offsetDateTime", "yyyy-MM-dd'T'HH:mm:ssZ", true);
-        assertDefaultFormat(type, "zonedDateTime", "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'", true);
+        assertDefaultFormat(type, "localDateTime", "yyyy-MM-dd'T'HH[:mm][:ss]", true);
+        assertDefaultFormat(type, "offsetDateTime", "yyyy-MM-dd'T'HH[:mm][:ss]Z", true);
+        assertDefaultFormat(type, "zonedDateTime", "yyyy-MM-dd'T'HH[:mm][:ss]Z'['VV']'", true);
         assertDefaultFormat(type, "localDateNoFormat", "yyyy-MM-dd", true);
         assertDefaultFormat(type, "significantDates", "yyyy-MM-dd", true);
         assertDefaultFormat(type, "formattedListOfDates", "dd/MM", false);
@@ -171,7 +172,7 @@ public void testDateAndTime() throws IOException {
         assertThat(argument.isPresent(), is(true));
         SchemaArgument a = argument.get();
         assertThat(a.getFormat()[0], is("MM/dd/yyyy"));
-//        assertThat(a.getArgumentType(), is(FORMATTED_DATE_SCALAR));
+
         mapResults = getAndAssertResult(
         executionContext.execute("mutation { echoFormattedDateWithJsonB(dates: [ \"09/22/2020\", \"09/23/2020\" ]) }"));
         assertThat(mapResults, is(notNullValue()));
@@ -179,6 +180,18 @@ public void testDateAndTime() throws IOException {
         assertThat(listDates.size(), is(2));
         assertThat(listDates.get(0), is("22/09/2020"));
         assertThat(listDates.get(1), is("23/09/2020"));
+
+        mapResults = getAndAssertResult(executionContext.execute(
+                        "query { echoOffsetDateTime(value: \"29 Jan 2020 at 09:45 in zone +0200\") }"));
+        assertThat(mapResults, is(notNullValue()));
+        assertThat(mapResults.get("echoOffsetDateTime"), is("2020-01-29T09:45:00+0200"));
+
+        mapResults = getAndAssertResult(executionContext.execute(
+                        "query { echoZonedDateTime(value: \"19 February 1900 at 12:00 in Africa/Johannesburg\") }"));
+        assertThat(mapResults, is(notNullValue()));
+        assertThat(mapResults.get("echoZonedDateTime"), is("1900-02-19T12:00:00+0130[Africa/Johannesburg]"));
+
+
     }
 
     @Test
@@ -232,8 +245,7 @@ public void testDatesAndMutations() throws IOException {
                 executionContext.execute("mutation { testDefaultFormatLocalDateTime(dateTime: \"2020-01-12T10:00:00\") }"));
         assertThat(mapResults.size(), is(1));
         assertThat(mapResults.get("testDefaultFormatLocalDateTime"), is( "10:00:00 12-01-2020"));
-
-        // TODO: https://github.com/eclipse/microprofile-graphql/issues/306 - 1.0.3 spec most likely
+        
         mapResults = getAndAssertResult(
                 executionContext.execute("query { transformedDate }"));
         assertThat(mapResults, is(notNullValue()));
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
new file mode 100644
index 00000000000..ed64556cf4b
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.queries.DateTimeScalarQueries;
+import io.helidon.microprofile.graphql.server.test.types.SimpleDateTimePojo;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+
+@HelidonTest
+@DisableDiscovery
+@AddExtension(GraphQLCdiExtension.class)
+@AddBean(DateTimeScalarQueries.class)
+@AddBean(SimpleDateTimePojo.class)
+@AddBean(TestDB.class)
+public class DateTimeScalarIT extends AbstractGraphQLIT {
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testDateAndTime() throws IOException {
+        setupIndex(indexFileName, SimpleDateTimePojo.class, DateTimeScalarQueries.class);
+        ExecutionContext executionContext = new ExecutionContext(defaultContext);
+
+        Map mapResults = getAndAssertResult(
+                executionContext.execute("query { echoSimpleDateTimePojo (dates:[\"2020-01-13\","
+                                                 + "\"2021-02-14\"]) { formattedListOfDates } }"));
+        assertThat(mapResults.size(), is(1));
+        Map mapResults2 = (Map) mapResults.get("echoSimpleDateTimePojo");
+        assertThat(mapResults2, is(notNullValue()));
+        assertThat(mapResults2.size(), is(1));
+
+        List listDates = (ArrayList) mapResults2.get("formattedListOfDates");
+        assertThat(listDates, is(notNullValue()));
+        assertThat(listDates.size(), is(2));
+        assertThat(listDates.get(0), is("13/01"));
+        assertThat(listDates.get(1), is("14/02"));
+
+        mapResults = getAndAssertResult(executionContext.execute("mutation { echoLocalTime(time: \"15:13:00\") }"));
+        assertThat(mapResults.size(), is(1));
+        assertThat(mapResults.get("echoLocalTime"), is("15:13"));
+    }
+
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DateTimeScalarQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DateTimeScalarQueries.java
new file mode 100644
index 00000000000..2498100a77b
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DateTimeScalarQueries.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server.test.queries;
+
+import java.time.LocalDate;
+
+import java.time.LocalTime;
+import java.util.List;
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.types.SimpleDateTimePojo;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+import javax.json.bind.annotation.JsonbDateFormat;
+import org.eclipse.microprofile.graphql.GraphQLApi;
+import org.eclipse.microprofile.graphql.Mutation;
+import org.eclipse.microprofile.graphql.Name;
+import org.eclipse.microprofile.graphql.Query;
+
+/**
+ * Class that holds simple query definitions with no-argument.
+ */
+@GraphQLApi
+@ApplicationScoped
+public class DateTimeScalarQueries {
+
+    @Inject
+    private TestDB testDB;
+
+    public DateTimeScalarQueries() {
+    }
+
+    @Query
+    public SimpleDateTimePojo echoSimpleDateTimePojo(@Name("dates") List listDates) {
+        return new SimpleDateTimePojo(listDates);
+    }
+
+    @Mutation
+    @JsonbDateFormat("HH:mm")
+    public LocalTime echoLocalTime(@Name("time") LocalTime localTime) {
+        return localTime;
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
index 12b98d04c7e..ef2ec6cb64c 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
@@ -21,6 +21,8 @@
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -234,4 +236,18 @@ public List echoFormattedDateWithJsonB(@Name("dates")
                                                       List<@DateFormat("MM/dd/yyyy") LocalDate> localDates) {
         return localDates;
     }
+
+    @Query
+    public OffsetDateTime echoOffsetDateTime(@Name("value")
+                                             @JsonbDateFormat(value = "dd MMM yyyy 'at' HH:mm 'in zone' Z",locale = "en-ZA")
+                                             OffsetDateTime offsetDateTime) {
+        return offsetDateTime;
+    }
+
+    @Query
+    public ZonedDateTime echoZonedDateTime(@Name("value")
+                                           @JsonbDateFormat(value = "dd MMMM yyyy 'at' HH:mm 'in' VV",locale = "en-ZA")
+                                           ZonedDateTime zonedDateTime) {
+        return zonedDateTime;
+    }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java
index adb057a61f2..635b8dd903d 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java
@@ -160,7 +160,7 @@ public List getSignificantDates() {
         return this.significantDates;
     }
 
-    public List<@DateFormat("dd/MM")  LocalDate> getFormattedListOfDates() {
+    public List<@DateFormat("dd/MM") LocalDate> getFormattedListOfDates() {
         return formattedListOfDates;
     }
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTimePojo.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTimePojo.java
new file mode 100644
index 00000000000..2f4e9898831
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTimePojo.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server.test.types;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.ZonedDateTime;
+import java.util.List;
+import javax.json.bind.annotation.JsonbDateFormat;
+import org.eclipse.microprofile.graphql.DateFormat;
+import org.eclipse.microprofile.graphql.Description;
+import org.eclipse.microprofile.graphql.Type;
+
+/**
+ * Class representing various date/time types.
+ */
+@Type
+public class SimpleDateTimePojo {
+
+    private List formattedListOfDates;
+
+    public SimpleDateTimePojo(List formattedListOfDates) {
+        this.formattedListOfDates = formattedListOfDates;
+    }
+
+    public List<@DateFormat("dd/MM") LocalDate> getFormattedListOfDates() {
+        return formattedListOfDates;
+    }
+
+    public void setFormattedListOfDates(List formattedListOfDates) {
+        this.formattedListOfDates = formattedListOfDates;
+    }
+}

From 7f524b9a6c3c1e8f4191eb27eb015113694bd068 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Fri, 9 Oct 2020 07:26:12 +0800
Subject: [PATCH 108/178] Before change for date fixes

---
 .../graphql/server/CustomScalars.java         | 33 ++++++----
 .../graphql/server/DataFetcherUtils.java      |  3 +-
 .../graphql/server/JsonUtils.java             |  6 +-
 .../graphql/server/SchemaGeneratorHelper.java |  4 ++
 .../server/src/main/java/module-info.java     |  1 +
 .../graphql/server/DateTimeIT.java            | 63 +++++++++++++++----
 .../graphql/server/DefaultValuesIT.java       |  5 +-
 .../test/queries/DefaultValueQueries.java     |  4 +-
 .../queries/SimpleQueriesAndMutations.java    |  5 ++
 .../server/test/types/DateTimePojo.java       | 10 ++-
 .../server/test/types/DefaultValuePOJO.java   | 28 ++++++++-
 11 files changed, 131 insertions(+), 31 deletions(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
index 545af454328..024a79d0505 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
@@ -145,12 +145,10 @@ public Object parseValue(Object input) throws CoercingParseValueException {
 
             @Override
             public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                if (!(input instanceof StringValue)) {
-                    throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
-                              + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
-                }
+                validateLiteral(input);
                 try {
-                    return LocalDateTime.parse(((StringValue) input).getValue());
+                    return ((StringValue) input).getValue();
+//                    return LocalDateTime.parse(((StringValue) input).getValue());
                 } catch (Exception e) {
                     throw new CoercingParseLiteralException(e);
                 }
@@ -194,7 +192,7 @@ public Object parseLiteral(Object input) throws CoercingParseLiteralException {
                               + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
                 }
                 try {
-                    return OffsetDateTime.parse(((StringValue) input).getValue());
+                    return ((StringValue) input).getValue();
                 } catch (Exception e) {
                     throw new CoercingParseLiteralException(e);
                 }
@@ -237,7 +235,8 @@ public Object parseLiteral(Object input) throws CoercingParseLiteralException {
                               + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
                 }
                 try {
-                    return ZonedDateTime.parse(((StringValue) input).getValue());
+                    return (((StringValue) input).getValue());
+//                    return ZonedDateTime.parse(((StringValue) input).getValue());
                 } catch (Exception e) {
                     throw new CoercingParseLiteralException(e);
                 }
@@ -274,10 +273,7 @@ public Object parseValue(Object input) throws CoercingParseValueException {
 
             @Override
             public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                if (!(input instanceof StringValue)) {
-                    throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
-                              + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
-                }
+                validateLiteral(input);
                 try {
                     return LocalTime.parse(((StringValue) input).getValue());
                 } catch (Exception e) {
@@ -316,7 +312,7 @@ public Object parseValue(Object input) throws CoercingParseValueException {
 
             @Override
             public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                   return originalCoercing.parseLiteral(input);
+                   return LocalDate.parse(((StringValue) input).getValue());
             }
         })
         .name(name)
@@ -452,4 +448,17 @@ public BigInteger parseLiteral(Object input) throws CoercingParseLiteralExceptio
         .description("Custom: " + originalScalar.getDescription())
         .build();
     }
+
+    /**
+     * Validate that an input is an instance of {@link StringValue}.
+     *
+     * @param input input to validate
+     * @throws CoercingParseLiteralException if it is not a {@link StringValue}
+     */
+    private static void validateLiteral(Object input) throws CoercingParseLiteralException {
+        if (!(input instanceof StringValue)) {
+            throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
+                                                            + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
+        }
+    }
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 89f60bd9023..65afad415b0 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -349,6 +349,7 @@ protected static Object parseArgumentValue(Class originalType, String argumen
                         NumberFormat numberFormat = getCorrectNumberFormat(originalType.getName(),
                                                                            format[1], format[0]);
                         if (numberFormat != null) {
+                             LOGGER.info("Parsing [" + rawValue + "] with " + numberFormat + " " + format[1] + ":" + format[0]);
                              return getOriginalValue(originalType, numberFormat.parse(rawValue.toString()));
                         } else {
                             return rawValue;
@@ -429,8 +430,6 @@ public DateFormattingDataFetcher(String propertyName, String type, String valueF
 
         @Override
         public Object get(DataFetchingEnvironment environment) {
-            LOGGER.info("Property=" + getPropertyName() + ", Original Value=" + super.get(environment) + ", dateTimeFormatter=" + dateTimeFormatter
-                       + ", formattedResult=" + formatDate(super.get(environment), dateTimeFormatter));
             return formatDate(super.get(environment), dateTimeFormatter);
         }
     }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JsonUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JsonUtils.java
index b396c52e2f8..e4a38d70924 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JsonUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JsonUtils.java
@@ -35,6 +35,7 @@
 import static io.helidon.microprofile.graphql.server.ElementGenerator.OPEN_SQUARE;
 import static io.helidon.microprofile.graphql.server.ElementGenerator.QUOTE;
 import static io.helidon.microprofile.graphql.server.ElementGenerator.SPACER;
+import static org.eclipse.yasson.YassonConfig.ZERO_TIME_PARSE_DEFAULTING;
 
 /**
  * Various Json utilities.
@@ -45,7 +46,10 @@ public class JsonUtils {
      * JSONB instance.
      */
     private static final Jsonb JSONB = JsonbBuilder.newBuilder()
-            .withConfig(new JsonbConfig().withNullValues(true).withAdapters()).build();
+                                .withConfig(new JsonbConfig()
+                                            .setProperty(ZERO_TIME_PARSE_DEFAULTING, true)
+                                            .withNullValues(true).withAdapters())
+                                .build();
 
     /**
      * Private constructor for utilities class.
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
index 7330c12cb1f..cbfb8f6117c 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
@@ -662,6 +662,10 @@ protected static String getDefaultDescription(String[] format, String descriptio
             return null;
         }
 
+        // for the format display replace all [] (optionals) with null so TCK works
+        if (fmt != null) {
+            fmt = fmt.replaceAll("\\[", "").replaceAll("]", "");
+        }
         return description == null
                 ? fmt.trim() : fmt == null
                 ? description : description + " (" + fmt.trim() + ")";
diff --git a/microprofile/graphql/server/src/main/java/module-info.java b/microprofile/graphql/server/src/main/java/module-info.java
index e10feea8d19..4382785d85a 100644
--- a/microprofile/graphql/server/src/main/java/module-info.java
+++ b/microprofile/graphql/server/src/main/java/module-info.java
@@ -32,6 +32,7 @@
     requires graphql.java;
     requires graphql.java.extended.scalars;
     requires microprofile.graphql.api;
+    requires org.eclipse.yasson;
 
     requires transitive microprofile.config.api;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
index 95ec33580b5..81867d05fe6 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
@@ -18,7 +18,6 @@
 
 import java.io.IOException;
 
-import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -71,7 +70,7 @@ public void testDateAndTime() throws IOException {
 
         fd = getFieldDefinition(type, "localTime");
         assertThat(fd, is(notNullValue()));
-        assertThat(fd.getFormat()[0], is("hh:mm:ss"));
+        assertThat(fd.getFormat()[0], is("hh:mm[:ss]"));
         assertThat(fd.getDescription(), is(nullValue()));
         assertThat(fd.isDefaultFormatApplied(), is(false));
         assertThat(fd.getReturnType(), is(FORMATTED_TIME_SCALAR));
@@ -85,7 +84,7 @@ public void testDateAndTime() throws IOException {
 
         // test default values for date and time
         assertDefaultFormat(type, "offsetTime", "HH[:mm][:ss]Z", true);
-        assertDefaultFormat(type, "localTime", "hh:mm:ss", false);
+        assertDefaultFormat(type, "localTime", "hh:mm[:ss]", false);
         assertDefaultFormat(type, "localDateTime", "yyyy-MM-dd'T'HH[:mm][:ss]", true);
         assertDefaultFormat(type, "offsetDateTime", "yyyy-MM-dd'T'HH[:mm][:ss]Z", true);
         assertDefaultFormat(type, "zonedDateTime", "yyyy-MM-dd'T'HH[:mm][:ss]Z'['VV']'", true);
@@ -157,7 +156,8 @@ public void testDateAndTime() throws IOException {
         assertThat(fd.getReturnType(), is(DATE_SCALAR));
 
         mapResults = getAndAssertResult(
-        executionContext.execute("query { echoFormattedLocalDateWithReturnFormat(value: [ \"23-09-2020\", \"22-09-2020\" ]) }"));
+                executionContext
+                        .execute("query { echoFormattedLocalDateWithReturnFormat(value: [ \"23-09-2020\", \"22-09-2020\" ]) }"));
         assertThat(mapResults, is(notNullValue()));
         listDates = (ArrayList) mapResults.get("echoFormattedLocalDateWithReturnFormat");
         assertThat(listDates.size(), is(2));
@@ -174,7 +174,7 @@ public void testDateAndTime() throws IOException {
         assertThat(a.getFormat()[0], is("MM/dd/yyyy"));
 
         mapResults = getAndAssertResult(
-        executionContext.execute("mutation { echoFormattedDateWithJsonB(dates: [ \"09/22/2020\", \"09/23/2020\" ]) }"));
+                executionContext.execute("mutation { echoFormattedDateWithJsonB(dates: [ \"09/22/2020\", \"09/23/2020\" ]) }"));
         assertThat(mapResults, is(notNullValue()));
         listDates = (ArrayList) mapResults.get("echoFormattedDateWithJsonB");
         assertThat(listDates.size(), is(2));
@@ -182,16 +182,14 @@ public void testDateAndTime() throws IOException {
         assertThat(listDates.get(1), is("23/09/2020"));
 
         mapResults = getAndAssertResult(executionContext.execute(
-                        "query { echoOffsetDateTime(value: \"29 Jan 2020 at 09:45 in zone +0200\") }"));
+                "query { echoOffsetDateTime(value: \"29 Jan 2020 at 09:45 in zone +0200\") }"));
         assertThat(mapResults, is(notNullValue()));
         assertThat(mapResults.get("echoOffsetDateTime"), is("2020-01-29T09:45:00+0200"));
 
         mapResults = getAndAssertResult(executionContext.execute(
-                        "query { echoZonedDateTime(value: \"19 February 1900 at 12:00 in Africa/Johannesburg\") }"));
+                "query { echoZonedDateTime(value: \"19 February 1900 at 12:00 in Africa/Johannesburg\") }"));
         assertThat(mapResults, is(notNullValue()));
         assertThat(mapResults.get("echoZonedDateTime"), is("1900-02-19T12:00:00+0130[Africa/Johannesburg]"));
-
-
     }
 
     @Test
@@ -244,12 +242,55 @@ public void testDatesAndMutations() throws IOException {
         mapResults = getAndAssertResult(
                 executionContext.execute("mutation { testDefaultFormatLocalDateTime(dateTime: \"2020-01-12T10:00:00\") }"));
         assertThat(mapResults.size(), is(1));
-        assertThat(mapResults.get("testDefaultFormatLocalDateTime"), is( "10:00:00 12-01-2020"));
-        
+        assertThat(mapResults.get("testDefaultFormatLocalDateTime"), is("10:00:00 12-01-2020"));
+
         mapResults = getAndAssertResult(
                 executionContext.execute("query { transformedDate }"));
         assertThat(mapResults, is(notNullValue()));
         assertThat(mapResults.get("transformedDate"), is("16 Aug. 2016"));
     }
 
+    @Test
+    public void testDateInputsAsPojo() throws IOException {
+        setupIndex(indexFileName, DateTimePojo.class, SimpleQueriesAndMutations.class);
+        ExecutionContext executionContext = new ExecutionContext(defaultContext);
+
+        validateResult(executionContext, "query { echoDateTimePojo ( "
+                                                  + " value: { localDate: \"02/17/1968\" "
+                                                 + "}) { localDate } }",
+                       "localDate", "02/17/1968");
+
+        validateResult(executionContext, "query { echoDateTimePojo ( "
+                                                  + " value: { localDate2: \"02/17/1968\" "
+                                                 + "}) { localDate2 } }",
+                       "localDate2", "02/17/1968");
+        
+       validateResult(executionContext, "query { echoDateTimePojo ( "
+                                                  + " value: { offsetDateTime: \"1968-02-17T10:12:23+0200\" "
+                                                 + "}) { offsetDateTime } }",
+                       "offsetDateTime", "1968-02-17T10:12:23+0200");
+
+       validateResult(executionContext, "query { echoDateTimePojo ( "
+                                                  + " value: { zonedDateTime: \"1968-02-17T10:12:23+0200[Africa/Johannesburg]\" "
+                                                 + "}) { zonedDateTime } }",
+                       "zonedDateTime", "1968-02-17T10:12:23+0200[Africa/Johannesburg]");
+
+        // TODO: Fixup LocalTime
+//        validateResult(executionContext, "query { echoDateTimePojo ( "
+//                                                  + " value: { localTime: \"10:00:00\" "
+//                                                 + "}) { localTime } }",
+//                       "localTime", "10:00:00");
+
+    }
+
+    @SuppressWarnings("unchecked")
+    private void validateResult(ExecutionContext executionContext, String query, String field, Object expectedResult) {
+           Map mapResults = getAndAssertResult(
+                executionContext.execute(query));
+        assertThat(mapResults, is(notNullValue()));
+        Map mapResults2 = (Map) mapResults.get("echoDateTimePojo");
+        assertThat(mapResults2, is(notNullValue()));
+        assertThat(mapResults2.get(field), is(expectedResult));
+    }
+
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
index 4ab0b717b8c..59d6e460b09 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
@@ -63,7 +63,6 @@ public void setOddNamedQueriesAndMutations() throws IOException {
         assertThat(mutation.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("getaway")).count(), is(1L));
     }
 
-
     @Test
     @SuppressWarnings("unchecked")
     public void testDefaultValues() throws IOException {
@@ -103,7 +102,9 @@ public void testDefaultValues() throws IOException {
         assertThat(fd, is(notNullValue()));
         SchemaArgument argument = fd.getArguments().get(0);
         assertThat(argument, is(notNullValue()));
-        assertThat(argument.getDefaultValue(), is("{ \"id\": \"ID-1\", \"value\": 1000, \"booleanValue\": true, \"dateObject\": \"1968-02-17\"}"));
+        assertThat(argument.getDefaultValue(), is(
+                "{ \"id\": \"ID-1\", \"value\": 1000, \"booleanValue\": true, \"dateObject\": \"1968-02-17\","
+                 + " \"formattedIntWithDefault\": \"2 value\", \"offsetDateTime\": \"29 Jan 2020 at 09:45 in zone +0200\"}"));
 
         mapResults = getAndAssertResult(
                 executionContext.execute("query { echoDefaultValuePOJO(input: {id: \"X123\" value: 1}) { id value } }"));
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java
index b3112d7715d..3d4409e80cb 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java
@@ -39,7 +39,9 @@ public class DefaultValueQueries {
             + " \"id\": \"ID-1\","
             + " \"value\": 1000,"
             + " \"booleanValue\": true,"
-            + " \"dateObject\": \"1968-02-17\""
+            + " \"dateObject\": \"1968-02-17\","
+            + " \"formattedIntWithDefault\": \"2 value\","
+            + " \"offsetDateTime\": \"29 Jan 2020 at 09:45 in zone +0200\""
             + "}";
             
     @Inject
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
index ef2ec6cb64c..cc7c495fdca 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
@@ -172,6 +172,11 @@ public LocalDate transformedDate() {
         return LocalDate.parse(date);
     }
 
+    @Query
+    public DateTimePojo echoDateTimePojo(@Name("value") DateTimePojo dateTimePojo) {
+        return dateTimePojo;
+    }
+
     @Query("localDateNoFormat")
     public LocalDate localDateNoFormat() {
         return LocalDate.of(1968, 02, 17);
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java
index 635b8dd903d..da127e8a271 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java
@@ -44,20 +44,28 @@ public class DateTimePojo {
     @DateFormat("MM/dd/yyyy")
     private LocalDate localDate2;
 
-    @JsonbDateFormat("hh:mm:ss")
+    @DateFormat("hh:mm[:ss]")
     private LocalTime localTime;
+    
     private OffsetTime offsetTime;
+
     private LocalDateTime localDateTime;
+
     private OffsetDateTime offsetDateTime;
+    
     private ZonedDateTime zonedDateTime;
     @Description("description")
     private LocalDate localDateNoFormat;
+
     private LocalTime localTimeNoFormat;
 
     private List significantDates;
 
     private List formattedListOfDates;
 
+    public DateTimePojo() {
+    }
+    
     public DateTimePojo(LocalDate localDate,
                         LocalDate localDate2,
                         LocalTime localTime,
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DefaultValuePOJO.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DefaultValuePOJO.java
index c0c3d50f8e5..3d4207880cb 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DefaultValuePOJO.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DefaultValuePOJO.java
@@ -17,9 +17,12 @@
 package io.helidon.microprofile.graphql.server.test.types;
 
 import java.time.LocalDate;
+import java.time.OffsetDateTime;
 
+import javax.json.bind.annotation.JsonbDateFormat;
 import org.eclipse.microprofile.graphql.DefaultValue;
 import org.eclipse.microprofile.graphql.Description;
+import org.eclipse.microprofile.graphql.NumberFormat;
 import org.eclipse.microprofile.graphql.Type;
 
 /**
@@ -30,15 +33,22 @@ public class DefaultValuePOJO {
 
     @DefaultValue("ID-123")
     private String id;
-    
+
     private int value;
 
+    @DefaultValue("1 value")
+    @NumberFormat("0 'value'")
+    private int formattedIntWithDefault;
+
     @DefaultValue("1978-07-03")
     private LocalDate dateObject;
 
     @DefaultValue("false")
     boolean booleanValue;
 
+    @JsonbDateFormat(value = "dd MMM yyyy 'at' HH:mm 'in zone' Z", locale = "en-ZA")
+    private OffsetDateTime offsetDateTime;
+
     public DefaultValuePOJO() {
     }
 
@@ -79,4 +89,20 @@ public boolean isBooleanValue() {
     public void setBooleanValue(boolean booleanValue) {
         this.booleanValue = booleanValue;
     }
+
+    public OffsetDateTime getOffsetDateTime() {
+        return offsetDateTime;
+    }
+
+    public void setOffsetDateTime(OffsetDateTime offsetDateTime) {
+        this.offsetDateTime = offsetDateTime;
+    }
+
+    public int getFormattedIntWithDefault() {
+        return formattedIntWithDefault;
+    }
+
+    public void setFormattedIntWithDefault(int formattedIntWithDefault) {
+        this.formattedIntWithDefault = formattedIntWithDefault;
+    }
 }

From 6bec90eca753279d9af9812e80385264e84e7db0 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Fri, 9 Oct 2020 14:04:02 +0800
Subject: [PATCH 109/178] Fixup custom scalars

---
 .../graphql/server/CustomScalars.java         | 427 ++++++++----------
 .../graphql/server/SchemaGenerator.java       |   4 +-
 .../graphql/server/DateTimeIT.java            |  39 +-
 3 files changed, 216 insertions(+), 254 deletions(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
index 024a79d0505..85ebaa8bf39 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
@@ -100,7 +100,8 @@ private CustomScalars() {
     /**
      * An instance of a custom offset date/time scalar (with default formatting).
      */
-    public static final GraphQLScalarType CUSTOM_OFFSET_DATE_TIME_SCALAR = newOffsetDateTimeScalar(FORMATTED_OFFSET_DATETIME_SCALAR);
+    public static final GraphQLScalarType CUSTOM_OFFSET_DATE_TIME_SCALAR = newOffsetDateTimeScalar(
+            FORMATTED_OFFSET_DATETIME_SCALAR);
 
     /**
      * An instance of a custom offset date/time scalar (with default formatting).
@@ -121,130 +122,50 @@ private CustomScalars() {
      * Return a new custom date/time scalar.
      *
      * @param name the name of the scalar
-     *
      * @return a new custom date/time scalar
      */
-    @SuppressWarnings("unchecked")
     public static GraphQLScalarType newDateTimeScalar(String name) {
         GraphQLScalarType originalScalar = ExtendedScalars.DateTime;
-        Coercing originalCoercing = originalScalar.getCoercing();
-        return GraphQLScalarType.newScalar().coercing(new Coercing() {
-            public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
-                if (dataFetcherResult instanceof String) {
-                    return (String) dataFetcherResult;
-                } else {
-                    return originalCoercing.serialize(dataFetcherResult);
-                }
-            }
 
-            @Override
-            public Object parseValue(Object input) throws CoercingParseValueException {
-                return input instanceof StringValue ? ((StringValue) input).getValue()
-                        : originalCoercing.parseValue(input);
-            }
-
-            @Override
-            public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                validateLiteral(input);
-                try {
-                    return ((StringValue) input).getValue();
-//                    return LocalDateTime.parse(((StringValue) input).getValue());
-                } catch (Exception e) {
-                    throw new CoercingParseLiteralException(e);
-                }
-            }
-        })
-        .name(name)
-        .description("Custom: " + originalScalar.getDescription())
-        .build();
+        return GraphQLScalarType.newScalar()
+                .coercing(new DateTimeCoercing())
+                .name(name)
+                .description("Custom: " + originalScalar.getDescription())
+                .build();
     }
 
     /**
      * Return a new custom offset date/time scalar.
      *
      * @param name the name of the scalar
-     *
      * @return a new custom date/time scalar
      */
     @SuppressWarnings("unchecked")
     public static GraphQLScalarType newOffsetDateTimeScalar(String name) {
         GraphQLScalarType originalScalar = ExtendedScalars.DateTime;
-        Coercing originalCoercing = originalScalar.getCoercing();
-        return GraphQLScalarType.newScalar().coercing(new Coercing() {
-            public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
-                if (dataFetcherResult instanceof String) {
-                    return (String) dataFetcherResult;
-                } else {
-                    return originalCoercing.serialize(dataFetcherResult);
-                }
-            }
-
-            @Override
-            public Object parseValue(Object input) throws CoercingParseValueException {
-                return input instanceof StringValue ? ((StringValue) input).getValue()
-                        : originalCoercing.parseValue(input);
-            }
 
-            @Override
-            public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                if (!(input instanceof StringValue)) {
-                    throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
-                              + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
-                }
-                try {
-                    return ((StringValue) input).getValue();
-                } catch (Exception e) {
-                    throw new CoercingParseLiteralException(e);
-                }
-            }
-        })
-        .name(name)
-        .description("Custom: " + originalScalar.getDescription())
-        .build();
+        return GraphQLScalarType.newScalar()
+                .coercing(new DateTimeCoercing())
+                .name(name)
+                .description("Custom: " + originalScalar.getDescription())
+                .build();
     }
+
     /**
      * Return a new custom zoned date/time scalar.
      *
      * @param name the name of the scalar
-     *
      * @return a new custom date/time scalar
      */
     @SuppressWarnings("unchecked")
     public static GraphQLScalarType newZonedDateTimeScalar(String name) {
         GraphQLScalarType originalScalar = ExtendedScalars.DateTime;
-        Coercing originalCoercing = originalScalar.getCoercing();
-        return GraphQLScalarType.newScalar().coercing(new Coercing() {
-            public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
-                if (dataFetcherResult instanceof String) {
-                    return (String) dataFetcherResult;
-                } else {
-                    return originalCoercing.serialize(dataFetcherResult);
-                }
-            }
-
-            @Override
-            public Object parseValue(Object input) throws CoercingParseValueException {
-                return input instanceof StringValue ? ((StringValue) input).getValue()
-                        : originalCoercing.parseValue(input);
-            }
 
-            @Override
-            public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                if (!(input instanceof StringValue)) {
-                    throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
-                              + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
-                }
-                try {
-                    return (((StringValue) input).getValue());
-//                    return ZonedDateTime.parse(((StringValue) input).getValue());
-                } catch (Exception e) {
-                    throw new CoercingParseLiteralException(e);
-                }
-            }
-        })
-        .name(name)
-        .description("Custom: " + originalScalar.getDescription())
-        .build();
+        return GraphQLScalarType.newScalar()
+                .coercing(new DateTimeCoercing())
+                .name(name)
+                .description("Custom: " + originalScalar.getDescription())
+                .build();
     }
 
     /**
@@ -253,71 +174,30 @@ public Object parseLiteral(Object input) throws CoercingParseLiteralException {
      * @param name the name of the scalar
      * @return a new custom time scalar
      */
-    @SuppressWarnings("unchecked")
     public static GraphQLScalarType newTimeScalar(String name) {
         GraphQLScalarType originalScalar = ExtendedScalars.Time;
-        Coercing originalCoercing = originalScalar.getCoercing();
-        return GraphQLScalarType.newScalar().coercing(new Coercing() {
-            public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
-                return dataFetcherResult instanceof String
-                        ? (String) dataFetcherResult
-                        : originalCoercing.serialize(dataFetcherResult);
-            }
-
-            @Override
-            public Object parseValue(Object input) throws CoercingParseValueException {
-                return input instanceof StringValue ? ((StringValue) input).getValue()
-                        : originalCoercing.parseValue(input);
-            }
-
 
-            @Override
-            public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                validateLiteral(input);
-                try {
-                    return LocalTime.parse(((StringValue) input).getValue());
-                } catch (Exception e) {
-                    throw new CoercingParseLiteralException(e);
-                }
-            }
-        })
-        .name(name)
-        .description("Custom: " + originalScalar.getDescription())
-        .build();
+        return GraphQLScalarType.newScalar()
+                .coercing(new TimeCoercing())
+                .name(name)
+                .description("Custom: " + originalScalar.getDescription())
+                .build();
     }
 
     /**
      * Return a new custom date scalar.
      *
      * @param name the name of the scalar
-     *
      * @return a new custom date scalar
      */
     @SuppressWarnings("unchecked")
     public static GraphQLScalarType newDateScalar(String name) {
         GraphQLScalarType originalScalar = ExtendedScalars.Date;
-        Coercing originalCoercing = originalScalar.getCoercing();
-        return GraphQLScalarType.newScalar().coercing(new Coercing() {
-            public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
-                return dataFetcherResult instanceof String
-                        ? (String) dataFetcherResult
-                        : originalCoercing.serialize(dataFetcherResult);
-            }
-
-            @Override
-            public Object parseValue(Object input) throws CoercingParseValueException {
-                return input instanceof StringValue ? ((StringValue) input).getValue()
-                        : originalCoercing.parseValue(input);
-            }
-
-            @Override
-            public Object parseLiteral(Object input) throws CoercingParseLiteralException {
-                   return LocalDate.parse(((StringValue) input).getValue());
-            }
-        })
-        .name(name)
-        .description("Custom: " + originalScalar.getDescription())
-        .build();
+        return GraphQLScalarType.newScalar()
+                .coercing(new DateTimeCoercing())
+                .name(name)
+                .description("Custom: " + originalScalar.getDescription())
+                .build();
     }
 
     /**
@@ -328,29 +208,12 @@ public Object parseLiteral(Object input) throws CoercingParseLiteralException {
     @SuppressWarnings("unchecked")
     private static GraphQLScalarType newCustomBigDecimalScalar() {
         GraphQLScalarType originalScalar = Scalars.GraphQLBigDecimal;
-        Coercing originalCoercing = originalScalar.getCoercing();
-
-        return GraphQLScalarType.newScalar().coercing(new Coercing<>() {
-            @Override
-            public Object serialize(Object dataFetcherResult) throws CoercingSerializeException {
-                return dataFetcherResult instanceof String
-                        ? (String) dataFetcherResult
-                        : originalCoercing.serialize(dataFetcherResult);
-            }
-
-            @Override
-            public BigDecimal parseValue(Object input) throws CoercingParseValueException {
-                return originalCoercing.parseValue(input);
-            }
 
-            @Override
-            public BigDecimal parseLiteral(Object input) throws CoercingParseLiteralException {
-                return originalCoercing.parseLiteral(input);
-            }
-        })
-        .name(originalScalar.getName())
-        .description("Custom: " + originalScalar.getDescription())
-        .build();
+        return GraphQLScalarType.newScalar()
+                .coercing(new NumberCoercing(originalScalar.getCoercing()))
+                .name(originalScalar.getName())
+                .description("Custom: " + originalScalar.getDescription())
+                .build();
     }
 
     /**
@@ -361,28 +224,12 @@ public BigDecimal parseLiteral(Object input) throws CoercingParseLiteralExceptio
     @SuppressWarnings("unchecked")
     private static GraphQLScalarType newCustomGraphQLInt() {
         GraphQLScalarType originalScalar = GraphQLInt;
-        Coercing originalCoercing = originalScalar.getCoercing();
-        return GraphQLScalarType.newScalar().coercing(new Coercing<>() {
-            @Override
-            public Object serialize(Object dataFetcherResult) throws CoercingSerializeException {
-                return dataFetcherResult instanceof String
-                        ? (String) dataFetcherResult
-                        : originalCoercing.serialize(dataFetcherResult);
-            }
 
-            @Override
-            public Integer parseValue(Object input) throws CoercingParseValueException {
-                return originalCoercing.parseValue(input);
-            }
-
-            @Override
-            public Integer parseLiteral(Object input) throws CoercingParseLiteralException {
-                return originalCoercing.parseLiteral(input);
-            }
-        })
-        .name(originalScalar.getName())
-        .description("Custom: " + originalScalar.getDescription())
-        .build();
+        return GraphQLScalarType.newScalar()
+                .coercing(new NumberCoercing(originalScalar.getCoercing()))
+                .name(originalScalar.getName())
+                .description("Custom: " + originalScalar.getDescription())
+                .build();
     }
 
     /**
@@ -393,28 +240,12 @@ public Integer parseLiteral(Object input) throws CoercingParseLiteralException {
     @SuppressWarnings("unchecked")
     private static GraphQLScalarType newCustomGraphQLFloat() {
         GraphQLScalarType originalScalar = GraphQLFloat;
-        Coercing originalCoercing = originalScalar.getCoercing();
-        return GraphQLScalarType.newScalar().coercing(new Coercing<>() {
-            @Override
-            public Object serialize(Object dataFetcherResult) throws CoercingSerializeException {
-                return dataFetcherResult instanceof String
-                        ? (String) dataFetcherResult
-                        : originalCoercing.serialize(dataFetcherResult);
-            }
-
-            @Override
-            public Double parseValue(Object input) throws CoercingParseValueException {
-                return originalCoercing.parseValue(input);
-            }
 
-            @Override
-            public Double parseLiteral(Object input) throws CoercingParseLiteralException {
-                return originalCoercing.parseLiteral(input);
-            }
-        })
-        .name(originalScalar.getName())
-        .description("Custom: " + originalScalar.getDescription())
-        .build();
+        return GraphQLScalarType.newScalar()
+                .coercing(new NumberCoercing(originalScalar.getCoercing()))
+                .name(originalScalar.getName())
+                .description("Custom: " + originalScalar.getDescription())
+                .build();
     }
 
     /**
@@ -425,40 +256,162 @@ public Double parseLiteral(Object input) throws CoercingParseLiteralException {
     @SuppressWarnings("unchecked")
     private static GraphQLScalarType newCustomGraphQLBigInteger() {
         GraphQLScalarType originalScalar = GraphQLBigInteger;
-        Coercing originalCoercing = originalScalar.getCoercing();
-        return GraphQLScalarType.newScalar().coercing(new Coercing<>() {
-            @Override
-            public Object serialize(Object dataFetcherResult) throws CoercingSerializeException {
-                return dataFetcherResult instanceof String
-                        ? (String) dataFetcherResult
-                        : originalCoercing.serialize(dataFetcherResult);
+
+        return GraphQLScalarType.newScalar()
+                .coercing(new NumberCoercing(originalScalar.getCoercing()))
+                .name(originalScalar.getName())
+                .description("Custom: " + originalScalar.getDescription())
+                .build();
+    }
+
+    /**
+     * Abstract implementation of {@link Coercing} interface for given classes.
+     */
+    public abstract static class AbstractDateTimeCoercing implements Coercing {
+
+        /**
+         * {@link Class}es that can be coerced.
+         */
+        private final Class[] clazzes;
+
+        /**
+         * Construct a {@link AbstractDateTimeCoercing}.
+         *
+         * @param clazzes {@link Class}es to coerce
+         */
+        public AbstractDateTimeCoercing(Class... clazzes) {
+            this.clazzes = clazzes;
+        }
+
+        @Override
+        public Object serialize(Object dataFetcherResult) throws CoercingSerializeException {
+            return convert(dataFetcherResult);
+        }
+
+        @Override
+        public Object parseValue(Object input) throws CoercingParseValueException {
+            return convert(input);
+        }
+
+        @Override
+        public Object parseLiteral(Object input) throws CoercingParseLiteralException {
+            return parseStringLiteral(input);
+        }
+
+        /**
+         * Convert the given input to the type of if a String then leave it be.
+         *
+         * @param input input to coerce
+         * @return the coerced value
+         * @throws CoercingParseLiteralException if any exceptions converting
+         */
+        private Object convert(Object input) throws CoercingParseLiteralException {
+            if (input instanceof String) {
+                return (String) input;
             }
 
-            @Override
-            public BigInteger parseValue(Object input) throws CoercingParseValueException {
-                return originalCoercing.parseValue(input);
+            for (Class clazz : clazzes) {
+                if (input.getClass().isInstance(clazz)) {
+                    return clazz.cast(input);
+                }
             }
 
-            @Override
-            public BigInteger parseLiteral(Object input) throws CoercingParseLiteralException {
-                return originalCoercing.parseLiteral(input);
+            throw new CoercingParseLiteralException("Unable to convert type of " + input.getClass());
+        }
+
+        /**
+         * Parse a String literal and return instance of {@link StringValue} or throw an exception.
+         *
+         * @param input input to parse
+         * @throws CoercingParseLiteralException if it is not a {@link StringValue}
+         */
+        private String parseStringLiteral(Object input) throws CoercingParseLiteralException {
+            if (!(input instanceof StringValue)) {
+                throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
+                                                                + (
+                        input == null
+                                ? "null"
+                                : input.getClass().getSimpleName()) + "'.");
             }
-        })
-        .name(originalScalar.getName())
-        .description("Custom: " + originalScalar.getDescription())
-        .build();
+            return ((StringValue) input).getValue();
+        }
     }
 
     /**
-     * Validate that an input is an instance of {@link StringValue}.
-     *
-     * @param input input to validate
-     * @throws CoercingParseLiteralException if it is not a {@link StringValue}
+     * Coercing Implementation for Date/Time.
+     */
+    public static class DateTimeCoercing extends AbstractDateTimeCoercing {
+
+        /**
+         * Construct a {@link DateTimeCoercing}.
+         */
+        public DateTimeCoercing() {
+            super(LocalDateTime.class, OffsetDateTime.class, ZonedDateTime.class);
+        }
+    }
+
+    /**
+     * Coercing implementation for Time.
      */
-    private static void validateLiteral(Object input) throws CoercingParseLiteralException {
-        if (!(input instanceof StringValue)) {
-            throw new CoercingParseLiteralException("Expected AST type 'StringValue' but was '"
-                                                            + (input == null ? "null" : input.getClass().getSimpleName()) + "'.");
+    public static class TimeCoercing extends AbstractDateTimeCoercing {
+
+        /**
+         * Construct a {@link TimeCoercing}.
+         */
+        public TimeCoercing() {
+            super(LocalTime.class, OffsetTime.class);
+        }
+    }
+
+    /**
+     * Coercing implementation for Date.
+     */
+    public static class DateCoercing extends AbstractDateTimeCoercing {
+
+        /**
+         * Construct a {@link DateCoercing}.
+         */
+        public DateCoercing() {
+            super(LocalDate.class);
+        }
+    }
+
+    /**
+     * Coercing implementation for BigDecimal.
+     */
+    public static class BigDecimalCoercing extends AbstractDateTimeCoercing {
+
+        /**
+         * Construct a {@link DateCoercing}.
+         */
+        public BigDecimalCoercing() {
+            super(BigDecimal.class);
+        }
+    }
+
+    public static class NumberCoercing implements Coercing {
+
+        private final Coercing originalCoercing;
+
+        public NumberCoercing(Coercing originalCoercing) {
+            this.originalCoercing = originalCoercing;
+        }
+
+        @Override
+        public Object serialize(Object dataFetcherResult) throws CoercingSerializeException {
+            return dataFetcherResult instanceof String
+                    ? (String) dataFetcherResult
+                    : originalCoercing.serialize(dataFetcherResult);
+        }
+
+        @Override
+        public I parseValue(Object input) throws CoercingParseValueException {
+            return (I) originalCoercing.parseValue(input);
+        }
+
+        @Override
+        public I parseLiteral(Object input) throws CoercingParseLiteralException {
+            return (I) originalCoercing.parseLiteral(input);
         }
     }
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index 9792ad856e4..c5eec152444 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -415,7 +415,9 @@ private void processDefaultDateTimeValues(Schema schema) {
                     String argumentType = a.getArgumentType();
                     if (isDateTimeScalar(argumentType)) {
                         String[] existingArgFormat = a.getFormat();
-                        String[] newArgFormat = ensureFormat(argumentType, a.getOriginalType().getName(), existingArgFormat);
+                        Class clazzOriginalType = a.getOriginalArrayType() != null
+                            ? a.getOriginalArrayType() : a.getOriginalType();
+                        String[] newArgFormat = ensureFormat(argumentType, clazzOriginalType.getName(), existingArgFormat);
                         if (!Arrays.equals(newArgFormat, existingArgFormat) && newArgFormat.length == 2) {
                             a.setFormat(newArgFormat);
                         }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
index 81867d05fe6..3b66c9bb817 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
@@ -256,36 +256,43 @@ public void testDateInputsAsPojo() throws IOException {
         ExecutionContext executionContext = new ExecutionContext(defaultContext);
 
         validateResult(executionContext, "query { echoDateTimePojo ( "
-                                                  + " value: { localDate: \"02/17/1968\" "
-                                                 + "}) { localDate } }",
+                               + " value: { localDate: \"02/17/1968\" "
+                               + "}) { localDate } }",
                        "localDate", "02/17/1968");
 
         validateResult(executionContext, "query { echoDateTimePojo ( "
-                                                  + " value: { localDate2: \"02/17/1968\" "
-                                                 + "}) { localDate2 } }",
+                               + " value: { localDate2: \"02/17/1968\" "
+                               + "}) { localDate2 } }",
                        "localDate2", "02/17/1968");
-        
-       validateResult(executionContext, "query { echoDateTimePojo ( "
-                                                  + " value: { offsetDateTime: \"1968-02-17T10:12:23+0200\" "
-                                                 + "}) { offsetDateTime } }",
+
+        validateResult(executionContext, "query { echoDateTimePojo ( "
+                               + " value: { offsetDateTime: \"1968-02-17T10:12:23+0200\" "
+                               + "}) { offsetDateTime } }",
                        "offsetDateTime", "1968-02-17T10:12:23+0200");
 
-       validateResult(executionContext, "query { echoDateTimePojo ( "
-                                                  + " value: { zonedDateTime: \"1968-02-17T10:12:23+0200[Africa/Johannesburg]\" "
-                                                 + "}) { zonedDateTime } }",
+        validateResult(executionContext, "query { echoDateTimePojo ( "
+                               + " value: { zonedDateTime: \"1968-02-17T10:12:23+0200[Africa/Johannesburg]\" "
+                               + "}) { zonedDateTime } }",
                        "zonedDateTime", "1968-02-17T10:12:23+0200[Africa/Johannesburg]");
 
-        // TODO: Fixup LocalTime
+
+        List listResults = List.of("1968-02-17", "1968-02-18");
+        validateResult(executionContext, "query { echoDateTimePojo ( "
+                       + " value: { significantDates: [\"1968-02-17\", \"1968-02-18\" ]"
+                       + "}) { significantDates } }",
+               "significantDates", List.of("1968-02-17", "1968-02-18"));
+
+        // TODO: Fix
 //        validateResult(executionContext, "query { echoDateTimePojo ( "
-//                                                  + " value: { localTime: \"10:00:00\" "
-//                                                 + "}) { localTime } }",
-//                       "localTime", "10:00:00");
+//                               + " value: { localTime: \"10:22:00\" "
+//                               + "}) { localTime } }",
+//                       "localTime", "10:22:00");
 
     }
 
     @SuppressWarnings("unchecked")
     private void validateResult(ExecutionContext executionContext, String query, String field, Object expectedResult) {
-           Map mapResults = getAndAssertResult(
+        Map mapResults = getAndAssertResult(
                 executionContext.execute(query));
         assertThat(mapResults, is(notNullValue()));
         Map mapResults2 = (Map) mapResults.get("echoDateTimePojo");

From be64c67d38d485f13a51a66021d84c59970bd6f4 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Tue, 13 Oct 2020 08:22:36 +0800
Subject: [PATCH 110/178] Fixup tck test kidsAges

---
 .../microprofile/graphql/server/DataFetcherUtils.java  |  4 ++--
 .../microprofile/graphql/server/SchemaGenerator.java   |  5 +++++
 .../microprofile/graphql/server/NumberFormatIT.java    |  1 +
 .../test/types/SimpleContactWithNumberFormats.java     | 10 ++++++++++
 4 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 65afad415b0..18de65b96a4 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -168,8 +168,8 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
                         // don't deserialize using formatting as JsonB will do this for us
                         mapConverted.put(fdName, value);
                     } else {
-                        mapConverted.put(fdName, parseArgumentValue(fd.getOriginalType(), fd.getReturnType(),
-                                                                    value, fd.getFormat()));
+                          mapConverted.put(fdName, generateArgumentValue(schema, fd.getReturnType(), fd.getOriginalType(),
+                                                                         fd.getOriginalArrayType(), value, fd.getFormat()));
                     }
                 }
             }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index c5eec152444..112bdbcb581 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -1149,6 +1149,11 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
             if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) {
                 // only set the original array type if it's a date/time
                 discoveredMethod.setOriginalArrayType(Class.forName(realReturnType.returnClass));
+            } else if (discoveredMethod.isArrayReturnType) {
+               Class originalArrayType = getSafeClass(realReturnType.returnClass);
+               if (originalArrayType != null) {
+                   discoveredMethod.setOriginalArrayType(originalArrayType);
+               }
             }
             discoveredMethod.setReturnType(realReturnType.getReturnClass());
             if (!isFormatEmpty(realReturnType.getFormat())) {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
index 660945ee3b2..76e6ce8bf1a 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
@@ -122,6 +122,7 @@ public void testNumberFormats() throws IOException {
                         + "value: \"9 value\" "
                         + "longValue: \"LongValue-123\""
                         + "bigDecimal: \"BigDecimal-12345\""
+                        + "listOfIntegers: [ \"1 number\", \"2 number\"]"
                         + " } ";
 
         mapResults = getAndAssertResult(
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java
index 65e4f9d8103..56e036179b8 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java
@@ -52,6 +52,8 @@ public class SimpleContactWithNumberFormats {
 
     private List<@DateFormat("DD-MM-YYYY") LocalDate> listDates;
 
+    private List listOfIntegers;
+
     public Integer getFormatMethod(@NumberFormat("0 'years old'") int age) {
         return age;
     }
@@ -146,6 +148,14 @@ public void setBigDecimal(BigDecimal bigDecimal) {
         this.bigDecimal = bigDecimal;
     }
 
+    public List<@NumberFormat("0 'number'") Integer> getListOfIntegers() {
+        return listOfIntegers;
+    }
+
+    public void setListOfIntegers(List<@NumberFormat("0 'number'") Integer> listOfIntegers) {
+        this.listOfIntegers = listOfIntegers;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {

From 5b776cfd0c53ece946e233701c28462947c5df89 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Tue, 13 Oct 2020 08:51:18 +0800
Subject: [PATCH 111/178] additional fix for list Integer

---
 .../helidon/microprofile/graphql/server/FormattingHelper.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
index 07c1e94a426..f7ad54927ea 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
@@ -464,7 +464,7 @@ public static Object formatNumber(Object originalResult, boolean isScalar, Numbe
         if (originalResult instanceof Collection) {
             Collection formattedResult = new ArrayList();
             Collection originalCollection = (Collection) originalResult;
-            originalCollection.forEach(e -> formattedResult.add(numberFormat.format(originalResult)));
+            originalCollection.forEach(e -> formattedResult.add(numberFormat.format(e)));
             LOGGER.info("Formatted result = " + formattedResult);
             return formattedResult;
         }

From 487bebe8bd57d56916b866268f13d0aea36c1cc2 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Tue, 13 Oct 2020 15:46:44 +0800
Subject: [PATCH 112/178] Fixup nulls

---
 microprofile/graphql/server/pom.xml           |  4 --
 .../graphql/server/DataFetcherUtils.java      |  9 ++-
 .../graphql/server/SchemaFieldDefinition.java | 33 +++++++++--
 .../graphql/server/SchemaGenerator.java       | 57 ++++++++++++++++---
 .../graphql/server/DateTimeIT.java            | 20 +++++++
 .../queries/SimpleQueriesAndMutations.java    |  6 ++
 .../server/test/types/SimpleDateTime.java     | 48 ++++++++++++++++
 7 files changed, 159 insertions(+), 18 deletions(-)
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTime.java

diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml
index 932e42878c6..b02c1e745f7 100644
--- a/microprofile/graphql/server/pom.xml
+++ b/microprofile/graphql/server/pom.xml
@@ -56,10 +56,6 @@
             io.helidon.microprofile.config
             helidon-microprofile-config
         
-
-
-
-
         
             jakarta.el-api
             jakarta.el
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 18de65b96a4..9b333c651b7 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -164,10 +164,15 @@ protected static Object generateArgumentValue(Schema schema, String argumentType
                                                                    null,
                                                                    value, EMPTY_FORMAT));
                 } else {
-                    if (fd.isJsonbFormat()) {
-                        // don't deserialize using formatting as JsonB will do this for us
+                    if (fd.isJsonbFormat() || fd.isJsonbProperty()) {
+                        // don't deserialize using formatting as Jsonb will do this for us
                         mapConverted.put(fdName, value);
                     } else {
+                          // retrieve the data fetcher and check if the property name is different as this should be used
+                          DataFetcher dataFetcher = fd.getDataFetcher();
+                          if (dataFetcher instanceof PropertyDataFetcher) {
+                              fdName = ((PropertyDataFetcher) dataFetcher).getPropertyName();
+                          }
                           mapConverted.put(fdName, generateArgumentValue(schema, fd.getReturnType(), fd.getOriginalType(),
                                                                          fd.getOriginalArrayType(), value, fd.getFormat()));
                     }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java
index 95a258f96a7..9b4ce9dc902 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java
@@ -96,10 +96,16 @@ public class SchemaFieldDefinition
     private boolean defaultFormatApplied;
 
     /**
-     * Indicates if the format is of type JsonB.
+     * Indicates if the format is of type Jsonb.
      */
     private boolean isJsonbFormat;
 
+    /**
+     * Indicates if the property name is of type Jsonb.
+     */
+    private boolean isJsonbProperty;
+
+
     /**
      * Construct a {@link SchemaFieldDefinition}.
      *
@@ -355,21 +361,37 @@ public boolean isDefaultFormatApplied() {
     }
 
     /**
-     * Set if the format is of type JsonB.
-     * @param isJsonbFormat if the format is of type JsonB
+     * Set if the format is of type Jsonb.
+     * @param isJsonbFormat if the format is of type Jsonb
      */
     public void setJsonbFormat(boolean isJsonbFormat) {
         this.isJsonbFormat = isJsonbFormat;
     }
 
     /**
-     * Returns true if the format is of type JsonB.
-     * @return true if the format is of type JsonB
+     * Returns true if the format is of type Jsonb.
+     * @return true if the format is of type Jsonb
      */
     public boolean isJsonbFormat() {
         return isJsonbFormat;
     }
 
+    /**
+     * Sets if the property has a JsonbProperty annotation.
+     *
+     * @param isJsonbProperty if the property has a JsonbProperty annotation
+     */
+    public void setJsonbProperty(boolean isJsonbProperty) {
+        this.isJsonbProperty = isJsonbProperty;
+    }
+
+    /**
+     * Indicates if the property has a JsonbProperty annotation.
+     */
+    public boolean isJsonbProperty() {
+        return isJsonbProperty;
+    }
+
     @Override
     public String toString() {
         return "FieldDefinition{"
@@ -385,6 +407,7 @@ public String toString() {
                 + ", originalArrayType=" + originalArrayType
                 + ", format=" + Arrays.toString(format)
                 + ", isJsonbFormat=" + isJsonbFormat
+                + ", isJsonbProperty=" + isJsonbProperty
                 + ", description='" + getDescription() + '\'' + '}';
     }
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index 112bdbcb581..3395f4bc832 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -43,11 +43,13 @@
 import java.util.stream.Stream;
 
 import javax.enterprise.inject.spi.CDI;
+import javax.json.bind.annotation.JsonbProperty;
 
 import graphql.schema.DataFetcher;
 import graphql.schema.DataFetcherFactories;
 import graphql.schema.GraphQLScalarType;
 import graphql.schema.PropertyDataFetcher;
+
 import org.eclipse.microprofile.graphql.Description;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Id;
@@ -837,8 +839,9 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth
         }
 
         fd.setDescription(discoveredMethod.getDescription());
-        fd.setJsonbFormat(discoveredMethod.isJsonbFormat);
+        fd.setJsonbFormat(discoveredMethod.isJsonbFormat());
         fd.setDefaultValue(discoveredMethod.getDefaultValue());
+        fd.setJsonbProperty(discoveredMethod.isJsonbProperty());
         return fd;
     }
 
@@ -999,6 +1002,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
         boolean isReturnTypeMandatory = false;
         boolean isArrayReturnTypeMandatory = false;
         boolean isJsonbFormat = false;
+        boolean isJsonbProperty;
         String defaultValue = null;
 
         // retrieve the method name
@@ -1017,6 +1021,8 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
             }
         }
 
+        isJsonbProperty = (isInputType ? pd.getWriteMethod() : method).getAnnotation(JsonbProperty.class) != null;
+
         ensureValidName(LOGGER, varName);
 
         Class returnClazz = method.getReturnType();
@@ -1070,11 +1076,23 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
                         // if the set method has a format then this should overwrite any formatting
                         // as this is an InputType
                         String[] writeMethodFormat = getFormattingAnnotation(writeMethod);
-                        if (writeMethodFormat[0] != null) {
+
+                        if (!isFormatEmpty(writeMethodFormat)) {
                             format = writeMethodFormat;
                             isJsonbFormat = isJsonbAnnotationPresent(writeMethod);
+                            isJsonbProperty = writeMethod.getAnnotation(JsonbProperty.class) != null;
+                        }
+                        // also check the parameter 0 which there will be one as this is a write Method
+                        Parameter[] parameters = writeMethod.getParameters();
+                        if (parameters.length == 1) {
+                            // this should always be true
+                            String[] argumentTypeFormat = FormattingHelper.getMethodParameterFormat(parameters[0], 0);
+                            if (!isFormatEmpty(argumentTypeFormat)) {
+                                format = argumentTypeFormat;
+                                isJsonbFormat = isJsonbAnnotationPresent(parameters[0]);
+                                isJsonbProperty = parameters[0].getAnnotation(JsonbProperty.class) != null;
+                            }
                         }
-
                     }
                 } else {
                     NonNull methodAnnotation = method.getAnnotation(NonNull.class);
@@ -1100,7 +1118,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
             }
 
             // check for format on the property but only override if it is null
-            if (field != null && (format.length == 0 || format[0] == null)) {
+            if (field != null && isFormatEmpty(format)) {
                 format = getFormattingAnnotation(field);
                 if (isFormatEmpty(format)) {
                     // check to see format of the inner most class. E.g. List<@DateFormat("DD/MM") String>
@@ -1131,6 +1149,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
         discoveredMethod.setFormat(format);
         discoveredMethod.setDefaultValue(defaultValue);
         discoveredMethod.setJsonbFormat(isJsonbFormat);
+        discoveredMethod.setJsonbProperty(isJsonbProperty);
         discoveredMethod.setPropertyName(pd != null ? pd.getName() : null);
 
         if (description == null && !isInputType) {
@@ -1156,7 +1175,8 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
                }
             }
             discoveredMethod.setReturnType(realReturnType.getReturnClass());
-            if (!isFormatEmpty(realReturnType.getFormat())) {
+            // only override if this is not an input type
+            if (!isInputType && !isFormatEmpty(realReturnType.getFormat())) {
                 discoveredMethod.setFormat(realReturnType.format);
             }
         } else {
@@ -1499,10 +1519,15 @@ public static class DiscoveredMethod {
         private Class originalArrayType;
 
         /**
-         * Indicates if the format is of type JsonB.
+         * Indicates if the format is of type Jsonb.
          */
         private boolean isJsonbFormat;
 
+        /**
+         * Indicates if the property name is of type Jsonb.
+         */
+        private boolean isJsonbProperty;
+
         /**
          * Default constructor.
          */
@@ -1563,6 +1588,22 @@ public void setCollectionType(String collectionType) {
             this.collectionType = collectionType;
         }
 
+        /**
+         * Sets if the property has a JsonbProperty annotation.
+         *
+         * @param isJsonbProperty if the property has a JsonbProperty annotation
+         */
+        public void setJsonbProperty(boolean isJsonbProperty) {
+            this.isJsonbProperty = isJsonbProperty;
+        }
+
+        /**
+         * Indicates if the property has a JsonbProperty annotation.
+         */
+        public boolean isJsonbProperty() {
+            return isJsonbProperty;
+        }
+
         /**
          * Indicates if the method is a map return type.
          *
@@ -1879,6 +1920,7 @@ public String toString() {
                     + ", originalArrayType=" + originalArrayType
                     + ", defaultValue=" + defaultValue
                     + ", isJsonbFormat=" + isJsonbFormat
+                    + ", isJsonbProperty=" + isJsonbProperty
                     + ", method=" + method + '}';
         }
 
@@ -1912,7 +1954,8 @@ public boolean equals(Object o) {
         public int hashCode() {
             return Objects.hash(name, returnType, methodType, method, arrayLevels, isQueryAnnotated,
                                 collectionType, isArrayReturnType, isMap, source, description,
-                                isReturnTypeMandatory, defaultValue, isArrayReturnTypeMandatory, isJsonbFormat);
+                                isReturnTypeMandatory, defaultValue, isArrayReturnTypeMandatory, isJsonbFormat,
+                                isJsonbProperty);
         }
     }
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
index 3b66c9bb817..fa9cfc084db 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
@@ -37,6 +37,7 @@
 import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesAndMutations;
 import io.helidon.microprofile.graphql.server.test.types.DateTimePojo;
 
+import io.helidon.microprofile.graphql.server.test.types.SimpleDateTime;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import io.helidon.microprofile.tests.junit5.AddExtension;
@@ -49,9 +50,28 @@
 @AddExtension(GraphQLCdiExtension.class)
 @AddBean(SimpleQueriesAndMutations.class)
 @AddBean(DateTimePojo.class)
+@AddBean(SimpleDateTime.class)
 @AddBean(TestDB.class)
 public class DateTimeIT extends AbstractGraphQLIT {
 
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testDifferentSetterGetter() throws IOException {
+        setupIndex(indexFileName, SimpleDateTime.class, SimpleQueriesAndMutations.class);
+        ExecutionContext executionContext = new ExecutionContext(defaultContext);
+        Map mapResults = getAndAssertResult(
+                executionContext.execute(
+                        "mutation { echoSimpleDateTime(value: { calendarEntries: [ \"22/09/20\", \"23/09/20\" ] } ) { "
+                                + "importantDates } }"));
+        assertThat(mapResults, is(notNullValue()));
+        Map mapResults2 = (Map) mapResults.get("echoSimpleDateTime");
+        assertThat(mapResults2.size(), is(1));
+        ArrayList listDates = (ArrayList) mapResults2.get("importantDates");
+        assertThat(listDates.size(), is(2));
+        assertThat(listDates.get(0), is("22/09"));
+        assertThat(listDates.get(1), is("23/09"));
+    }
+
     @Test
     @SuppressWarnings("unchecked")
     public void testDateAndTime() throws IOException {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
index cc7c495fdca..28b23e10229 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
@@ -30,6 +30,7 @@
 
 import java.util.UUID;
 
+import io.helidon.microprofile.graphql.server.test.types.SimpleDateTime;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.json.bind.annotation.JsonbDateFormat;
@@ -64,6 +65,11 @@ public class SimpleQueriesAndMutations {
     public SimpleQueriesAndMutations() {
     }
 
+    @Mutation
+    public SimpleDateTime echoSimpleDateTime(@Name("value") SimpleDateTime simpleDateTime) {
+        return simpleDateTime;
+    }
+
     @Query
     public Boolean isBooleanObject() {
         return Boolean.valueOf(true);
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTime.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTime.java
new file mode 100644
index 00000000000..55bfc36fedb
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTime.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server.test.types;
+
+import org.eclipse.microprofile.graphql.DateFormat;
+import org.eclipse.microprofile.graphql.Name;
+import org.eclipse.microprofile.graphql.Type;
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * Class representing a simple date/time type.
+ */
+@Type
+public class SimpleDateTime {
+
+    private List importantDates;
+
+    public SimpleDateTime() {
+    }
+
+    public SimpleDateTime(List importantDates) {
+        this.importantDates = importantDates;
+    }
+
+    @Name("calendarEntries")
+    public void setImportantDates(List<@DateFormat("dd/MM/yy") LocalDate> importantDates) {
+        this.importantDates = importantDates;
+    }
+
+    public List<@DateFormat("dd/MM") LocalDate> getImportantDates() {
+        return importantDates;
+    }
+}

From 38e84c04256cac39de9e969cddba6155c501ffc5 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Thu, 15 Oct 2020 09:55:12 +0800
Subject: [PATCH 113/178] fix getCharacter

---
 .../graphql/server/JandexUtils.java           | 104 ++++++++++++------
 .../graphql/server/SchemaGenerator.java       |   2 +-
 .../graphql/server/AbstractGraphQLTest.java   |   9 +-
 .../graphql/server/JandexUtilsTest.java       |  19 ++--
 microprofile/tests/tck/tck-graphql/pom.xml    |  24 ++++
 5 files changed, 111 insertions(+), 47 deletions(-)

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java
index e988a61670f..fb48beddbc6 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java
@@ -17,11 +17,15 @@
 package io.helidon.microprofile.graphql.server;
 
 import java.io.File;
-import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.reflect.Modifier;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Enumeration;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.logging.Logger;
 
@@ -48,9 +52,9 @@ public class JandexUtils {
     public static final String PROP_INDEX_FILE = "io.helidon.microprofile.graphql.indexfile";
 
     /**
-     * The loaded index or null if none was found.
+     * The {@link Set} of loaded indexes.
      */
-    private Index index;
+    private Set setIndexes = new HashSet<>();
 
     /**
      * The file used to load the index.
@@ -65,29 +69,54 @@ public JandexUtils() {
     }
 
     /**
-     * Load the index file.
+     * Load all the index files of the given name.
      */
-    public void loadIndex() {
+    public void loadIndexes() {
+        try {
+            List listUrls = findIndexFiles(indexFile);
+
+            // loop through each URL and load the index
+            for (URL url : listUrls) {
+                try (InputStream input = url.openStream()) {
+                    setIndexes.add(new IndexReader(input).read());
+                } catch (Exception e) {
+                    LOGGER.warning("Unable to load default Jandex index file: " + url
+                                           + " : " + e.getMessage());
+                }
+            }
+        } catch (IOException ignore) {
+            // any Exception coming from getResources() or toURL() is ignored and
+            // the Map of indexes remain empty
+        }
+    }
+
+    /**
+     * Return all the Jandex index files with the given name. If the name is absolute then
+     * return the singl file.
+     *
+     * @param indexFileName  index file name
+     * @return a {@link List} of the index file names
+     *
+     * @throws IOException if any error
+     */
+    private List findIndexFiles(String indexFileName) throws IOException {
+        List result = new ArrayList<>();
+        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
         File file = new File(indexFile);
-        String actualFile;
         if (file.isAbsolute()) {
-            actualFile = indexFile;
-        } else {
-            URL resource = JandexUtils.class.getClassLoader().getResource(indexFile);
-            if (resource == null) {
-                return;
-            }
-            actualFile = resource.getFile();
+            result.add(file.toPath().toUri().toURL());
+            return result;
         }
-        try (FileInputStream input = new FileInputStream(actualFile)) {
-            IndexReader reader = new IndexReader(input);
-            index = reader.read();
-        } catch (Exception e) {
-            LOGGER.warning("Unable to load default Jandex index file: " + indexFile
-                                   + ": " + e.getMessage());
+
+        Enumeration urls = contextClassLoader.getResources(indexFileName);
+        while (urls.hasMoreElements()) {
+            result.add(urls.nextElement());
         }
+
+        return result;
     }
 
+
     /**
      * Return a {@link Collection} of {@link Class}es which are implementors of a given class/interface.
      *
@@ -96,23 +125,26 @@ public void loadIndex() {
      * @return a {@link Collection} of {@link Class}es
      */
     public Collection> getKnownImplementors(String clazz, boolean includeAbstract) {
-        if (index == null) {
+        Set> setResults = new HashSet<>();
+        if (!hasIndex()) {
             return null;
         }
-        Set allKnownImplementors = index.getAllKnownImplementors(DotName.createSimple(clazz));
-        Set> setResults = new HashSet<>();
 
-        for (ClassInfo classInfo : allKnownImplementors) {
-            Class clazzName = null;
-            try {
-                clazzName = Class.forName(classInfo.toString());
-            } catch (ClassNotFoundException e) {
-                // ignore as class should exist
-            }
-            if (includeAbstract || !Modifier.isAbstract(clazzName.getModifiers())) {
-                setResults.add(clazzName);
+        for (Index index : setIndexes) {
+            Set allKnownImplementors = index.getAllKnownImplementors(DotName.createSimple(clazz));
+            for (ClassInfo classInfo : allKnownImplementors) {
+                Class clazzName = null;
+                try {
+                    clazzName = Class.forName(classInfo.toString());
+                } catch (ClassNotFoundException e) {
+                    // ignore as class should exist
+                }
+                if (includeAbstract || !Modifier.isAbstract(clazzName.getModifiers())) {
+                    setResults.add(clazzName);
+                }
             }
         }
+
         return setResults;
     }
 
@@ -132,16 +164,16 @@ public Collection> getKnownImplementors(String clazz) {
      * @return true if an index was found
      */
     public boolean hasIndex() {
-        return index != null;
+        return setIndexes != null && setIndexes.size() > 0;
     }
 
     /**
-     * Return the generated {@link Index}.
+     * Return the generated {@link Set} of {@link Index}es.
      *
-     * @return the generated {@link Index}
+     * @return the generated {@link Set} of {@link Index}es
      */
-    public Index getIndex() {
-        return index;
+    public Set getIndexes() {
+        return setIndexes;
     }
 
     /**
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index 3395f4bc832..70b96654064 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -175,7 +175,7 @@ public class SchemaGenerator {
     public SchemaGenerator(Context context) {
         this.context = context;
         jandexUtils = new JandexUtils();
-        jandexUtils.loadIndex();
+        jandexUtils.loadIndexes();
         if (!jandexUtils.hasIndex()) {
             String message = "Unable to find or load jandex index file: "
                     + jandexUtils.getIndexFile() + ".\nEnsure you are using the "
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java
index 78bd12bd853..3da10f4078b 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java
@@ -158,9 +158,14 @@ protected static void setupIndex(String indexFileName, Class... clazzes) thro
 
         // do a load to check the classes are there
         JandexUtils utils = new JandexUtils();
-        utils.loadIndex();
+        utils.loadIndexes();
         assertThat(utils.hasIndex(), CoreMatchers.is(true));
-        assertThat(utils.getIndex().getKnownClasses().size(), CoreMatchers.is(clazzes.length));
+        int count = 0;
+        for (Index index : utils.getIndexes()) {
+            count += index.getKnownClasses().size();
+        }
+
+        assertThat(count, CoreMatchers.is(clazzes.length));
     }
 
     protected static String getIndexClassName(Class clazz) {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java
index d3da3f281f5..43b15fe89e1 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Set;
 
 import io.helidon.microprofile.graphql.server.AbstractGraphQLTest;
 import org.jboss.jandex.ClassInfo;
@@ -65,16 +66,18 @@ public void testLoadingCustomIndexFile() throws IOException, ClassNotFoundExcept
                               "java/lang/Double.class",
                               "java/lang/Integer.class");
             JandexUtils utils = new JandexUtils();
-            utils.loadIndex();
+            utils.loadIndexes();
 
             assertThat(utils.hasIndex(), is(true));
-            Index index = utils.getIndex();
-            Arrays.stream(new Class[] { String.class, Double.class, Integer.class })
-                    .forEach(c -> {
-                        ClassInfo classByName = index.getClassByName(DotName.createSimple(c.getName()));
-                        assertThat(classByName, is(notNullValue()));
-                        assertThat(classByName.toString(), is(c.getName()));
-                    });
+            Set setIndexes = utils.getIndexes();
+            
+            for (Index index : setIndexes) {
+                for (Class c : new Class[] { String.class, Double.class, Integer.class }) {
+                    ClassInfo classByName = index.getClassByName(DotName.createSimple(c.getName()));
+                    assertThat(classByName, is(notNullValue()));
+                    assertThat(classByName.toString(), is(c.getName()));
+                }
+            }
         } finally {
             if (indexFile != null) {
                 new File(indexFile).delete();
diff --git a/microprofile/tests/tck/tck-graphql/pom.xml b/microprofile/tests/tck/tck-graphql/pom.xml
index d48c2ff7c48..588a6029c8b 100644
--- a/microprofile/tests/tck/tck-graphql/pom.xml
+++ b/microprofile/tests/tck/tck-graphql/pom.xml
@@ -59,6 +59,30 @@
 
     
         
+            
+                org.apache.maven.plugins
+                maven-dependency-plugin
+                
+                    
+                        generate-sources
+                        
+                            unpack
+                        
+                        
+                            
+                                
+                                    org.eclipse.microprofile.graphql
+                                    microprofile-graphql-tck
+                                    jar
+                                    true
+                                    ${project.build.directory}/test-classes
+                                
+                            
+                            **/dynamic/,**/*Test.class,**/beans.xml
+                        
+                    
+                
+            
             
                 org.jboss.jandex
                 jandex-maven-plugin

From 8a91e8d1aab648ad2f10acc2670e7a47d909bdf6 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Thu, 15 Oct 2020 17:22:51 +0800
Subject: [PATCH 114/178] Error handling fixes

---
 .../graphql/server/CustomScalars.java         |  13 ++
 .../graphql/server/ExecutionContext.java      |  38 +++-
 .../graphql/server/AbstractGraphQLIT.java     |  28 ++-
 .../server/AllDefaultsExceptionIT.java        |  47 +++++
 .../server/BLListAndWLExceptionIT.java        |  56 ++++++
 .../graphql/server/BLOfIOExceptionIT.java     |  43 +++++
 .../graphql/server/DataFetcherUtilsIT.java    |   6 +-
 .../graphql/server/DateTimeIT.java            |   3 -
 .../graphql/server/DateTimeScalarIT.java      |   3 -
 .../server/DefaultCheckedExceptionIT.java     |  39 ++++
 .../graphql/server/DefaultValuesIT.java       |   3 -
 .../graphql/server/DescriptionIT.java         |   3 -
 .../server/DifferentMessageExceptionIT.java   |  46 +++++
 .../DuplicateQueriesAndMutationsIT.java       |  38 ++++
 .../server/ErrorConditionsTestContainer.java  | 175 ------------------
 .../graphql/server/ExceptionHandlingIT.java   |  84 +--------
 .../graphql/server/IngorableIT.java           |   3 -
 .../graphql/server/InputTypeIT.java           |   3 -
 .../server/InterfaceOnlyAnnotatedIT.java      |   3 -
 .../server/InterfaceTypeOnlyAnnotatedIT.java  |   3 -
 .../graphql/server/InvalidNamedEnumIT.java    |  41 ++++
 .../server/InvalidNamedInputTypeIT.java       |  40 ++++
 .../server/InvalidNamedInterfaceIT.java       |  40 ++++
 .../server/InvalidNamedMutationIT.java        |  37 ++++
 .../graphql/server/InvalidNamedQueryIT.java   |  37 ++++
 .../graphql/server/InvalidNamedTypeIT.java    |  40 ++++
 .../graphql/server/InvalidQueriesIT.java      |  41 ++++
 .../graphql/server/MultiLevelArraysIT.java    |   3 -
 .../microprofile/graphql/server/NullIT.java   |   3 -
 .../graphql/server/NumberFormatIT.java        |   3 -
 .../graphql/server/PojoNamingIT.java          |   3 -
 .../graphql/server/PropertyNameIT.java        |   3 -
 .../graphql/server/SimpleMutationsIT.java     |   3 -
 .../server/SimpleQueriesWithArgsIT.java       |   3 -
 .../microprofile/graphql/server/SourceIT.java |   3 -
 ...ResourceTest.java => VoidMutationsIT.java} |  17 +-
 .../graphql/server/VoidQueriesIT.java         |  38 ++++
 .../server/WLOfCheckedExceptionIT.java        |  44 +++++
 .../test/exception/ExceptionQueries.java      |  33 +++-
 .../server/test/types/InvalidNamedTypes.java  |   9 +
 .../test/resources/config/config1.properties  |  16 --
 .../test/resources/config/config2.properties  |  17 --
 .../test/resources/config/config3.properties  |  16 --
 .../test/resources/config/config4.properties  |  16 --
 44 files changed, 750 insertions(+), 393 deletions(-)
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
 delete mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
 rename microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/{GraphQLResourceTest.java => VoidMutationsIT.java} (51%)
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
 delete mode 100644 microprofile/graphql/server/src/test/resources/config/config1.properties
 delete mode 100644 microprofile/graphql/server/src/test/resources/config/config2.properties
 delete mode 100644 microprofile/graphql/server/src/test/resources/config/config3.properties
 delete mode 100644 microprofile/graphql/server/src/test/resources/config/config4.properties

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
index 85ebaa8bf39..649b3531e52 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java
@@ -389,10 +389,23 @@ public BigDecimalCoercing() {
         }
     }
 
+    @SuppressWarnings("unchecked")
+
+    /**
+     * Number implementation of {@link Coercing} interface for given classes.
+     */
     public static class NumberCoercing implements Coercing {
 
+        /**
+         * Original {@link Coercing} to fall back on if neeed.
+         */
         private final Coercing originalCoercing;
 
+        /**
+         * Construct a {@Link NumberCoercing} from an original {@link Coercing}.
+         *
+         * @param originalCoercing original {@link Coercing}
+         */
         public NumberCoercing(Coercing originalCoercing) {
             this.originalCoercing = originalCoercing;
         }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
index 11efc44bf0a..fbb90d23180 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
@@ -41,6 +41,7 @@
 
 import static graphql.ExecutionInput.newExecutionInput;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getSafeClass;
 
 /**
  * Defines a context in which to execute GraphQL commands.
@@ -346,30 +347,47 @@ private Map getErrorPayload(String message, Throwable t) {
 
     /**
      * Return the message for an un-checked exception. This will return the default message unless the unchecked exception is on
-     * the whitelist.
+     * the whitelist or is a subclass of an exception on the whitelist.
      *
      * @param throwable {@link Throwable}
      * @return the message for an un-checked exception
      */
     protected String getUncheckedMessage(Throwable throwable) {
-        String exceptionClass = throwable.getClass().getName();
-        return exceptionWhitelist.contains(exceptionClass)
-                ? throwable.toString()
-                : defaultErrorMessage;
+        Class exceptionClazz = throwable.getClass();
+
+        // loop through each exception in the whitelist and check if
+        // the exception on the whitelist or a subclass of an exception on the blacklist
+
+        for (String exception : exceptionWhitelist) {
+            Class clazz = getSafeClass(exception);
+            if (clazz != null && (exceptionClazz.equals(clazz) || clazz.isAssignableFrom(exceptionClazz))) {
+                return throwable.getMessage();
+            }
+        }
+        return defaultErrorMessage;
     }
 
     /**
      * Return the message for a checked exception. This will return the exception message unless the exception is on the
-     * blacklist.
+     * blacklist or is a subclass of an exception on the blacklist.
      *
      * @param throwable {@link Throwable}
      * @return the message for a checked exception
      */
     protected String getCheckedMessage(Throwable throwable) {
-        String exceptionClass = throwable.getClass().getName();
-        return exceptionBlacklist.contains(exceptionClass)
-                ? defaultErrorMessage
-                : throwable.toString();
+        Class exceptionClazz = throwable.getClass();
+
+        // loop through each exception in the blacklist and check if
+        // the exception on the blacklist or a subclass of an exception on the blacklist
+
+        for (String exception : exceptionBlacklist) {
+            Class clazz = getSafeClass(exception);
+            if (clazz != null && (exceptionClazz.equals(clazz) || clazz.isAssignableFrom(exceptionClazz))) {
+                return defaultErrorMessage;
+            }
+        }
+
+        return throwable.getMessage();
     }
 
     /**
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java
index ee36b3e78ef..fb3128be975 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java
@@ -16,6 +16,9 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -23,13 +26,19 @@
 import java.beans.IntrospectionException;
 import java.io.File;
 import java.io.IOException;
+import java.util.List;
+import java.util.Map;
 
+import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 /**
  * Common functionality for integration tests.
  */
+@HelidonTest
+@DisableDiscovery
+@AddExtension(GraphQLCdiExtension.class)
 public abstract class AbstractGraphQLIT
         extends AbstractGraphQLTest {
 
@@ -51,7 +60,7 @@ public void teardownTest() {
             indexFile.delete();
         }
     }
-    
+
     protected void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException {
         SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext);
         Schema schema = schemaGenerator.generateSchema();
@@ -66,4 +75,21 @@ protected void assertInterfaceResults() throws IntrospectionException, ClassNotF
         assertThat(schema.getTypeByName("Mutation"), CoreMatchers.is(notNullValue()));
         generateGraphQLSchema(schema);
     }
+
+    @SuppressWarnings("unchecked")
+    protected void assertMessageValue(String query, String expectedMessage, boolean dataExpected) {
+        ExecutionContext executionContext = new ExecutionContext(new DefaultContext());
+        Map mapResults = executionContext.execute(query);
+        if (dataExpected && mapResults.size() != 2) {
+            System.out.println(JsonUtils.convertMapToJson(mapResults));
+        }
+        assertThat(mapResults.size(), is(dataExpected ? 2 : 1));
+        List> listErrors = (List>) mapResults.get(ExecutionContext.ERRORS);
+        assertThat(listErrors, is(notNullValue()));
+        assertThat(listErrors.size(), is(1));
+        Map mapErrors = listErrors.get(0);
+        assertThat(mapErrors.get(ExecutionContext.MESSAGE), is(expectedMessage));
+
+        assertThat(mapResults.containsKey(ExecutionContext.DATA), is(dataExpected));
+    }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
new file mode 100644
index 00000000000..b414e7aee88
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
+import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+@AddBean(ExceptionQueries.class)
+@AddBean(SimpleContact.class)
+@AddBean(TestDB.class)
+public class AllDefaultsExceptionIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testAllDefaultsForConfig() throws IOException {
+        setupIndex(indexFileName);
+        ExecutionContext executionContext = new ExecutionContext(defaultContext);
+        assertThat(executionContext, is(notNullValue()));
+        assertThat(executionContext.getDefaultErrorMessage(), is("Server Error"));
+        assertThat(executionContext.getExceptionBlacklist().size(), is(0));
+        assertThat(executionContext.getExceptionWhitelist().size(), is(0));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
new file mode 100644
index 00000000000..88bb7a832cb
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
+import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddConfig;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.eclipse.microprofile.graphql.ConfigKey;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+@AddBean(ExceptionQueries.class)
+@AddBean(SimpleContact.class)
+@AddBean(TestDB.class)
+@AddConfig(key = ConfigKey.EXCEPTION_WHITE_LIST,
+           value = "org.eclipse.microprofile.graphql.tck.apps.superhero.api.WeaknessNotFoundException")
+@AddConfig(key = ConfigKey.EXCEPTION_BLACK_LIST, value = "java.io.IOException,java.util.concurrent.TimeoutException")
+public class BLListAndWLExceptionIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testBlackListAndWhiteList() throws IOException {
+        setupIndex(indexFileName);
+
+        ExecutionContext executionContext = new ExecutionContext(defaultContext);
+        assertThat(executionContext.getDefaultErrorMessage(), is("Server Error"));
+        assertThat(executionContext.getExceptionBlacklist().size(), is(2));
+        assertThat(executionContext.getExceptionWhitelist().size(), is(1));
+        assertThat(executionContext.getExceptionBlacklist().contains("java.io.IOException"), is(true));
+        assertThat(executionContext.getExceptionBlacklist().contains("java.util.concurrent.TimeoutException"), is(true));
+        assertThat(executionContext.getExceptionWhitelist()
+                           .contains("org.eclipse.microprofile.graphql.tck.apps.superhero.api.WeaknessNotFoundException"),
+                   is(true));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
new file mode 100644
index 00000000000..4e42a06abc7
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
+import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddConfig;
+
+import org.eclipse.microprofile.graphql.ConfigKey;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+
+@AddBean(ExceptionQueries.class)
+@AddBean(SimpleContact.class)
+@AddBean(TestDB.class)
+@AddConfig(key = ConfigKey.EXCEPTION_BLACK_LIST, value = "java.io.IOException,java.util.concurrent.TimeoutException")
+public class BLOfIOExceptionIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testBlackListOfIOException() throws IOException {
+        setupIndex(indexFileName, ExceptionQueries.class);
+        assertMessageValue("query { checkedException }", "Server Error", true);
+        assertMessageValue("query { checkedException2 }", "Server Error", true);
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
index 740da7a4d75..7b3fc3fa747 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
@@ -49,16 +49,12 @@
  * Tests for {@link DataFetcherUtils} class.
  */
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(SimpleQueriesWithArgs.class)
 @AddBean(SimpleContactWithNumberFormats.class)
 @AddBean(ContactRelationship.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
-class DataFetcherUtilsIT
-        extends AbstractGraphQLIT {
+class DataFetcherUtilsIT extends AbstractGraphQLIT {
 
     @Test
     public void testSimpleContact() throws Exception {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
index fa9cfc084db..4d58713b836 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
@@ -45,9 +45,6 @@
 import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(SimpleQueriesAndMutations.class)
 @AddBean(DateTimePojo.class)
 @AddBean(SimpleDateTime.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
index ed64556cf4b..bd60c71f763 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
@@ -35,9 +35,6 @@
 import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(DateTimeScalarQueries.class)
 @AddBean(SimpleDateTimePojo.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
new file mode 100644
index 00000000000..09448263451
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
+import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+@AddBean(ExceptionQueries.class)
+@AddBean(SimpleContact.class)
+@AddBean(TestDB.class)
+public class DefaultCheckedExceptionIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testBlackListAndWhiteList() throws IOException {
+        setupIndex(indexFileName, ExceptionQueries.class);
+        assertMessageValue("query { checkedQuery1(throwException: true) }", "exception", true);
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
index 59d6e460b09..7de6669c683 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
@@ -39,9 +39,6 @@
 /**
  * Tests for default values.
  */
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(DefaultValuePOJO.class)
 @AddBean(DefaultValueQueries.class)
 @AddBean(OddNamedQueriesAndMutations.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
index 52392787c16..0c5d43d71f4 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
@@ -33,9 +33,6 @@
 import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(DescriptionType.class)
 @AddBean(DescriptionQueries.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
new file mode 100644
index 00000000000..93ac31cca0c
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
+import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddConfig;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+@AddBean(ExceptionQueries.class)
+@AddBean(SimpleContact.class)
+@AddBean(TestDB.class)
+@AddConfig(key = "mp.graphql.defaultErrorMessage", value = "new message")
+public class DifferentMessageExceptionIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testDifferentMessage() throws IOException {
+        setupIndex(indexFileName);
+
+        ExecutionContext executionContext = new ExecutionContext(defaultContext);
+        assertThat(executionContext.getDefaultErrorMessage(), is("new message"));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
new file mode 100644
index 00000000000..4c1f22fd11b
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
+import io.helidon.microprofile.graphql.server.test.queries.DuplicateNameQueries;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@AddBean(VoidMutations.class)
+public class DuplicateQueriesAndMutationsIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testDuplicateQueryOrMutationNames() throws IOException {
+        setupIndex(indexFileName, DuplicateNameQueries.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java
deleted file mode 100644
index 13be96be932..00000000000
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ErrorConditionsTestContainer.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (c) 2020 Oracle and/or its affiliates.
- *
- * 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 io.helidon.microprofile.graphql.server;
-
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
-
-import io.helidon.microprofile.graphql.server.test.queries.DuplicateNameQueries;
-import io.helidon.microprofile.graphql.server.test.queries.InvalidQueries;
-import io.helidon.microprofile.graphql.server.test.queries.VoidQueries;
-import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
-
-import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
-import org.junit.jupiter.api.Test;
-
-/**
- * Container for Error conditions tests.
- */
-
-public class ErrorConditionsTestContainer {
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(VoidMutations.class)
-    public static class VoidMutationsIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testVoidMutations() throws IOException {
-            setupIndex(indexFileName, VoidMutations.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(VoidMutations.class)
-    public static class VoidQueriesIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testVoidQueries() throws IOException {
-            setupIndex(indexFileName, VoidQueries.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(VoidMutations.class)
-    public static class InvalidQueriesIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testInvalidQueries() throws IOException {
-            setupIndex(indexFileName, InvalidQueries.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(VoidMutations.class)
-    public static class DuplicateQueriesAndMutationsIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testDuplicateQueryOrMutationNames() throws IOException {
-            setupIndex(indexFileName, DuplicateNameQueries.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-
-
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(InvalidNamedTypes.InvalidNamedPerson.class)
-    public static class InvalidNamedTypeIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testInvalidNamedType() throws IOException {
-            setupIndex(indexFileName, InvalidNamedTypes.InvalidNamedPerson.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(InvalidNamedTypes.InvalidNamedPerson.class)
-    public static class InvalidNamedInputTypeIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testInvalidNamedInputType() throws IOException {
-            setupIndex(indexFileName, InvalidNamedTypes.InvalidInputType.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-
-
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(InvalidNamedTypes.InvalidNamedPerson.class)
-    public static class InvalidNamedInterfaceIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testInvalidNamedInterface() throws IOException {
-            setupIndex(indexFileName, InvalidNamedTypes.InvalidInterface.class, InvalidNamedTypes.InvalidClass.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-
-
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(InvalidNamedTypes.ClassWithInvalidQuery.class)
-    public static class InvalidNamedQueryIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testInvalidNamedQuery() throws IOException {
-            setupIndex(indexFileName, InvalidNamedTypes.ClassWithInvalidQuery.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-
-
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(InvalidNamedTypes.ClassWithInvalidMutation.class)
-    public static class InvalidNamedMutationIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testInvalidNamedMutation() throws IOException {
-            setupIndex(indexFileName, InvalidNamedTypes.ClassWithInvalidMutation.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-
-
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean( InvalidNamedTypes.Size.class)
-    public static class InvalidNamedEnumIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testInvalidNameEnum() throws IOException {
-            setupIndex(indexFileName, InvalidNamedTypes.Size.class);
-            assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
-        }
-    }
-}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
index 79ffd32b7c2..3e5590cf0d1 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
@@ -27,6 +27,7 @@
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddConfig;
 import io.helidon.microprofile.tests.junit5.AddExtension;
 import io.helidon.microprofile.tests.junit5.DisableDiscovery;
 import io.helidon.microprofile.tests.junit5.HelidonTest;
@@ -46,55 +47,12 @@
 /**
  * Integration tests for testing exception handing in {@link SchemaGeneratorTest}.
  */
-@SuppressWarnings("unchecked")
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(ExceptionQueries.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
+@SuppressWarnings("unchecked")
 public class ExceptionHandlingIT extends AbstractGraphQLIT {
-    
-    @Test
-    public void testAllDefaultsForConfig() throws IOException {
-        setupConfig(null);
-
-        setupIndex(indexFileName);
-        ExecutionContext executionContext = new ExecutionContext(defaultContext);
-        assertThat(executionContext, is(notNullValue()));
-        assertThat(executionContext.getDefaultErrorMessage(), is("Server Error"));
-        assertThat(executionContext.getExceptionBlacklist().size(), is(0));
-        assertThat(executionContext.getExceptionWhitelist().size(), is(0));
-    }
-
-    @Test
-    public void testDifferentMessage() throws IOException {
-        Config config = setupConfig("config/config1.properties");
-        assertThat(config.getValue("mp.graphql.defaultErrorMessage", String.class), is("new message"));
-
-        setupIndex(indexFileName);
-
-        ExecutionContext executionContext = new ExecutionContext(defaultContext);
-        assertThat(executionContext.getDefaultErrorMessage(), is("new message"));
-    }
-
-    @Test
-    public void testBlackListAndWhiteList() throws IOException {
-        setupConfig("config/config2.properties");
-
-        setupIndex(indexFileName);
-
-        ExecutionContext executionContext = new ExecutionContext(defaultContext);
-        assertThat(executionContext.getDefaultErrorMessage(), is("Server Error"));
-        assertThat(executionContext.getExceptionBlacklist().size(), is(2));
-        assertThat(executionContext.getExceptionWhitelist().size(), is(1));
-        assertThat(executionContext.getExceptionBlacklist().contains("java.io.IOException"), is(true));
-        assertThat(executionContext.getExceptionBlacklist().contains("java.util.concurrent.TimeoutException"), is(true));
-        assertThat(executionContext.getExceptionWhitelist()
-                           .contains("org.eclipse.microprofile.graphql.tck.apps.superhero.api.WeaknessNotFoundException"),
-                   is(true));
-    }
 
     @Test
     public void testEmptyErrorPayloads() throws IOException {
@@ -204,44 +162,6 @@ public void testUnknownField() throws IOException {
                                    " type 'SimpleContact' is undefined @ 'defaultContact/invalidField'", true);
     }
 
-    @Test
-    public void testDefaultCheckedException() throws IOException {
-        setupConfig(null);
-        setupIndex(indexFileName, ExceptionQueries.class);
-        assertMessageValue("query { checkedQuery1(throwException: true) }", "java.io.IOException: exception", true);
-    }
-
-    @Test
-    public void testBlackListOfIOException() throws IOException {
-        setupConfig("config/config3.properties");
-        setupIndex(indexFileName, ExceptionQueries.class);
-        assertMessageValue("query { blackListOfIOException }", "Server Error", true);
-    }
-
-    @Test
-    public void testWhiteListOfCheckedException() throws IOException {
-        setupConfig("config/config4.properties");
-        setupIndex(indexFileName, ExceptionQueries.class);
-        assertMessageValue("query { whiteListOfUncheckedException }",
-                           "java.io.IOError: java.security.AccessControlException: my exception", true);
-    }
-
-    private void assertMessageValue(String query, String expectedMessage, boolean dataExpected) {
-        ExecutionContext executionContext = new ExecutionContext(new DefaultContext());
-        Map mapResults = executionContext.execute(query);
-        if (dataExpected && mapResults.size() != 2) {
-            System.out.println(JsonUtils.convertMapToJson(mapResults));
-        }
-        assertThat(mapResults.size(), is(dataExpected ? 2 : 1));
-        List> listErrors = (List>) mapResults.get(ExecutionContext.ERRORS);
-        assertThat(listErrors, is(notNullValue()));
-        assertThat(listErrors.size(), is(1));
-        Map mapErrors = listErrors.get(0);
-        assertThat(mapErrors.get(ExecutionContext.MESSAGE), is(expectedMessage));
-
-        assertThat(mapResults.containsKey(ExecutionContext.DATA), is(dataExpected));
-    }
-
     private void assertPayload(Map errorMap) {
         assertThat(errorMap, is(Matchers.notNullValue()));
         assertThat(errorMap.size(), is(2));
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
index c2a00b34597..62cd699a0ec 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
@@ -38,9 +38,6 @@
 
 import org.opentest4j.AssertionFailedError;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(QueriesWithIgnorable.class)
 @AddBean(ObjectWithIgnorableFieldsAndMethods.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
index bfbe835441e..6279d13b1d5 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
@@ -32,9 +32,6 @@
 import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(SimpleContactInputType.class)
 @AddBean(SimpleContactInputTypeWithName.class)
 @AddBean(SimpleContactInputTypeWithNameValue.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
index 86ed4729997..364cf3f6eb2 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
@@ -31,9 +31,6 @@
 import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(Vehicle.class)
 @AddBean(Car.class)
 @AddBean(Motorbike.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
index 21ebdde0378..1f1e3d6ab25 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
@@ -31,9 +31,6 @@
 import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(Vehicle.class)
 @AddBean(Car.class)
 @AddBean(Motorbike.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
new file mode 100644
index 00000000000..dcef6ff8e07
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@AddBean(InvalidNamedTypes.Size.class)
+@AddBean(InvalidNamedTypes.ClassWithInvalidEnum.class)
+/**
+ * Tests
+ */
+public class InvalidNamedEnumIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testInvalidNameEnum() throws IOException {
+        setupIndex(indexFileName, InvalidNamedTypes.Size.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
new file mode 100644
index 00000000000..cea2766ebfd
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@HelidonTest
+@DisableDiscovery
+@AddExtension(GraphQLCdiExtension.class)
+@AddBean(InvalidNamedTypes.InvalidNamedPerson.class)
+public class InvalidNamedInputTypeIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testInvalidNamedInputType() throws IOException {
+        setupIndex(indexFileName, InvalidNamedTypes.InvalidInputType.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
new file mode 100644
index 00000000000..3ceab102f7d
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@HelidonTest
+@DisableDiscovery
+@AddExtension(GraphQLCdiExtension.class)
+@AddBean(InvalidNamedTypes.InvalidNamedPerson.class)
+public class InvalidNamedInterfaceIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testInvalidNamedInterface() throws IOException {
+        setupIndex(indexFileName, InvalidNamedTypes.InvalidInterface.class, InvalidNamedTypes.InvalidClass.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
new file mode 100644
index 00000000000..a319bad0aa5
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@AddBean(InvalidNamedTypes.ClassWithInvalidMutation.class)
+public class InvalidNamedMutationIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testInvalidNamedMutation() throws IOException {
+        setupIndex(indexFileName, InvalidNamedTypes.ClassWithInvalidMutation.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
new file mode 100644
index 00000000000..b2c04faed5f
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@AddBean(InvalidNamedTypes.ClassWithInvalidQuery.class)
+public class InvalidNamedQueryIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testInvalidNamedQuery() throws IOException {
+        setupIndex(indexFileName, InvalidNamedTypes.ClassWithInvalidQuery.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
new file mode 100644
index 00000000000..bd7e01d8490
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@HelidonTest
+@DisableDiscovery
+@AddExtension(GraphQLCdiExtension.class)
+@AddBean(InvalidNamedTypes.InvalidNamedPerson.class)
+public class InvalidNamedTypeIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testInvalidNamedType() throws IOException {
+        setupIndex(indexFileName, InvalidNamedTypes.InvalidNamedPerson.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
new file mode 100644
index 00000000000..2099e89f8af
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
+import io.helidon.microprofile.graphql.server.test.queries.InvalidQueries;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@HelidonTest
+@DisableDiscovery
+@AddExtension(GraphQLCdiExtension.class)
+@AddBean(VoidMutations.class)
+public class InvalidQueriesIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testInvalidQueries() throws IOException {
+        setupIndex(indexFileName, InvalidQueries.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
index a11afe57f5b..0aed06a2c37 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
@@ -45,9 +45,6 @@
 /**
  * Tests for Multi-level arrays.
  */
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(MultiLevelListsAndArrays.class)
 @AddBean(ArrayAndListQueries.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
index c8f7897c4c1..e4c04b911c2 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
@@ -39,9 +39,6 @@
 /**
  * Tests for Nulls.
  */
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(NullPOJO.class)
 @AddBean(QueriesAndMutationsWithNulls.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
index 76e6ce8bf1a..400b1ceae39 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
@@ -44,9 +44,6 @@
 /**
  * Tests for NUmber formats.
  */
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(SimpleContactWithNumberFormats.class)
 @AddBean(NumberFormatQueriesAndMutations.class)
 @AddBean(SimpleQueriesWithArgs.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
index 1526d92560a..ff21331c1d8 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
@@ -35,9 +35,6 @@
 /**
  * Tests for naming of Pojo's.
  */
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(Person.class)
 public class PojoNamingIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
index 5a9608645b7..d878830bc70 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
@@ -32,9 +32,6 @@
 import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(PropertyNameQueries.class)
 @AddBean(TypeWithNameAndJsonbProperty.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
index 770900e8fc7..e7c71fda11e 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
@@ -33,9 +33,6 @@
 import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(SimpleMutations.class)
 @AddBean(TestDB.class)
 public class SimpleMutationsIT extends AbstractGraphQLIT {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
index e1b45fa59dd..62b252b0cc6 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
@@ -46,9 +46,6 @@
 /**
  * Tests for simple queries with args.
  */
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(SimpleQueriesWithArgs.class)
 @AddBean(Car.class)
 @AddBean(AbstractVehicle.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
index 41b130a2cc8..78c39252f3d 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
@@ -38,9 +38,6 @@
 /**
  * Tests for Source annotation.
  */
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(SimpleQueriesWithSource.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLResourceTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
similarity index 51%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLResourceTest.java
rename to microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
index 56053adc757..3f788300ae8 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLResourceTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
@@ -16,11 +16,22 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
+import java.io.IOException;
 
-class GraphQLResourceTest {
-    @Test
-    public void test() {
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@AddBean(VoidMutations.class)
+public class VoidMutationsIT extends AbstractGraphQLIT {
 
+    @Test
+    public void testVoidMutations() throws IOException {
+        setupIndex(indexFileName, VoidMutations.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
     }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
new file mode 100644
index 00000000000..52baedee193
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
+import io.helidon.microprofile.graphql.server.test.queries.VoidQueries;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@AddBean(VoidMutations.class)
+public class VoidQueriesIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testVoidQueries() throws IOException {
+        setupIndex(indexFileName, VoidQueries.class);
+        assertThrows(RuntimeException.class, () -> new ExecutionContext(defaultContext));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
new file mode 100644
index 00000000000..d288a8f604e
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
+import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddConfig;
+
+import org.eclipse.microprofile.graphql.ConfigKey;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+@AddBean(ExceptionQueries.class)
+@AddBean(SimpleContact.class)
+@AddBean(TestDB.class)
+@AddConfig(key = ConfigKey.EXCEPTION_WHITE_LIST, value = "java.io.IOError")
+public class WLOfCheckedExceptionIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testWhiteListOfCheckedException() throws IOException {
+        setupIndex(indexFileName, ExceptionQueries.class);
+        assertMessageValue("query { uncheckedQuery1 }",
+                           "java.security.AccessControlException: my exception", true);
+        assertMessageValue("query { uncheckedQuery2 }",
+                           "java.security.AccessControlException: my exception", true);
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
index 0425e197af2..64814b41453 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
@@ -47,27 +47,52 @@ public String query1() {
         return "hello world";
     }
 
-    @Query("whiteListOfUncheckedException")
+    @Query("uncheckedQuery1")
     public String uncheckedQuery1() {
         throw new IOError(new AccessControlException("my exception"));
     }
 
+    @Query("uncheckedQuery2")
+    public String uncheckedQuery2() {
+        throw new MyIOError(new AccessControlException("my exception"));
+    }
+
     @Query
-    public String checkedQuery1(@Name("throwException") boolean throwException) throws IOException
-    {
+    public String checkedQuery1(@Name("throwException") boolean throwException) throws IOException {
         if (throwException) {
             throw new IOException("exception");
         }
         return String.valueOf(throwException);
     }
 
-    @Query("blackListOfIOException")
+    @Query("checkedException")
     public String checkedException() throws IOException {
         throw new IOException("unable to do this");
     }
 
+    @Query("checkedException2")
+    public String checkedException2() throws MyIOException {
+        throw new MyIOException("my message");
+    }
+
     @Query("defaultContact")
     public SimpleContact getDefaultContact() {
         return testDB.createRandomContact();
     }
+
+    public static class MyIOException extends IOException {
+        public MyIOException() {
+            super();
+        }
+
+        public MyIOException(String message) {
+            super(message);
+        }
+    }
+
+    public static class MyIOError extends IOError {
+        public MyIOError(Throwable cause) {
+            super(cause);
+        }
+    }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/InvalidNamedTypes.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/InvalidNamedTypes.java
index 925cd031e18..803f42fa84d 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/InvalidNamedTypes.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/InvalidNamedTypes.java
@@ -107,6 +107,15 @@ public String echoString(String string) {
         }
     }
 
+    @GraphQLApi
+    public static class ClassWithInvalidEnum {
+
+        @Query
+        public Size echoSize(@Name("value") Size size) {
+            return size;
+        }
+    }
+
     @Enum("&!@@!")
     public enum Size {
         S,
diff --git a/microprofile/graphql/server/src/test/resources/config/config1.properties b/microprofile/graphql/server/src/test/resources/config/config1.properties
deleted file mode 100644
index 34844d028dd..00000000000
--- a/microprofile/graphql/server/src/test/resources/config/config1.properties
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Copyright (c) 2020 Oracle and/or its affiliates.
-#
-# 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.
-#
-mp.graphql.defaultErrorMessage=new message
diff --git a/microprofile/graphql/server/src/test/resources/config/config2.properties b/microprofile/graphql/server/src/test/resources/config/config2.properties
deleted file mode 100644
index 1970523c073..00000000000
--- a/microprofile/graphql/server/src/test/resources/config/config2.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Copyright (c) 2020 Oracle and/or its affiliates.
-#
-# 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.
-#
-mp.graphql.exceptionsWhiteList=org.eclipse.microprofile.graphql.tck.apps.superhero.api.WeaknessNotFoundException
-mp.graphql.exceptionsBlackList=java.io.IOException,java.util.concurrent.TimeoutException
diff --git a/microprofile/graphql/server/src/test/resources/config/config3.properties b/microprofile/graphql/server/src/test/resources/config/config3.properties
deleted file mode 100644
index 6325451eadd..00000000000
--- a/microprofile/graphql/server/src/test/resources/config/config3.properties
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Copyright (c) 2020 Oracle and/or its affiliates.
-#
-# 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.
-#
-mp.graphql.exceptionsBlackList=java.io.IOException,java.util.concurrent.TimeoutException
diff --git a/microprofile/graphql/server/src/test/resources/config/config4.properties b/microprofile/graphql/server/src/test/resources/config/config4.properties
deleted file mode 100644
index 69db7fb165f..00000000000
--- a/microprofile/graphql/server/src/test/resources/config/config4.properties
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Copyright (c) 2020 Oracle and/or its affiliates.
-#
-# 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.
-#
-mp.graphql.exceptionsWhiteList=java.io.IOError

From a284bbfa8ee03f85a9f3ba8d331174a7ba16a0ba Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Fri, 16 Oct 2020 16:43:56 +0800
Subject: [PATCH 115/178] error handling process

---
 .../graphql/server/ExecutionContext.java      | 48 ++++++++++--
 .../server/AllDefaultsExceptionIT.java        |  3 +
 .../server/BLListAndWLExceptionIT.java        |  3 +
 .../graphql/server/BLOfIOExceptionIT.java     |  4 +-
 .../graphql/server/DataFetcherUtilsIT.java    |  1 -
 .../graphql/server/DateTimeIT.java            |  6 +-
 .../graphql/server/DateTimeScalarIT.java      |  8 +-
 .../server/DefaultCheckedExceptionIT.java     |  7 +-
 .../graphql/server/DefaultValuesIT.java       |  4 -
 .../graphql/server/DescriptionIT.java         |  7 +-
 .../server/DifferentMessageExceptionIT.java   |  7 +-
 .../DuplicateQueriesAndMutationsIT.java       |  7 +-
 .../graphql/server/ExceptionHandlingIT.java   |  9 +--
 .../graphql/server/GraphQLEndpointIT.java     |  2 +-
 .../graphql/server/IngorableIT.java           |  9 +--
 .../graphql/server/InputTypeIT.java           |  7 +-
 .../server/InterfaceOnlyAnnotatedIT.java      | 12 ++-
 .../server/InterfaceTypeOnlyAnnotatedIT.java  | 12 ++-
 .../graphql/server/InvalidNamedEnumIT.java    | 10 +--
 .../server/InvalidNamedInputTypeIT.java       | 10 +--
 .../server/InvalidNamedInterfaceIT.java       | 10 +--
 .../server/InvalidNamedMutationIT.java        |  7 +-
 .../graphql/server/InvalidNamedQueryIT.java   |  7 +-
 .../graphql/server/InvalidNamedTypeIT.java    | 10 +--
 .../graphql/server/InvalidQueriesIT.java      | 10 +--
 .../microprofile/graphql/server/Level0IT.java | 10 +--
 .../graphql/server/MultiLevelArraysIT.java    |  4 +-
 .../microprofile/graphql/server/NullIT.java   |  3 -
 .../graphql/server/NumberFormatIT.java        |  6 +-
 .../server/PartialResultsExceptionIT.java     | 73 +++++++++++++++++++
 .../graphql/server/PojoNamingIT.java          |  4 +-
 .../graphql/server/PropertyNameIT.java        |  7 +-
 .../graphql/server/SimpleMutationsIT.java     |  8 +-
 .../server/SimpleQueriesWithArgsIT.java       |  4 +-
 .../microprofile/graphql/server/SourceIT.java |  4 +-
 .../graphql/server/VoidMutationsIT.java       |  7 +-
 .../graphql/server/VoidQueriesIT.java         |  7 +-
 .../server/WLOfCheckedExceptionIT.java        |  3 +
 .../test/exception/ExceptionQueries.java      | 23 ++++++
 39 files changed, 246 insertions(+), 137 deletions(-)
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
index fbb90d23180..c959b426244 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
@@ -22,8 +22,10 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.logging.Logger;
 
+import graphql.execution.DataFetcherResult;
 import io.helidon.config.Config;
 
 import graphql.ExceptionWhileDataFetching;
@@ -38,6 +40,7 @@
 import graphql.validation.ValidationError;
 import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
 import org.eclipse.microprofile.graphql.ConfigKey;
+import org.eclipse.microprofile.graphql.GraphQLException;
 
 import static graphql.ExecutionInput.newExecutionInput;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException;
@@ -280,6 +283,7 @@ public Map execute(String query, String operationName) {
      * @param mapVariables  the map of variables to pass through
      * @return the {@link Map} containing the execution result
      */
+    @SuppressWarnings({"unchecked", "rawtypes"})
     public Map execute(String query, String operationName, Map mapVariables) {
         try {
             ExecutionInput executionInput = newExecutionInput()
@@ -293,13 +297,43 @@ public Map execute(String query, String operationName, Map errors = result.getErrors();
             boolean hasErrors = false;
             Map mapErrors = newErrorPayload(result.getData());
+
+            // process errors
             if (errors != null && errors.size() > 0) {
                 for (GraphQLError error : errors) {
                     if (error instanceof ExceptionWhileDataFetching) {
                         ExceptionWhileDataFetching e = (ExceptionWhileDataFetching) error;
                         Throwable cause = e.getException().getCause();
                         hasErrors = true;
-                        if (cause instanceof Error || cause instanceof RuntimeException) {
+                        if (cause instanceof GraphQLException) {
+                            // process partial results
+                            GraphQLException graphQLE = (GraphQLException) cause;
+                            Object partialResults = graphQLE.getPartialResults();
+
+                            // the current key for data will be the name of the data result
+                            // and there should only be one
+                            Map data = result.getData();
+                            String key = data.keySet().stream().findFirst().orElse(null);
+                            if (key == null) {
+                                ensureRuntimeException(LOGGER, "Partial results should contain 1 single data key");
+                            }
+                            Map dataMap = new HashMap<>();
+                            if (partialResults != null) {
+                                // partial results are native objects and must be converted
+                                if (partialResults instanceof List) {
+                                    ((List) partialResults).removeIf(Objects::isNull);
+                                }
+                                dataMap.put(key, partialResults);
+                            } else {
+                                dataMap = result.getData();
+                                dataMap.values().removeIf(Objects::isNull);
+                            }
+
+                            DataFetcherResult.Builder builder = DataFetcherResult.newResult().data(dataMap);
+                            DataFetcherResult build = builder.build();
+                            mapErrors.put(DATA, dataMap);
+                            addErrorPayload(mapErrors, getCheckedMessage(cause), error);
+                        } else if (cause instanceof Error || cause instanceof RuntimeException) {
                             // unchecked
                             addErrorPayload(mapErrors, getUncheckedMessage(cause), error);
                         } else {
@@ -347,7 +381,7 @@ private Map getErrorPayload(String message, Throwable t) {
 
     /**
      * Return the message for an un-checked exception. This will return the default message unless the unchecked exception is on
-     * the whitelist or is a subclass of an exception on the whitelist.
+     * the allow list or is a subclass of an exception on the allow list.
      *
      * @param throwable {@link Throwable}
      * @return the message for an un-checked exception
@@ -355,9 +389,8 @@ private Map getErrorPayload(String message, Throwable t) {
     protected String getUncheckedMessage(Throwable throwable) {
         Class exceptionClazz = throwable.getClass();
 
-        // loop through each exception in the whitelist and check if
+        // loop through each exception in the allow list and check if
         // the exception on the whitelist or a subclass of an exception on the blacklist
-
         for (String exception : exceptionWhitelist) {
             Class clazz = getSafeClass(exception);
             if (clazz != null && (exceptionClazz.equals(clazz) || clazz.isAssignableFrom(exceptionClazz))) {
@@ -369,7 +402,7 @@ protected String getUncheckedMessage(Throwable throwable) {
 
     /**
      * Return the message for a checked exception. This will return the exception message unless the exception is on the
-     * blacklist or is a subclass of an exception on the blacklist.
+     * deny list or is a subclass of an exception on the deny list.
      *
      * @param throwable {@link Throwable}
      * @return the message for a checked exception
@@ -378,8 +411,7 @@ protected String getCheckedMessage(Throwable throwable) {
         Class exceptionClazz = throwable.getClass();
 
         // loop through each exception in the blacklist and check if
-        // the exception on the blacklist or a subclass of an exception on the blacklist
-
+        // the exception on the deny list or a subclass of an exception on the deny list
         for (String exception : exceptionBlacklist) {
             Class clazz = getSafeClass(exception);
             if (clazz != null && (exceptionClazz.equals(clazz) || clazz.isAssignableFrom(exceptionClazz))) {
@@ -530,7 +562,7 @@ protected void addErrorPayload(Map errorMap, String message, Str
      *
      * @param errorMap error {@link Map} to add to
      * @param message  message to add
-     * @param error    {@link GraphQLError} to retireve infornation from
+     * @param error    {@link GraphQLError} to retrieve information from
      */
     protected void addErrorPayload(Map errorMap, String message, GraphQLError error) {
         int line = -1;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
index b414e7aee88..9874196b1d6 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
@@ -30,6 +30,9 @@
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+/**
+ * Tests for exception handling with all defaults.
+ */
 @AddBean(ExceptionQueries.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
index 88bb7a832cb..c4dc4e763f8 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
@@ -31,6 +31,9 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+/**
+ * Tests for deny list and allow list.
+ */
 @AddBean(ExceptionQueries.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
index 4e42a06abc7..54c635588b6 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
@@ -27,7 +27,9 @@
 
 import java.io.IOException;
 
-
+/**
+ * Tests for deny list.
+ */
 @AddBean(ExceptionQueries.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
index 7b3fc3fa747..97d115811c3 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
@@ -48,7 +48,6 @@
 /**
  * Tests for {@link DataFetcherUtils} class.
  */
-
 @AddBean(SimpleQueriesWithArgs.class)
 @AddBean(SimpleContactWithNumberFormats.class)
 @AddBean(ContactRelationship.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
index 4d58713b836..5fbae30312e 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
@@ -40,11 +40,11 @@
 import io.helidon.microprofile.graphql.server.test.types.SimpleDateTime;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
+/**
+ * Tests for date/times.
+ */
 @AddBean(SimpleQueriesAndMutations.class)
 @AddBean(DateTimePojo.class)
 @AddBean(SimpleDateTime.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
index bd60c71f763..1090f2a3444 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
@@ -23,18 +23,18 @@
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.queries.DateTimeScalarQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleDateTimePojo;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
+/**
+ * Tests for date/time scalars.
+ */
 @AddBean(DateTimeScalarQueries.class)
 @AddBean(SimpleDateTimePojo.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
index 09448263451..425b9727136 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
@@ -20,12 +20,13 @@
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
+/**
+ * Tests for default exceptions.
+ */
 @AddBean(ExceptionQueries.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
index 7de6669c683..93a26a3b279 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
@@ -27,12 +27,8 @@
 import io.helidon.microprofile.graphql.server.test.queries.DefaultValueQueries;
 import io.helidon.microprofile.graphql.server.test.queries.OddNamedQueriesAndMutations;
 import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO;
-
 import io.helidon.microprofile.tests.junit5.AddBean;
 
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
index 0c5d43d71f4..f82739ff4b6 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
@@ -28,11 +28,12 @@
 import io.helidon.microprofile.graphql.server.test.types.DescriptionType;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
+/**
+ * Tests for descriptions.
+ */
 @AddBean(DescriptionType.class)
 @AddBean(DescriptionQueries.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
index 93ac31cca0c..40e31d1cae5 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
@@ -21,15 +21,16 @@
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
 import io.helidon.microprofile.tests.junit5.AddConfig;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+/**
+ * Tests for different exception messages.
+ */
 @AddBean(ExceptionQueries.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
index 4c1f22fd11b..0feb2cc4d1f 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
@@ -19,14 +19,15 @@
 import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
 import io.helidon.microprofile.graphql.server.test.queries.DuplicateNameQueries;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+/**
+ * Tests for duplicate queries and mutations.
+ */
 @AddBean(VoidMutations.class)
 public class DuplicateQueriesAndMutationsIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
index 3e5590cf0d1..96ebfb3d982 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
@@ -27,10 +27,7 @@
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddConfig;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.eclipse.microprofile.config.Config;
 import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
 
@@ -38,16 +35,14 @@
 
 import org.junit.jupiter.api.Test;
 
-
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 /**
- * Integration tests for testing exception handing in {@link SchemaGeneratorTest}.
+ * Tests for testing exception handing in {@link SchemaGeneratorTest}.
  */
-
 @AddBean(ExceptionQueries.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java
index 7053bd2a7bc..f8a1991e4a1 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java
@@ -47,7 +47,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 /**
- * Integration tests for microprofile-graphql implementation via /graphql endpoint.
+ * Tests for microprofile-graphql implementation via /graphql endpoint.
  */
 @HelidonTest
 @DisableDiscovery
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
index 62cd699a0ec..04a6a82dec3 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
@@ -27,17 +27,16 @@
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.queries.QueriesWithIgnorable;
-
 import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods;
-
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
 import org.opentest4j.AssertionFailedError;
 
+/**
+ * Tests for ignorable fields.
+ */
 @AddBean(QueriesWithIgnorable.class)
 @AddBean(ObjectWithIgnorableFieldsAndMethods.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
index 6279d13b1d5..95bf0bfce80 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
@@ -27,11 +27,12 @@
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithNameValue;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
+/**
+ * Tests for input types.
+ */
 @AddBean(SimpleContactInputType.class)
 @AddBean(SimpleContactInputTypeWithName.class)
 @AddBean(SimpleContactInputTypeWithNameValue.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
index 364cf3f6eb2..34c0364c895 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
@@ -26,21 +26,19 @@
 import io.helidon.microprofile.graphql.server.test.types.Vehicle;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
+/**
+ * Tests for discovery of interfaces when only the interface annotated.
+ */
 @AddBean(Vehicle.class)
 @AddBean(Car.class)
 @AddBean(Motorbike.class)
 @AddBean(AbstractVehicle.class)
 @AddBean(TestDB.class)
 public class InterfaceOnlyAnnotatedIT extends AbstractGraphQLIT {
-
-    /**
-     * Test discovery of interfaces when only the interface annotated.
-     */
+    
     @Test
     public void testInterfaceDiscoveryWithImplementorsWithNoTypeAnnotation()
             throws IOException, IntrospectionException, ClassNotFoundException {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
index 1f1e3d6ab25..5a72b4ff315 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
@@ -26,21 +26,19 @@
 import io.helidon.microprofile.graphql.server.test.types.VehicleIncident;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
+/**
+ * Tests for interfaces and subsequent unresolved type which has a Name annotation .
+ */
 @AddBean(Vehicle.class)
 @AddBean(Car.class)
 @AddBean(Motorbike.class)
 @AddBean(VehicleIncident.class)
 @AddBean(TestDB.class)
 public class InterfaceTypeOnlyAnnotatedIT extends AbstractGraphQLIT {
-    
-    /**
-     * Test discovery of interfaces and subsequent unresolved type which has a Name annotation .
-     */
+
     @Test
     public void testInterfaceDiscoveryWithUnresolvedType() throws IOException, IntrospectionException, ClassNotFoundException {
         setupIndex(indexFileName, Vehicle.class, Car.class, Motorbike.class, VehicleIncident.class, AbstractVehicle.class);
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
index dcef6ff8e07..4a1311265cc 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
@@ -18,19 +18,17 @@
 
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
-@AddBean(InvalidNamedTypes.Size.class)
-@AddBean(InvalidNamedTypes.ClassWithInvalidEnum.class)
 /**
- * Tests
+ * Tests for invalid named enums.
  */
+@AddBean(InvalidNamedTypes.Size.class)
+@AddBean(InvalidNamedTypes.ClassWithInvalidEnum.class)
 public class InvalidNamedEnumIT extends AbstractGraphQLIT {
 
     @Test
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
index cea2766ebfd..59d42b4041d 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
@@ -18,17 +18,15 @@
 
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
+/**
+ * Tests for invalid named input types.
+ */
 @AddBean(InvalidNamedTypes.InvalidNamedPerson.class)
 public class InvalidNamedInputTypeIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
index 3ceab102f7d..9139a7cbbd4 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
@@ -18,17 +18,15 @@
 
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
+/**
+ * Tests for invalid names interface.
+ */
 @AddBean(InvalidNamedTypes.InvalidNamedPerson.class)
 public class InvalidNamedInterfaceIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
index a319bad0aa5..1b7af5b5b02 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
@@ -18,14 +18,15 @@
 
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+/**
+ * Tests for inalid name mutations.
+ */
 @AddBean(InvalidNamedTypes.ClassWithInvalidMutation.class)
 public class InvalidNamedMutationIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
index b2c04faed5f..48ce4b64c99 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
@@ -18,14 +18,15 @@
 
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+/**
+ * Tests for invalid named queries.
+ */
 @AddBean(InvalidNamedTypes.ClassWithInvalidQuery.class)
 public class InvalidNamedQueryIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
index bd7e01d8490..17609f913cf 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
@@ -18,17 +18,15 @@
 
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
+/**
+ * Tests for invalid named types.
+ */
 @AddBean(InvalidNamedTypes.InvalidNamedPerson.class)
 public class InvalidNamedTypeIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
index 2099e89f8af..b0f4b60d046 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
@@ -19,17 +19,15 @@
 import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
 import io.helidon.microprofile.graphql.server.test.queries.InvalidQueries;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
+/**
+ * Tests for invalid queries.
+ */
 @AddBean(VoidMutations.class)
 public class InvalidQueriesIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java
index 75048b18bac..97d1e5922bd 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java
@@ -9,14 +9,12 @@
 
 import io.helidon.microprofile.graphql.server.test.types.Level0;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
+/**
+ * Tests for multi-level object graphs - Level0.
+ */
 @AddBean(Level0.class)
 public class Level0IT extends AbstractGraphQLIT {
     
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
index 0aed06a2c37..fea59b0789e 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
@@ -37,9 +37,7 @@
 
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
 /**
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
index e4c04b911c2..7729ea3097f 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
@@ -30,9 +30,6 @@
 
 import io.helidon.microprofile.tests.junit5.AddBean;
 
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
index 400b1ceae39..e3ddd168fc8 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
@@ -36,13 +36,11 @@
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
 /**
- * Tests for NUmber formats.
+ * Tests for Number formats.
  */
 @AddBean(SimpleContactWithNumberFormats.class)
 @AddBean(NumberFormatQueriesAndMutations.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java
new file mode 100644
index 00000000000..d0b4117ba09
--- /dev/null
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.microprofile.graphql.server;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
+import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+
+import io.helidon.microprofile.tests.junit5.AddBean;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.CoreMatchers.is;
+
+/**
+ * Tests for partial results with exception.
+ */
+@AddBean(ExceptionQueries.class)
+@AddBean(SimpleContact.class)
+@AddBean(TestDB.class)
+public class PartialResultsExceptionIT extends AbstractGraphQLIT {
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testSimplePartialResults() throws IOException {
+        setupIndex(indexFileName, ExceptionQueries.class);
+
+        ExecutionContext executionContext = new ExecutionContext(defaultContext);
+
+        Map results = executionContext.execute("query { failAfterNResults(failAfter: 4) }");
+        assertThat(results.size(), is(2));
+
+        List> listErrors = (List>) results.get(ExecutionContext.ERRORS);
+        assertThat(listErrors.size(), is(1));
+        Map mapErrors = (Map) listErrors.get(0);
+
+        // since this is a checked exception we should see message
+        assertThat(mapErrors.get("message"), is("Partial results"));
+        Map mapData = (Map) results.get(ExecutionContext.DATA);
+        assertThat(mapData.size(), is(1));
+        List listIntegers = (List) mapData.get("failAfterNResults");
+        assertThat(listIntegers.size(), is(4));
+    }
+
+    @Test
+    public void testComplexPartialResults() throws IOException {
+        setupIndex(indexFileName, ExceptionQueries.class, SimpleContact.class);
+
+        ExecutionContext executionContext = new ExecutionContext(defaultContext);
+
+        Map results = executionContext.execute(
+                "query { failAfterNContacts(failAfter: 4) { id name age } }");
+        assertThat(results.size(), is(2));
+    }
+}
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
index ff21331c1d8..9ac4ef604c0 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
@@ -20,9 +20,7 @@
 import io.helidon.microprofile.graphql.server.test.types.PersonWithName;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
 import java.beans.IntrospectionException;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
index d878830bc70..c375ddc1dd8 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
@@ -27,11 +27,12 @@
 import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
+/**
+ * Tests for property naming.
+ */
 @AddBean(PropertyNameQueries.class)
 @AddBean(TypeWithNameAndJsonbProperty.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
index e7c71fda11e..df0f333151d 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
@@ -24,15 +24,15 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
-import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
+/**
+ * Tests for simple mutations.
+ */
 @AddBean(SimpleMutations.class)
 @AddBean(TestDB.class)
 public class SimpleMutationsIT extends AbstractGraphQLIT {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
index 62b252b0cc6..85a79b09d37 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
@@ -38,9 +38,7 @@
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
 /**
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
index 78c39252f3d..899e5a40dd4 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
@@ -29,9 +29,7 @@
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
index 3f788300ae8..50073f6701c 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
@@ -18,14 +18,15 @@
 
 import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+/**
+ * Tests for void mutations.
+ */
 @AddBean(VoidMutations.class)
 public class VoidMutationsIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
index 52baedee193..229f700cd37 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
@@ -19,14 +19,15 @@
 import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
 import io.helidon.microprofile.graphql.server.test.queries.VoidQueries;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
 import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+/**
+ * Tests for void queries.
+ */
 @AddBean(VoidMutations.class)
 public class VoidQueriesIT extends AbstractGraphQLIT {
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
index d288a8f604e..b999a9e48d6 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
@@ -27,6 +27,9 @@
 
 import java.io.IOException;
 
+/**
+ * Tests for allow list of checked exceptions.
+ */
 @AddBean(ExceptionQueries.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
index 64814b41453..8bc987a48bd 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
@@ -17,6 +17,7 @@
 package io.helidon.microprofile.graphql.server.test.exception;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
@@ -28,6 +29,8 @@
 import java.io.IOError;
 import java.io.IOException;
 import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Class that holds queries that raise various exceptions.
@@ -80,6 +83,26 @@ public SimpleContact getDefaultContact() {
         return testDB.createRandomContact();
     }
 
+    @Query
+    public List failAfterNResults(@Name("failAfter") int failAfter) throws GraphQLException {
+        List listIntegers = new ArrayList<>();
+        int i = 0;
+        while (i++ < failAfter) {
+            listIntegers.add(i);
+        }
+        throw new GraphQLException("Partial results", listIntegers);
+    }
+
+    @Query
+    public List failAfterNContacts(@Name("failAfter") int failAfter) throws GraphQLException {
+        List listContacts = new ArrayList<>();
+        int i = 0;
+        while (i++ < failAfter) {
+            listContacts.add(new SimpleContact("id-" + i, "Name-" + i, i, EnumTestWithEnumName.XL));
+        }
+        throw new GraphQLException("Partial results", listContacts);
+    }
+
     public static class MyIOException extends IOException {
         public MyIOException() {
             super();

From fa29b59bf8f9a4c1cc4c466f7f4aef173fc9ffdb Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Tue, 20 Oct 2020 10:00:53 +0800
Subject: [PATCH 116/178]  TCK: Fix Partial Results Errors

---
 microprofile/graphql/pom.xml                  |   2 +-
 microprofile/graphql/server/pom.xml           |   2 +-
 .../microprofile/graphql/server/Context.java  |  20 ++-
 .../graphql/server/DataFetcherUtils.java      |  13 +-
 .../graphql/server/DefaultContext.java        |  23 ++++
 .../graphql/server/ExecutionContext.java      | 123 ++++++++++--------
 .../graphql/server/BLOfIOExceptionIT.java     |   2 +-
 .../graphql/server/ExceptionHandlingIT.java   |  29 ++---
 .../graphql/server/IngorableIT.java           |   7 -
 .../server/WLOfCheckedExceptionIT.java        |   2 +-
 .../test/exception/ExceptionQueries.java      |  13 +-
 microprofile/tests/tck/tck-graphql/pom.xml    |   2 +-
 12 files changed, 143 insertions(+), 95 deletions(-)

diff --git a/microprofile/graphql/pom.xml b/microprofile/graphql/pom.xml
index bdff7fde4de..cf4428c2a40 100644
--- a/microprofile/graphql/pom.xml
+++ b/microprofile/graphql/pom.xml
@@ -20,7 +20,7 @@
     
         io.helidon.microprofile
         helidon-microprofile-project
-        2.0.3-SNAPSHOT
+        2.1.1-SNAPSHOT
     
     4.0.0
 
diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml
index b02c1e745f7..43a18b49be7 100644
--- a/microprofile/graphql/server/pom.xml
+++ b/microprofile/graphql/server/pom.xml
@@ -19,7 +19,7 @@
     
         io.helidon.microprofile.graphql
         helidon-microprofile-graphql
-        2.0.3-SNAPSHOT
+        2.1.1-SNAPSHOT
     
     4.0.0
 
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java
index 9c6b1ac873f..4b548bc17a9 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java
@@ -16,9 +16,27 @@
 
 package io.helidon.microprofile.graphql.server;
 
-
 /**
  * A Default Context to be supplied to {@link ExecutionContext}.
  */
 public interface Context {
+
+    /**
+     * Add a partial results  {@link Throwable}.
+     *
+     * @param throwable {@link Throwable}
+     */
+    void addPartialResultsException(Throwable throwable);
+
+    /**
+     * Retrieve partial results {@link Throwable}.
+     *
+     * @return the {@link Throwable}
+     */
+    Throwable getPartialResultsException();
+
+    /**
+     * Remove partial results {@link Throwable}.
+     */
+    void removePartialResultsException();
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index 9b333c651b7..b69a71cb832 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -118,9 +118,20 @@ public static  DataFetcher newMethodDataFetcher(Schema schema, Class cl
             }
 
             try {
+
                 return (V) method.invoke(instance, listArgumentValues.toArray());
             } catch (InvocationTargetException e) {
-                throw new GraphQLException(e.getTargetException());
+                Throwable targetException = e.getTargetException();
+                GraphQLException exception = new GraphQLException(e.getTargetException());
+                if (targetException instanceof org.eclipse.microprofile.graphql.GraphQLException) {
+                    // if we have partial results we need to return those results and they will
+                    // get converted correctly to the format required by GraphQL and the ExecutionContext.execute()
+                    // we ensure this is throw correctly as an error
+                    Context context = environment.getContext();
+                    context.addPartialResultsException(exception);
+                    return (V) ((org.eclipse.microprofile.graphql.GraphQLException) targetException).getPartialResults();
+                }
+                throw exception;
             }
         };
     }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java
index 8bcdffb3473..dfd638b68af 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java
@@ -22,4 +22,27 @@
  */
 public class DefaultContext
         implements Context {
+
+    private ThreadLocal currentThrowable = new ThreadLocal<>();
+
+    /**
+     * Construct a default context.
+     */
+    public DefaultContext() {
+    }
+
+    @Override
+    public void addPartialResultsException(Throwable throwable) {
+        currentThrowable.set(throwable);
+    }
+
+    @Override
+    public Throwable getPartialResultsException() {
+        return currentThrowable.get();
+    }
+
+    @Override
+    public void removePartialResultsException() {
+         currentThrowable.remove();
+    }
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
index c959b426244..1769fb4d49b 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
@@ -18,6 +18,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -297,51 +298,33 @@ public Map execute(String query, String operationName, Map errors = result.getErrors();
             boolean hasErrors = false;
             Map mapErrors = newErrorPayload(result.getData());
+            Throwable partialResultsThrowable = null;
 
-            // process errors
-            if (errors != null && errors.size() > 0) {
+            Context context = this.context;
+
+            // retrieve and remove any partial results for this ThreadLocal and context
+            partialResultsThrowable = context.getPartialResultsException();
+            context.removePartialResultsException();
+
+            if (errors.size() == 0 && partialResultsThrowable != null) {
+                // process partial results errors as we have retrieved the Throwable from ThreadLocal
+                Throwable cause = partialResultsThrowable.getCause();
+                hasErrors = true;
+                processError(mapErrors, cause, result, null, cause.getMessage());
+            } else {
                 for (GraphQLError error : errors) {
                     if (error instanceof ExceptionWhileDataFetching) {
                         ExceptionWhileDataFetching e = (ExceptionWhileDataFetching) error;
                         Throwable cause = e.getException().getCause();
-                        hasErrors = true;
-                        if (cause instanceof GraphQLException) {
-                            // process partial results
-                            GraphQLException graphQLE = (GraphQLException) cause;
-                            Object partialResults = graphQLE.getPartialResults();
-
-                            // the current key for data will be the name of the data result
-                            // and there should only be one
-                            Map data = result.getData();
-                            String key = data.keySet().stream().findFirst().orElse(null);
-                            if (key == null) {
-                                ensureRuntimeException(LOGGER, "Partial results should contain 1 single data key");
-                            }
-                            Map dataMap = new HashMap<>();
-                            if (partialResults != null) {
-                                // partial results are native objects and must be converted
-                                if (partialResults instanceof List) {
-                                    ((List) partialResults).removeIf(Objects::isNull);
-                                }
-                                dataMap.put(key, partialResults);
-                            } else {
-                                dataMap = result.getData();
-                                dataMap.values().removeIf(Objects::isNull);
-                            }
-
-                            DataFetcherResult.Builder builder = DataFetcherResult.newResult().data(dataMap);
-                            DataFetcherResult build = builder.build();
-                            mapErrors.put(DATA, dataMap);
-                            addErrorPayload(mapErrors, getCheckedMessage(cause), error);
-                        } else if (cause instanceof Error || cause instanceof RuntimeException) {
-                            // unchecked
-                            addErrorPayload(mapErrors, getUncheckedMessage(cause), error);
-                        } else {
-                            // checked
-                            addErrorPayload(mapErrors, cause == null ? e.getMessage() : getCheckedMessage(cause), error);
+                        if (cause instanceof Error) {
+                            // re-throw the error as this should result in 500 from graphQL endpoint
+                            throw (Error) cause;
                         }
+                        hasErrors = true;
+                        processError(mapErrors, cause, result, error, e.getMessage());
+
                     } else if (error instanceof ValidationError) {
-                        addErrorPayload(mapErrors, error.getMessage(), error);
+                        addErrorPayload(mapErrors, error.getMessage(), error, null);
                         // the spec tests for empty "data" node on validation errors
                         if (!mapErrors.containsKey(DATA)) {
                             mapErrors.put(DATA, null);
@@ -353,19 +336,40 @@ public Map execute(String query, String operationName, Map mapErrors = getErrorPayload(getUncheckedMessage(e), e);
-            LOGGER.warning(e.getMessage());
+            Map mapErrors = getErrorPayload(getUncheckedMessage(rte), rte);
+            LOGGER.warning(rte.getMessage());
             return mapErrors;
-        } catch (Exception e) {
+        } catch (Exception ex) {
             // checked exception
-            Map mapErrors = getErrorPayload(getCheckedMessage(e), e);
-            LOGGER.warning(e.getMessage());
+            Map mapErrors = getErrorPayload(getCheckedMessage(ex), ex);
+            LOGGER.warning(ex.getMessage());
             return mapErrors;
         }
     }
 
+    @SuppressWarnings("unchecked")
+    private void processError(Map mapErrors, Throwable cause, ExecutionResult result, GraphQLError error,
+                              String originalMessage) {
+        if (cause instanceof GraphQLException) {
+            // at this stage the partial results will be contained in the results.getData()
+            Object data = result.getData();
+            mapErrors.put(DATA, data);
+            Map mapData = (Map) result.getData();
+            String queryName = mapData.keySet().stream().findFirst().orElse(null);
+            addErrorPayload(mapErrors, getCheckedMessage(cause), error, queryName);
+        } else if (cause instanceof Error || cause instanceof RuntimeException) {
+            // unchecked
+            addErrorPayload(mapErrors, getUncheckedMessage(cause), error, null);
+        } else {
+            // checked
+            addErrorPayload(mapErrors, cause == null ? originalMessage : getCheckedMessage(cause), error, null);
+        }
+    }
+
     /**
      * Return the a new error payload.
      *
@@ -563,24 +567,31 @@ protected void addErrorPayload(Map errorMap, String message, Str
      * @param errorMap error {@link Map} to add to
      * @param message  message to add
      * @param error    {@link GraphQLError} to retrieve information from
+     * @param queryName query name to use in path - only valid when GraphQLError is null
      */
-    protected void addErrorPayload(Map errorMap, String message, GraphQLError error) {
+    @SuppressWarnings("unchecked")
+    protected void addErrorPayload(Map errorMap, String message, GraphQLError error, String queryName) {
         int line = -1;
         int column = -1;
-        String path = null;
-        List locations = error.getLocations();
-        if (locations != null && locations.size() > 0) {
-            SourceLocation sourceLocation = locations.get(0);
-            line = sourceLocation.getLine();
-            column = sourceLocation.getColumn();
-        }
+        String path = null; //queryName;
+        if (error != null) {
+            List locations = error.getLocations();
+            if (locations != null && locations.size() > 0) {
+                SourceLocation sourceLocation = locations.get(0);
+                line = sourceLocation.getLine();
+                column = sourceLocation.getColumn();
+            }
 
-        List listPath = error.getPath();
-        if (listPath != null && listPath.size() > 0) {
-            path = listPath.get(0).toString();
+            List listPath = error.getPath();
+            if (listPath != null && listPath.size() > 0) {
+                path = listPath.get(0).toString();
+            }
+        } else {
+            // no error so set the path to be the queryName
+            path = queryName;
         }
 
-        addErrorPayload(errorMap, message, line, column, error.getExtensions(), path);
+        addErrorPayload(errorMap, message, line, column, error != null ? error.getExtensions() : Collections.EMPTY_MAP, path);
     }
 
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
index 54c635588b6..97b08e155a4 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
@@ -37,7 +37,7 @@
 public class BLOfIOExceptionIT extends AbstractGraphQLIT {
 
     @Test
-    public void testBlackListOfIOException() throws IOException {
+    public void testDenyListOfIOException() throws IOException {
         setupIndex(indexFileName, ExceptionQueries.class);
         assertMessageValue("query { checkedException }", "Server Error", true);
         assertMessageValue("query { checkedException2 }", "Server Error", true);
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
index 96ebfb3d982..24b1e9d45c0 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
@@ -21,18 +21,15 @@
 import java.util.List;
 import java.util.Map;
 
-import io.helidon.config.mp.MpConfigSources;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
-import org.eclipse.microprofile.config.Config;
-import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
-
 import org.hamcrest.Matchers;
 
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -150,32 +147,22 @@ public void testErrorPayLoadWithMessagesLocationsAndExtensions() throws IOExcept
 
     @Test
     public void testUnknownField() throws IOException {
-        setupConfig(null);
         setupIndex(indexFileName, ExceptionQueries.class, SimpleContact.class);
         assertMessageValue("query { defaultContact { invalidField } }",
                            "Validation error of type FieldUndefined: Field 'invalidField' in" +
                                    " type 'SimpleContact' is undefined @ 'defaultContact/invalidField'", true);
     }
 
+    @Test
+    public void testError() throws IOException {
+        setupIndex(indexFileName, ExceptionQueries.class, SimpleContact.class);
+        ExecutionContext executionContext = new ExecutionContext(defaultContext);
+        Assertions.assertThrows(Error.class, () -> executionContext.execute("query { throwOOME }"));
+    }
+
     private void assertPayload(Map errorMap) {
         assertThat(errorMap, is(Matchers.notNullValue()));
         assertThat(errorMap.size(), is(2));
         assertThat(errorMap.get(ExecutionContext.DATA), is(nullValue()));
     }
-
-    protected Config setupConfig(String propertiesFile) {
-        Config config = propertiesFile == null
-                ? ConfigProviderResolver.instance().getBuilder().build()
-                : ConfigProviderResolver.instance()
-                        .getBuilder()
-                        .withSources(
-                                MpConfigSources.create(ExceptionHandlingIT.class.getClassLoader().getResource(propertiesFile)))
-                        .build();
-
-        ConfigProviderResolver.instance()
-                .registerConfig(config, Thread.currentThread().getContextClassLoader());
-
-        return config;
-    }
-
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
index 04a6a82dec3..39dd5df050f 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
@@ -42,13 +42,6 @@
 @AddBean(TestDB.class)
 public class IngorableIT extends AbstractGraphQLIT {
 
-    @Test
-    public void testObjectWithIgnorableFields() throws IOException {
-        setupIndex(indexFileName, ObjectWithIgnorableFieldsAndMethods.class);
-        ExecutionContext executionContext =  new ExecutionContext(defaultContext);
-        executionContext.execute("query { hero }");
-    }
-
     @Test
     @SuppressWarnings("unchecked")
     public void testIgnorable() throws IOException {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
index b999a9e48d6..5e51fa96aee 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
@@ -33,7 +33,7 @@
 @AddBean(ExceptionQueries.class)
 @AddBean(SimpleContact.class)
 @AddBean(TestDB.class)
-@AddConfig(key = ConfigKey.EXCEPTION_WHITE_LIST, value = "java.io.IOError")
+@AddConfig(key = ConfigKey.EXCEPTION_WHITE_LIST, value = "java.lang.IllegalArgumentException")
 public class WLOfCheckedExceptionIT extends AbstractGraphQLIT {
 
     @Test
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
index 8bc987a48bd..63ce96ff118 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
@@ -52,12 +52,12 @@ public String query1() {
 
     @Query("uncheckedQuery1")
     public String uncheckedQuery1() {
-        throw new IOError(new AccessControlException("my exception"));
+        throw new IllegalArgumentException(new AccessControlException("my exception"));
     }
 
     @Query("uncheckedQuery2")
     public String uncheckedQuery2() {
-        throw new MyIOError(new AccessControlException("my exception"));
+        throw new MyIllegalArgumentException(new AccessControlException("my exception"));
     }
 
     @Query
@@ -103,6 +103,11 @@ public List failAfterNContacts(@Name("failAfter") int failAfter)
         throw new GraphQLException("Partial results", listContacts);
     }
 
+    @Query
+    public String throwOOME() {
+        throw new OutOfMemoryError("error");
+    }
+
     public static class MyIOException extends IOException {
         public MyIOException() {
             super();
@@ -113,8 +118,8 @@ public MyIOException(String message) {
         }
     }
 
-    public static class MyIOError extends IOError {
-        public MyIOError(Throwable cause) {
+    public static class MyIllegalArgumentException extends IllegalArgumentException {
+        public MyIllegalArgumentException(Throwable cause) {
             super(cause);
         }
     }
diff --git a/microprofile/tests/tck/tck-graphql/pom.xml b/microprofile/tests/tck/tck-graphql/pom.xml
index 588a6029c8b..a9f43ce0732 100644
--- a/microprofile/tests/tck/tck-graphql/pom.xml
+++ b/microprofile/tests/tck/tck-graphql/pom.xml
@@ -21,7 +21,7 @@
     
         io.helidon.microprofile.tests
         tck-project
-        2.0.3-SNAPSHOT
+        2.1.1-SNAPSHOT
     
     tck-graphql
     Helidon Microprofile Tests TCK GraphQL

From b92b7169c49de0e53faae26193cae546c1e7d792 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Tue, 20 Oct 2020 13:07:08 +0800
Subject: [PATCH 117/178] code cleanup

---
 .../server/AbstractDescriptiveElement.java    |   3 +-
 .../microprofile/graphql/server/Context.java  |   2 +-
 .../graphql/server/CustomTimeScalar.java      | 117 ------------------
 .../graphql/server/DataFetcherUtils.java      |   9 +-
 .../graphql/server/ElementGenerator.java      |   1 -
 .../graphql/server/ExecutionContext.java      |   5 +-
 .../graphql/server/FormattingHelper.java      |   1 -
 .../graphql/server/FormattingProvider.java    |   4 -
 .../graphql/server/GraphQLCdiExtension.java   |   4 +-
 .../microprofile/graphql/server/Schema.java   |   3 +-
 .../graphql/server/SchemaArgument.java        |   5 +-
 .../graphql/server/SchemaDirective.java       |   4 +-
 .../graphql/server/SchemaEnum.java            |   5 +-
 .../graphql/server/SchemaFieldDefinition.java |   6 +-
 .../graphql/server/SchemaGenerator.java       |  50 +++++---
 .../graphql/server/SchemaGeneratorHelper.java |   6 +-
 .../graphql/server/SchemaInputType.java       |   3 +-
 .../graphql/server/SchemaScalar.java          |   4 +-
 .../graphql/server/SchemaType.java            |   4 +-
 .../server/AbstractGraphQLEndpointIT.java     |  10 +-
 .../graphql/server/AbstractGraphQLIT.java     |  46 +++----
 .../graphql/server/AbstractGraphQLTest.java   |   3 +-
 .../server/AllDefaultsExceptionIT.java        |   7 +-
 .../server/BLListAndWLExceptionIT.java        |   9 +-
 .../graphql/server/BLOfIOExceptionIT.java     |   4 +-
 .../graphql/server/DataFetcherUtilsIT.java    |  30 ++---
 .../graphql/server/DateTimeIT.java            |  18 ++-
 .../graphql/server/DateTimeScalarIT.java      |   8 +-
 .../server/DefaultCheckedExceptionIT.java     |   3 +-
 .../graphql/server/DefaultValuesIT.java       |   7 +-
 .../graphql/server/DescriptionIT.java         |  10 +-
 .../server/DifferentMessageExceptionIT.java   |   3 +-
 .../DuplicateQueriesAndMutationsIT.java       |   3 +-
 .../graphql/server/ExceptionHandlingIT.java   |   2 -
 .../graphql/server/GraphQLEndpointIT.java     |  11 +-
 .../graphql/server/IngorableIT.java           |   2 -
 .../graphql/server/InputTypeIT.java           |   1 -
 .../server/InterfaceOnlyAnnotatedIT.java      |   6 +-
 .../server/InterfaceTypeOnlyAnnotatedIT.java  |   2 +-
 .../graphql/server/InvalidNamedEnumIT.java    |   3 +-
 .../server/InvalidNamedInputTypeIT.java       |   3 +-
 .../server/InvalidNamedInterfaceIT.java       |   3 +-
 .../server/InvalidNamedMutationIT.java        |   3 +-
 .../graphql/server/InvalidNamedQueryIT.java   |   3 +-
 .../graphql/server/InvalidNamedTypeIT.java    |   3 +-
 .../graphql/server/InvalidQueriesIT.java      |   3 +-
 .../graphql/server/JandexUtilsTest.java       |   2 -
 .../graphql/server/JsonUtilsTest.java         |   4 -
 .../microprofile/graphql/server/NullIT.java   |   1 -
 .../graphql/server/NumberFormatIT.java        |  16 +--
 .../server/PartialResultsExceptionIT.java     |   2 +-
 .../graphql/server/PojoNamingIT.java          |   7 +-
 .../graphql/server/PropertyNameIT.java        |   6 +-
 .../server/SchemaFieldDefinitionTest.java     |   3 +-
 .../graphql/server/SchemaGeneratorTest.java   |   4 +-
 .../graphql/server/SchemaTest.java            |   1 -
 .../graphql/server/SimpleMutationsIT.java     |   9 +-
 .../server/SimpleQueriesWithArgsIT.java       |  10 +-
 .../microprofile/graphql/server/SourceIT.java |   7 +-
 .../graphql/server/VoidMutationsIT.java       |   3 +-
 .../graphql/server/VoidQueriesIT.java         |   3 +-
 .../server/WLOfCheckedExceptionIT.java        |   3 +-
 .../test/exception/ExceptionQueries.java      |  14 ++-
 .../test/mutations/SimpleMutations.java       |   6 +-
 .../test/queries/ArrayAndListQueries.java     |   5 +-
 .../test/queries/DateTimeScalarQueries.java   |  10 +-
 .../test/queries/DefaultValueQueries.java     |   2 +-
 .../test/queries/DuplicateNameQueries.java    |   1 +
 .../NumberFormatQueriesAndMutations.java      |  10 +-
 .../test/queries/PropertyNameQueries.java     |   3 +-
 .../queries/QueriesAndMutationsWithNulls.java |   9 +-
 .../test/queries/QueriesWithIgnorable.java    |  20 +--
 .../queries/SimpleQueriesAndMutations.java    |  29 ++---
 .../test/queries/SimpleQueriesWithArgs.java   |  25 ++--
 .../test/queries/SimpleQueriesWithSource.java |  19 +--
 .../server/test/types/DateTimePojo.java       |   2 -
 .../server/test/types/PersonWithName.java     |   1 -
 .../server/test/types/SimpleContact.java      |   4 +-
 .../types/SimpleContactWithNumberFormats.java |  10 +-
 .../server/test/types/SimpleDateTime.java     |   5 +-
 .../server/test/types/SimpleDateTimePojo.java |   8 +-
 81 files changed, 274 insertions(+), 432 deletions(-)
 delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomTimeScalar.java
 delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingProvider.java

diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java
index 22c08d516b8..16d24a8af60 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java
@@ -21,8 +21,7 @@
 /**
  * An abstract implementation of a {@link DescriptiveElement}.
  */
-public class AbstractDescriptiveElement
-        implements DescriptiveElement {
+public class AbstractDescriptiveElement implements DescriptiveElement {
 
     /**
      * The description for an element.
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java
index 4b548bc17a9..25b93671326 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java
@@ -22,7 +22,7 @@
 public interface Context {
 
     /**
-     * Add a partial results  {@link Throwable}.
+     * Add a partial results {@link Throwable}.
      *
      * @param throwable {@link Throwable}
      */
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomTimeScalar.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomTimeScalar.java
deleted file mode 100644
index 663ce5cd1d1..00000000000
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomTimeScalar.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates.
- *
- * 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 io.helidon.microprofile.graphql.server;
-
-import java.time.DateTimeException;
-import java.time.LocalTime;
-import java.time.OffsetTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
-import java.time.temporal.TemporalAccessor;
-import java.util.function.Function;
-
-import graphql.language.StringValue;
-import graphql.schema.Coercing;
-import graphql.schema.CoercingParseLiteralException;
-import graphql.schema.CoercingParseValueException;
-import graphql.schema.CoercingSerializeException;
-import graphql.schema.GraphQLScalarType;
-
-import static graphql.scalars.util.Kit.typeName;
-
-/**
- * Custom Time scalar. Copied initially from graphql.scalars.datetime.TimeScalar from extend scalars package.
- */
-public class CustomTimeScalar
-        extends GraphQLScalarType {
-
-    /**
-     * Instance of the scalar.
-     */
-    public static final GraphQLScalarType INSTANCE = new CustomTimeScalar();
-
-    /**
-     * Construct a new scalar.
-     */
-    public CustomTimeScalar() {
-        super("Time", "An Customized RFC-3339 compliant Full Time Scalar", new Coercing() {
-            @Override
-            public String serialize(Object input) throws CoercingSerializeException {
-                TemporalAccessor temporalAccessor;
-                if (input instanceof TemporalAccessor) {
-                    temporalAccessor = (TemporalAccessor) input;
-                } else if (input instanceof String) {
-                    temporalAccessor = parseOffsetTime(input.toString(), CoercingSerializeException::new);
-                } else {
-                    throw new CoercingSerializeException(
-                            "Expected a 'String' or 'java.time.temporal.TemporalAccessor' but was '" + typeName(input) + "'."
-                    );
-                }
-                try {
-                    return input instanceof LocalTime
-                            ? DateTimeFormatter.ISO_LOCAL_TIME.format(temporalAccessor)
-                            : DateTimeFormatter.ISO_OFFSET_TIME.format(temporalAccessor);
-                } catch (DateTimeException e) {
-                    throw new CoercingSerializeException(
-                            "Unable to turn TemporalAccessor into full time because of : '" + e.getMessage() + "'."
-                    );
-                }
-            }
-
-            @Override
-            public OffsetTime parseValue(Object input) throws CoercingParseValueException {
-                TemporalAccessor temporalAccessor;
-                if (input instanceof TemporalAccessor) {
-                    temporalAccessor = (TemporalAccessor) input;
-                } else if (input instanceof String) {
-                    temporalAccessor = parseOffsetTime(input.toString(), CoercingParseValueException::new);
-                } else {
-                    throw new CoercingParseValueException(
-                            "Expected a 'String' or 'java.time.temporal.TemporalAccessor' but was '" + typeName(input) + "'."
-                    );
-                }
-                try {
-                    return OffsetTime.from(temporalAccessor);
-                } catch (DateTimeException e) {
-                    throw new CoercingParseValueException(
-                            "Unable to turn TemporalAccessor into full time because of : '" + e.getMessage() + "'."
-                    );
-                }
-            }
-
-            @Override
-            public OffsetTime parseLiteral(Object input) throws CoercingParseLiteralException {
-                if (!(input instanceof StringValue)) {
-                    throw new CoercingParseLiteralException(
-                            "Expected AST type 'StringValue' but was '" + typeName(input) + "'."
-                    );
-                }
-                return parseOffsetTime(((StringValue) input).getValue(), CoercingParseLiteralException::new);
-            }
-
-            private OffsetTime parseOffsetTime(String s, Function exceptionMaker) {
-                try {
-                    TemporalAccessor temporalAccessor = DateTimeFormatter.ISO_OFFSET_TIME.parse(s);
-                    return OffsetTime.from(temporalAccessor);
-                } catch (DateTimeParseException e) {
-                    throw exceptionMaker
-                            .apply("Invalid RFC3339 full time value : '" + s + "'. because of : '" + e.getMessage() + "'");
-                }
-            }
-        });
-    }
-}
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index b69a71cb832..ab03e5002db 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -103,7 +103,7 @@ public static  DataFetcher newMethodDataFetcher(Schema schema, Class cl
                     sourceClazz = Class.forName(source);
                     listArgumentValues.add(sourceClazz.cast(environment.getSource()));
                 } catch (ClassNotFoundException e) {
-                    // this should not happen
+                    // this should not happen as class is validated previously
                 }
             }
 
@@ -382,8 +382,8 @@ protected static Object parseArgumentValue(Class originalType, String argumen
     /**
      * An implementation of a {@link PropertyDataFetcher} which returns a formatted number.
      */
-    public static class NumberFormattingDataFetcher
-            extends PropertyDataFetcher {
+    @SuppressWarnings("rawtypes")
+    public static class NumberFormattingDataFetcher extends PropertyDataFetcher {
 
         /**
          * {@link NumberFormat} to format with.
@@ -423,8 +423,7 @@ public Object get(DataFetchingEnvironment environment) {
      * An implementation of a {@link PropertyDataFetcher} which returns a formatted date.
      */
     @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static class DateFormattingDataFetcher
-            extends PropertyDataFetcher {
+    public static class DateFormattingDataFetcher extends PropertyDataFetcher {
 
         /**
          * {@link DateTimeFormatter} to format with.
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ElementGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ElementGenerator.java
index db5a2004801..fc3082fb4dd 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ElementGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ElementGenerator.java
@@ -25,7 +25,6 @@
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException;
 
-
 /**
  * An interface representing a class which can generate a GraphQL representation of it's state.
  */
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
index 1769fb4d49b..01b8f2e8677 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java
@@ -18,15 +18,12 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.logging.Logger;
 
-import graphql.execution.DataFetcherResult;
 import io.helidon.config.Config;
 
 import graphql.ExceptionWhileDataFetching;
@@ -43,6 +40,8 @@
 import org.eclipse.microprofile.graphql.ConfigKey;
 import org.eclipse.microprofile.graphql.GraphQLException;
 
+
+
 import static graphql.ExecutionInput.newExecutionInput;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getSafeClass;
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
index f7ad54927ea..968fa172ec4 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java
@@ -470,5 +470,4 @@ public static Object formatNumber(Object originalResult, boolean isScalar, Numbe
         }
         return numberFormat.format(originalResult);
     }
-
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingProvider.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingProvider.java
deleted file mode 100644
index 9a772d1df52..00000000000
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingProvider.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package io.helidon.microprofile.graphql.server;
-
-public interface FormattingProvider {
-}
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java
index e72f7b32c0a..5e967d69240 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java
@@ -70,7 +70,7 @@ void collectProcessors(@Observes @WithAnnotations(SchemaProcessor.class) Process
      *
      * @return the collected API's
      */
-    public Class[] collectedApis() {
+    public Class[] collectedApis() {
         return collectedApis.toArray(new Class[0]);
     }
 
@@ -79,7 +79,7 @@ public Class[] collectedApis() {
      *
      * @return the collected Schema Processors.
      */
-    public Class[] collectedSchemaProcessors() {
+    public Class[] collectedSchemaProcessors() {
         return collectedProcessors.toArray(new Class[0]);
     }
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java
index 8748d0a767c..f6bf333bb26 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java
@@ -40,8 +40,7 @@
 /**
  * The representation of a GraphQL Schema.
  */
-public class Schema
-        implements ElementGenerator {
+public class Schema implements ElementGenerator {
 
     private static final Logger LOGGER = Logger.getLogger(Schema.class.getName());
 
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java
index 5f71c892850..d8be6e1e37e 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java
@@ -19,13 +19,10 @@
 import java.util.Arrays;
 import java.util.Objects;
 
-
 /**
  * The representation of a GraphQL Argument or Parameter.
  */
-public class SchemaArgument
-        extends AbstractDescriptiveElement
-        implements ElementGenerator {
+public class SchemaArgument extends AbstractDescriptiveElement implements ElementGenerator {
     /**
      * Argument name.
      */
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java
index f2c776c471b..ab0f2bf0855 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java
@@ -27,8 +27,7 @@
 /**
  * The representation of a GraphQL directive.
  */
-public class SchemaDirective
-        implements ElementGenerator {
+public class SchemaDirective implements ElementGenerator {
 
     /**
      * The name of the directive.
@@ -159,5 +158,4 @@ public String toString() {
                 + ", setLocations=" + setLocations
                 + '}';
     }
-
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java
index bce15b3d433..e49901f5fd0 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java
@@ -19,13 +19,10 @@
 import java.util.ArrayList;
 import java.util.List;
 
-
 /**
  * The representation of a GraphQL Enum.
  */
-public class SchemaEnum
-        extends AbstractDescriptiveElement
-        implements ElementGenerator {
+public class SchemaEnum extends AbstractDescriptiveElement implements ElementGenerator {
 
     /**
      * The name of the enum.
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java
index 9b4ce9dc902..ea07e1502f6 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java
@@ -26,9 +26,7 @@
 /**
  * The representation of a GraphQL Field Definition.
  */
-public class SchemaFieldDefinition
-        extends AbstractDescriptiveElement
-        implements ElementGenerator {
+public class SchemaFieldDefinition extends AbstractDescriptiveElement implements ElementGenerator {
     /**
      * Name of the field definition.
      */
@@ -387,6 +385,8 @@ public void setJsonbProperty(boolean isJsonbProperty) {
 
     /**
      * Indicates if the property has a JsonbProperty annotation.
+     *
+     * @return true if the property has a JsonbProperty annotation
      */
     public boolean isJsonbProperty() {
         return isJsonbProperty;
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
index 70b96654064..cb904209740 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java
@@ -49,7 +49,6 @@
 import graphql.schema.DataFetcherFactories;
 import graphql.schema.GraphQLScalarType;
 import graphql.schema.PropertyDataFetcher;
-
 import org.eclipse.microprofile.graphql.Description;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Id;
@@ -1073,19 +1072,16 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
                             isArrayReturnTypeMandatory = true;
                         }
 
-                        // if the set method has a format then this should overwrite any formatting
-                        // as this is an InputType
+                        // if the set method has a format then this should overwrite any formatting as this is an InputType
                         String[] writeMethodFormat = getFormattingAnnotation(writeMethod);
-
                         if (!isFormatEmpty(writeMethodFormat)) {
                             format = writeMethodFormat;
                             isJsonbFormat = isJsonbAnnotationPresent(writeMethod);
                             isJsonbProperty = writeMethod.getAnnotation(JsonbProperty.class) != null;
                         }
-                        // also check the parameter 0 which there will be one as this is a write Method
+
                         Parameter[] parameters = writeMethod.getParameters();
                         if (parameters.length == 1) {
-                            // this should always be true
                             String[] argumentTypeFormat = FormattingHelper.getMethodParameterFormat(parameters[0], 0);
                             if (!isFormatEmpty(argumentTypeFormat)) {
                                 format = argumentTypeFormat;
@@ -1157,9 +1153,31 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
         }
 
         processMethodParameters(method, discoveredMethod, annotatedName);
-
-        // process the return type for the method
         ReturnType realReturnType = getReturnType(returnClazz, method.getGenericReturnType(), -1, method);
+        processReturnType(discoveredMethod, realReturnType, returnClazzName, isInputType, varName, method);
+
+        discoveredMethod.setReturnTypeMandatory(isReturnTypeMandatory);
+        discoveredMethod.setArrayReturnTypeMandatory(isArrayReturnTypeMandatory
+                                                             || realReturnType.isReturnTypeMandatory && !isInputType);
+        discoveredMethod.setDescription(description);
+
+        return discoveredMethod;
+    }
+
+    /**
+     * Process the {@link ReturnType} and update {@link DiscoveredMethod} as required.
+     * @param discoveredMethod  {@link DiscoveredMethod}
+     * @param realReturnType    {@link ReturnType} with details of the return types
+     * @param returnClazzName   return class name
+     * @param isInputType       indicates if the method is part of an input type
+     * @param varName           name of the variable
+     * @param method            {@link Method} being processed
+     *                                        
+     * @throws ClassNotFoundException if any class not found
+     */
+    private void processReturnType(DiscoveredMethod discoveredMethod, ReturnType realReturnType,
+                                   String returnClazzName, boolean isInputType,
+                                   String varName, Method method) throws ClassNotFoundException {
         if (realReturnType.getReturnClass() != null && !ID.equals(returnClazzName)) {
             discoveredMethod.setArrayReturnType(realReturnType.isArrayType());
             discoveredMethod.setCollectionType(realReturnType.getCollectionType());
@@ -1169,10 +1187,10 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
                 // only set the original array type if it's a date/time
                 discoveredMethod.setOriginalArrayType(Class.forName(realReturnType.returnClass));
             } else if (discoveredMethod.isArrayReturnType) {
-               Class originalArrayType = getSafeClass(realReturnType.returnClass);
-               if (originalArrayType != null) {
-                   discoveredMethod.setOriginalArrayType(originalArrayType);
-               }
+                Class originalArrayType = getSafeClass(realReturnType.returnClass);
+                if (originalArrayType != null) {
+                    discoveredMethod.setOriginalArrayType(originalArrayType);
+                }
             }
             discoveredMethod.setReturnType(realReturnType.getReturnClass());
             // only override if this is not an input type
@@ -1186,12 +1204,6 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz,
         }
 
         discoveredMethod.setArrayLevels(realReturnType.getArrayLevels());
-        discoveredMethod.setReturnTypeMandatory(isReturnTypeMandatory);
-        discoveredMethod.setArrayReturnTypeMandatory(isArrayReturnTypeMandatory
-                                                             || realReturnType.isReturnTypeMandatory && !isInputType);
-        discoveredMethod.setDescription(description);
-
-        return discoveredMethod;
     }
 
     /**
@@ -1599,6 +1611,8 @@ public void setJsonbProperty(boolean isJsonbProperty) {
 
         /**
          * Indicates if the property has a JsonbProperty annotation.
+         *
+         * @return true if the property has a JsonbProperty annotation
          */
         public boolean isJsonbProperty() {
             return isJsonbProperty;
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
index cbfb8f6117c..b01c6288f95 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java
@@ -16,6 +16,9 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import javax.json.bind.annotation.JsonbProperty;
+import javax.json.bind.annotation.JsonbTransient;
+
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.AnnotatedParameterizedType;
@@ -38,9 +41,6 @@
 import java.util.Map;
 import java.util.logging.Logger;
 
-import javax.json.bind.annotation.JsonbProperty;
-import javax.json.bind.annotation.JsonbTransient;
-
 import graphql.scalars.ExtendedScalars;
 import org.eclipse.microprofile.graphql.DefaultValue;
 import org.eclipse.microprofile.graphql.Description;
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java
index d39323cc81d..a70be8d9def 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java
@@ -19,8 +19,7 @@
 /**
  * The representation of a GraphQL Input Type.
  */
-public class SchemaInputType
-        extends SchemaType {
+public class SchemaInputType extends SchemaType {
 
     /**
      * Construct an {@link SchemaInputType}.
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java
index 181e9d09926..f9434ae5cbb 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java
@@ -23,8 +23,7 @@
 /**
  * The representation of a GraphQL Scalar.
  */
-public class SchemaScalar
-        implements ElementGenerator {
+public class SchemaScalar implements ElementGenerator {
 
     /**
      * Name of the Scalar.
@@ -140,5 +139,4 @@ public String toString() {
                 + ", defaultFormat='" + defaultFormat + '\''
                 + ", graphQLScalarType=" + graphQLScalarType + '}';
     }
-
 }
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java
index 3051c9587fb..44e20cf4c09 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java
@@ -23,9 +23,7 @@
 /**
  * The representation of a GraphQL Type.
  */
-public class SchemaType
-        extends AbstractDescriptiveElement
-        implements ElementGenerator {
+public class SchemaType extends AbstractDescriptiveElement implements ElementGenerator {
 
     /**
      * Name of the type.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
index b691cee7e00..62f0da76125 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
@@ -23,7 +23,6 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import io.helidon.microprofile.server.ServerCdiExtension;
 import javax.enterprise.inject.spi.CDI;
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
@@ -31,10 +30,10 @@
 import javax.ws.rs.core.Response;
 
 import io.helidon.microprofile.cdi.Main;
+import io.helidon.microprofile.server.ServerCdiExtension;
 
 import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.logging.LoggingFeature;
-
 import org.junit.jupiter.api.AfterAll;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -115,7 +114,12 @@ protected static WebTarget getGraphQLWebTarget() {
         return client.target(graphQLUrl);
     }
 
-    protected String encode(String param) throws UnsupportedEncodingException {
+    /**
+     * Encode the { and }.
+     * @param param {@link String} to encode
+     * @return an encoded @link String}
+     */
+    protected String encode(String param)  {
         return param == null ? null : param.replaceAll("}", "%7D").replaceAll("\\{", "%7B");
     }
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java
index fb3128be975..cb338a5553a 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java
@@ -16,19 +16,20 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.beans.IntrospectionException;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
 import io.helidon.microprofile.tests.junit5.AddExtension;
 import io.helidon.microprofile.tests.junit5.DisableDiscovery;
 import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
-import java.beans.IntrospectionException;
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -39,8 +40,7 @@
 @HelidonTest
 @DisableDiscovery
 @AddExtension(GraphQLCdiExtension.class)
-public abstract class AbstractGraphQLIT
-        extends AbstractGraphQLTest {
+public abstract class AbstractGraphQLIT extends AbstractGraphQLTest {
 
     protected String indexFileName = null;
     protected File indexFile = null;
@@ -61,21 +61,6 @@ public void teardownTest() {
         }
     }
 
-    protected void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException {
-        SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext);
-        Schema schema = schemaGenerator.generateSchema();
-        assertThat(schema, CoreMatchers.is(notNullValue()));
-        schema.getTypes().forEach(t -> System.out.println(t.getName()));
-        assertThat(schema.getTypes().size(), CoreMatchers.is(6));
-        assertThat(schema.getTypeByName("Vehicle"), CoreMatchers.is(notNullValue()));
-        assertThat(schema.getTypeByName("Car"), CoreMatchers.is(notNullValue()));
-        assertThat(schema.getTypeByName("Motorbike"), CoreMatchers.is(notNullValue()));
-        assertThat(schema.getTypeByName("Incident"), CoreMatchers.is(notNullValue()));
-        assertThat(schema.getTypeByName("Query"), CoreMatchers.is(notNullValue()));
-        assertThat(schema.getTypeByName("Mutation"), CoreMatchers.is(notNullValue()));
-        generateGraphQLSchema(schema);
-    }
-
     @SuppressWarnings("unchecked")
     protected void assertMessageValue(String query, String expectedMessage, boolean dataExpected) {
         ExecutionContext executionContext = new ExecutionContext(new DefaultContext());
@@ -92,4 +77,19 @@ protected void assertMessageValue(String query, String expectedMessage, boolean
 
         assertThat(mapResults.containsKey(ExecutionContext.DATA), is(dataExpected));
     }
+
+    protected void assertInterfaceResults() throws IntrospectionException, ClassNotFoundException {
+        SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext);
+        Schema schema = schemaGenerator.generateSchema();
+        assertThat(schema, CoreMatchers.is(notNullValue()));
+        schema.getTypes().forEach(t -> System.out.println(t.getName()));
+        assertThat(schema.getTypes().size(), CoreMatchers.is(6));
+        assertThat(schema.getTypeByName("Vehicle"), CoreMatchers.is(notNullValue()));
+        assertThat(schema.getTypeByName("Car"), CoreMatchers.is(notNullValue()));
+        assertThat(schema.getTypeByName("Motorbike"), CoreMatchers.is(notNullValue()));
+        assertThat(schema.getTypeByName("Incident"), CoreMatchers.is(notNullValue()));
+        assertThat(schema.getTypeByName("Query"), CoreMatchers.is(notNullValue()));
+        assertThat(schema.getTypeByName("Mutation"), CoreMatchers.is(notNullValue()));
+        generateGraphQLSchema(schema);
+    }
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java
index 3da10f4078b..29dec18fcaa 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java
@@ -16,7 +16,6 @@
 
 package io.helidon.microprofile.graphql.server;
 
-import java.beans.IntrospectionException;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -32,6 +31,7 @@
 import graphql.schema.idl.SchemaPrinter;
 
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+
 import org.hamcrest.CoreMatchers;
 import org.jboss.jandex.Index;
 import org.jboss.jandex.IndexWriter;
@@ -146,7 +146,6 @@ protected SchemaPrinter getSchemaPrinter() {
      * Setup an index file for the given {@link Class}es.
      *
      * @param clazzes classes to setup index for
-     * @return a {@link JandexUtils} instance
      * @throws IOException
      */
     protected static void setupIndex(String indexFileName, Class... clazzes) throws IOException {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
index 9874196b1d6..b88a990cd88 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
@@ -16,15 +16,14 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
index c4dc4e763f8..bc1532b852a 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
@@ -16,17 +16,16 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
 import io.helidon.microprofile.tests.junit5.AddConfig;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.eclipse.microprofile.graphql.ConfigKey;
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -43,7 +42,7 @@
 public class BLListAndWLExceptionIT extends AbstractGraphQLIT {
 
     @Test
-    public void testBlackListAndWhiteList() throws IOException {
+    public void tesDenyListAndAllowList() throws IOException {
         setupIndex(indexFileName);
 
         ExecutionContext executionContext = new ExecutionContext(defaultContext);
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
index 97b08e155a4..b9c27c9761d 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
@@ -16,6 +16,8 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
@@ -25,8 +27,6 @@
 import org.eclipse.microprofile.graphql.ConfigKey;
 import org.junit.jupiter.api.Test;
 
-import java.io.IOException;
-
 /**
  * Tests for deny list.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
index 97d115811c3..f8d265f1f7b 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
@@ -16,10 +16,15 @@
 
 package io.helidon.microprofile.graphql.server;
 
-import static io.helidon.microprofile.graphql.server.JsonUtils.convertObjectToMap;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
@@ -30,20 +35,12 @@
 
 import io.helidon.microprofile.tests.junit5.AddBean;
 
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
 import org.junit.jupiter.api.Test;
 
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
+import static io.helidon.microprofile.graphql.server.JsonUtils.convertObjectToMap;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
 
 /**
  * Tests for {@link DataFetcherUtils} class.
@@ -310,5 +307,4 @@ protected SchemaArgument getArgument(Schema schema, String typeName, String fdNa
         }
         return null;
     }
-
 }
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
index 5fbae30312e..f178881c6eb 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
@@ -17,12 +17,19 @@
 package io.helidon.microprofile.graphql.server;
 
 import java.io.IOException;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
+import io.helidon.microprofile.graphql.server.test.db.TestDB;
+import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesAndMutations;
+import io.helidon.microprofile.graphql.server.test.types.DateTimePojo;
+import io.helidon.microprofile.graphql.server.test.types.SimpleDateTime;
+import io.helidon.microprofile.tests.junit5.AddBean;
+
+import org.junit.jupiter.api.Test;
+
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATETIME_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR;
 import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR;
@@ -33,15 +40,6 @@
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import io.helidon.microprofile.graphql.server.test.db.TestDB;
-import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesAndMutations;
-import io.helidon.microprofile.graphql.server.test.types.DateTimePojo;
-
-import io.helidon.microprofile.graphql.server.test.types.SimpleDateTime;
-import io.helidon.microprofile.tests.junit5.AddBean;
-
-import org.junit.jupiter.api.Test;
-
 /**
  * Tests for date/times.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
index 1090f2a3444..e146df853ac 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
@@ -21,10 +21,6 @@
 import java.util.List;
 import java.util.Map;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.queries.DateTimeScalarQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleDateTimePojo;
@@ -32,6 +28,10 @@
 
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
 /**
  * Tests for date/time scalars.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
index 425b9727136..88e5392b301 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
@@ -16,13 +16,14 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 /**
  * Tests for default exceptions.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
index 93a26a3b279..f97d7843e3a 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
@@ -19,10 +19,6 @@
 import java.io.IOException;
 import java.util.Map;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.queries.DefaultValueQueries;
 import io.helidon.microprofile.graphql.server.test.queries.OddNamedQueriesAndMutations;
@@ -31,6 +27,9 @@
 
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
 
 /**
  * Tests for default values.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
index f82739ff4b6..db6dfe6743b 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
@@ -18,11 +18,6 @@
 
 import java.io.IOException;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.queries.DescriptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.DescriptionType;
@@ -31,6 +26,11 @@
 
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
 /**
  * Tests for descriptions.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
index 40e31d1cae5..c80a0ea7fbb 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
@@ -16,6 +16,8 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
@@ -23,7 +25,6 @@
 import io.helidon.microprofile.tests.junit5.AddConfig;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
index 0feb2cc4d1f..5b73bcffc27 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
@@ -16,12 +16,13 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
 import io.helidon.microprofile.graphql.server.test.queries.DuplicateNameQueries;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
index 24b1e9d45c0..0cc088273ea 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
@@ -17,11 +17,9 @@
 package io.helidon.microprofile.graphql.server;
 
 import java.io.IOException;
-
 import java.util.List;
 import java.util.Map;
 
-
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java
index f8a1991e4a1..dc92a129c68 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java
@@ -20,6 +20,11 @@
 import java.io.UnsupportedEncodingException;
 import java.util.Map;
 
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
 import io.helidon.microprofile.config.ConfigCdiExtension;
 import io.helidon.microprofile.server.JaxRsCdiExtension;
 import io.helidon.microprofile.server.ServerCdiExtension;
@@ -28,16 +33,11 @@
 import io.helidon.microprofile.tests.junit5.AddExtension;
 import io.helidon.microprofile.tests.junit5.DisableDiscovery;
 import io.helidon.microprofile.tests.junit5.HelidonTest;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
 
 import io.helidon.microprofile.graphql.server.test.types.Person;
 
 import org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider;
 import org.hamcrest.CoreMatchers;
-
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
@@ -102,7 +102,6 @@ public void basicEndpointTests()
 
     @Test
     public void testUIEndpoint() {
-        // test /ui endpoint
         WebTarget webTarget = getGraphQLWebTarget().path(UI).path("index.html");
         Response response = webTarget.request().get();
         assertThat(response, is(notNullValue()));
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
index 39dd5df050f..345e325d992 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
@@ -17,7 +17,6 @@
 package io.helidon.microprofile.graphql.server;
 
 import java.io.IOException;
-
 import java.util.Map;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -31,7 +30,6 @@
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-
 import org.opentest4j.AssertionFailedError;
 
 /**
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
index 95bf0bfce80..89cefedc6b7 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
@@ -25,7 +25,6 @@
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithAddress;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithName;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactInputTypeWithNameValue;
-
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
index 34c0364c895..75eed0c33ac 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
@@ -27,8 +27,12 @@
 
 import io.helidon.microprofile.tests.junit5.AddBean;
 
+import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
 /**
  * Tests for discovery of interfaces when only the interface annotated.
  */
@@ -38,7 +42,7 @@
 @AddBean(AbstractVehicle.class)
 @AddBean(TestDB.class)
 public class InterfaceOnlyAnnotatedIT extends AbstractGraphQLIT {
-    
+
     @Test
     public void testInterfaceDiscoveryWithImplementorsWithNoTypeAnnotation()
             throws IOException, IntrospectionException, ClassNotFoundException {
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
index 5a72b4ff315..556475943f3 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
@@ -18,13 +18,13 @@
 
 import java.beans.IntrospectionException;
 import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle;
 import io.helidon.microprofile.graphql.server.test.types.Car;
 import io.helidon.microprofile.graphql.server.test.types.Motorbike;
 import io.helidon.microprofile.graphql.server.test.types.Vehicle;
 import io.helidon.microprofile.graphql.server.test.types.VehicleIncident;
-
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
index 4a1311265cc..c2aaa4a68ca 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
@@ -16,11 +16,12 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
index 59d42b4041d..df80633bdd2 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
@@ -16,11 +16,12 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
index 9139a7cbbd4..8f034988d00 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
@@ -16,11 +16,12 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
index 1b7af5b5b02..533301d26f7 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
@@ -16,11 +16,12 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
index 48ce4b64c99..9dfbbc58ff9 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
@@ -16,11 +16,12 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
index 17609f913cf..dacce275774 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
@@ -16,11 +16,12 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.types.InvalidNamedTypes;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
index b0f4b60d046..f54296ff2a2 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
@@ -16,12 +16,13 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
 import io.helidon.microprofile.graphql.server.test.queries.InvalidQueries;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java
index 43b15fe89e1..6afb08849a8 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java
@@ -18,10 +18,8 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.Set;
 
-import io.helidon.microprofile.graphql.server.AbstractGraphQLTest;
 import org.jboss.jandex.ClassInfo;
 import org.jboss.jandex.DotName;
 import org.jboss.jandex.Index;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JsonUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JsonUtilsTest.java
index 816f5fce56a..da6a2800bd9 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JsonUtilsTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JsonUtilsTest.java
@@ -20,12 +20,8 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
 import java.util.TreeMap;
-import java.util.TreeSet;
 
-import io.helidon.microprofile.graphql.server.AbstractGraphQLTest;
-import org.antlr.v4.runtime.tree.Tree;
 import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.Test;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
index 7729ea3097f..062b40bf4a7 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
@@ -32,7 +32,6 @@
 
 import org.junit.jupiter.api.Test;
 
-
 /**
  * Tests for Nulls.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
index e3ddd168fc8..983eea71572 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
@@ -22,14 +22,6 @@
 import java.util.List;
 import java.util.Map;
 
-import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR;
-import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT;
-import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.queries.NumberFormatQueriesAndMutations;
 import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithArgs;
@@ -39,6 +31,14 @@
 
 import org.junit.jupiter.api.Test;
 
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.INT;
+import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.STRING;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
 /**
  * Tests for Number formats.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java
index d0b4117ba09..9374e18e35c 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java
@@ -19,10 +19,10 @@
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
-
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
index 9ac4ef604c0..e26ec7c4f70 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
@@ -16,16 +16,15 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.beans.IntrospectionException;
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.types.Person;
 import io.helidon.microprofile.graphql.server.test.types.PersonWithName;
-
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
 
-import java.beans.IntrospectionException;
-import java.io.IOException;
-
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
index c375ddc1dd8..503f3592f85 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
@@ -19,9 +19,6 @@
 import java.io.IOException;
 import java.util.Map;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.queries.PropertyNameQueries;
 import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty;
@@ -30,6 +27,9 @@
 
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
 /**
  * Tests for property naming.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java
index 4f4254d25ac..55655240dd3 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java
@@ -18,8 +18,7 @@
 
 import graphql.schema.DataFetcher;
 import graphql.schema.StaticDataFetcher;
-import io.helidon.microprofile.graphql.server.SchemaArgument;
-import io.helidon.microprofile.graphql.server.SchemaFieldDefinition;
+
 import org.junit.jupiter.api.Test;
 
 import static org.hamcrest.CoreMatchers.is;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java
index e507304e37e..2ac56c15c4c 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java
@@ -22,7 +22,6 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Parameter;
 import java.lang.reflect.ParameterizedType;
-
 import java.math.BigDecimal;
 import java.text.NumberFormat;
 import java.time.LocalDate;
@@ -32,6 +31,8 @@
 import java.util.List;
 import java.util.Map;
 
+import javax.json.bind.annotation.JsonbNumberFormat;
+
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestNoEnumName;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAndNameAnnotation;
@@ -62,7 +63,6 @@
 import io.helidon.microprofile.graphql.server.test.types.Vehicle;
 import io.helidon.microprofile.graphql.server.test.types.VehicleIncident;
 
-import javax.json.bind.annotation.JsonbNumberFormat;
 import org.eclipse.microprofile.graphql.DateFormat;
 import org.eclipse.microprofile.graphql.Name;
 import org.eclipse.microprofile.graphql.NonNull;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java
index 828698f5c2a..e633a5bc68a 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java
@@ -30,7 +30,6 @@
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.CoreMatchers.is;
-
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 /**
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
index df0f333151d..8ee4cb8faa1 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
@@ -19,17 +19,16 @@
 import java.io.IOException;
 import java.util.Map;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.mutations.SimpleMutations;
-
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
 /**
  * Tests for simple mutations.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
index 85a79b09d37..65426e099b2 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
@@ -25,10 +25,6 @@
 import java.util.Map;
 import java.util.UUID;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation;
@@ -36,11 +32,15 @@
 import io.helidon.microprofile.graphql.server.test.types.AbstractVehicle;
 import io.helidon.microprofile.graphql.server.test.types.Car;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
-
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+
 /**
  * Tests for simple queries with args.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
index 899e5a40dd4..966fde0fee6 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
@@ -19,10 +19,6 @@
 import java.io.IOException;
 import java.util.Map;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.queries.SimpleQueriesWithSource;
@@ -32,6 +28,9 @@
 
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
 
 /**
  * Tests for Source annotation.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
index 50073f6701c..8d86922ad8a 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
@@ -16,11 +16,12 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
index 229f700cd37..0d60af887fd 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
@@ -16,12 +16,13 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.mutations.VoidMutations;
 import io.helidon.microprofile.graphql.server.test.queries.VoidQueries;
 import io.helidon.microprofile.tests.junit5.AddBean;
 
 import org.junit.jupiter.api.Test;
-import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
index 5e51fa96aee..46c03e0af24 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
@@ -16,6 +16,8 @@
 
 package io.helidon.microprofile.graphql.server;
 
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.exception.ExceptionQueries;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
@@ -25,7 +27,6 @@
 import org.eclipse.microprofile.graphql.ConfigKey;
 import org.junit.jupiter.api.Test;
 
-import java.io.IOException;
 
 /**
  * Tests for allow list of checked exceptions.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
index 63ce96ff118..1207fee8ff7 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/exception/ExceptionQueries.java
@@ -19,19 +19,21 @@
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
-import org.eclipse.microprofile.graphql.GraphQLApi;
-import org.eclipse.microprofile.graphql.GraphQLException;
-import org.eclipse.microprofile.graphql.Mutation;
-import org.eclipse.microprofile.graphql.Name;
-import org.eclipse.microprofile.graphql.Query;
-import java.io.IOError;
+
 import java.io.IOException;
 import java.security.AccessControlException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.microprofile.graphql.GraphQLApi;
+import org.eclipse.microprofile.graphql.GraphQLException;
+import org.eclipse.microprofile.graphql.Name;
+import org.eclipse.microprofile.graphql.Query;
+
+
 /**
  * Class that holds queries that raise various exceptions.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java
index d9da658ee4d..b6f8f27f264 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/mutations/SimpleMutations.java
@@ -19,13 +19,15 @@
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 
+import java.util.Set;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
-import javax.json.bind.annotation.JsonbNumberFormat;
+
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Mutation;
 import org.eclipse.microprofile.graphql.Name;
-import java.util.Set;
+
 
 /**
  * Class that holds simple mutations definitions with various numbers of arguments.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java
index 572ab963dc6..d49f86e63f4 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java
@@ -16,6 +16,9 @@
 
 package io.helidon.microprofile.graphql.server.test.queries;
 
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -23,8 +26,6 @@
 import java.util.List;
 
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DateTimeScalarQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DateTimeScalarQueries.java
index 2498100a77b..c266ac787a1 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DateTimeScalarQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DateTimeScalarQueries.java
@@ -16,17 +16,17 @@
 
 package io.helidon.microprofile.graphql.server.test.queries;
 
-import java.time.LocalDate;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.json.bind.annotation.JsonbDateFormat;
 
+import java.time.LocalDate;
 import java.time.LocalTime;
 import java.util.List;
+
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.types.SimpleDateTimePojo;
 
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
-
-import javax.json.bind.annotation.JsonbDateFormat;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Mutation;
 import org.eclipse.microprofile.graphql.Name;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java
index 3d4409e80cb..83f8af2f7ab 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DefaultValueQueries.java
@@ -21,7 +21,7 @@
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.types.DefaultValuePOJO;
-import io.helidon.microprofile.graphql.server.test.types.DescriptionType;
+
 import org.eclipse.microprofile.graphql.DefaultValue;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Mutation;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DuplicateNameQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DuplicateNameQueries.java
index 31a2c975ef7..9e8025dde67 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DuplicateNameQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/DuplicateNameQueries.java
@@ -17,6 +17,7 @@
 package io.helidon.microprofile.graphql.server.test.queries;
 
 import javax.enterprise.context.ApplicationScoped;
+
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Mutation;
 import org.eclipse.microprofile.graphql.Query;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java
index bfc2470af2e..fd805d67e9f 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/NumberFormatQueriesAndMutations.java
@@ -18,6 +18,11 @@
 
 import javax.enterprise.context.ApplicationScoped;
 
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats;
 import javax.json.bind.annotation.JsonbNumberFormat;
 import org.eclipse.microprofile.graphql.GraphQLApi;
@@ -25,11 +30,6 @@
 import org.eclipse.microprofile.graphql.Name;
 import org.eclipse.microprofile.graphql.NumberFormat;
 import org.eclipse.microprofile.graphql.Query;
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * Class that holds queries and mutations that have various formatting types.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/PropertyNameQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/PropertyNameQueries.java
index 57755c60920..235502a9ca0 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/PropertyNameQueries.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/PropertyNameQueries.java
@@ -17,10 +17,11 @@
 package io.helidon.microprofile.graphql.server.test.queries;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
-import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods;
 import io.helidon.microprofile.graphql.server.test.types.TypeWithNameAndJsonbProperty;
+
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
+
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Name;
 import org.eclipse.microprofile.graphql.Query;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesAndMutationsWithNulls.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesAndMutationsWithNulls.java
index 319ce716786..982c754dbec 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesAndMutationsWithNulls.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesAndMutationsWithNulls.java
@@ -17,17 +17,20 @@
 package io.helidon.microprofile.graphql.server.test.queries;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
-import javax.enterprise.context.ApplicationScoped;
-
 import io.helidon.microprofile.graphql.server.test.types.NullPOJO;
+
+import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
+
+import java.time.LocalDate;
+
 import org.eclipse.microprofile.graphql.DefaultValue;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Mutation;
 import org.eclipse.microprofile.graphql.Name;
 import org.eclipse.microprofile.graphql.NonNull;
 import org.eclipse.microprofile.graphql.Query;
-import java.time.LocalDate;
+
 
 /**
  * Class that holds queries that also have Null.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithIgnorable.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithIgnorable.java
index b295a4d128f..96d348f67e2 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithIgnorable.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/QueriesWithIgnorable.java
@@ -16,31 +16,13 @@
 
 package io.helidon.microprofile.graphql.server.test.queries;
 
-import java.math.BigDecimal;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.OffsetDateTime;
-import java.time.OffsetTime;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.UUID;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
-import javax.json.bind.annotation.JsonbProperty;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
-import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
-import io.helidon.microprofile.graphql.server.test.types.DateTimePojo;
-import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays;
 import io.helidon.microprofile.graphql.server.test.types.ObjectWithIgnorableFieldsAndMethods;
-import io.helidon.microprofile.graphql.server.test.types.Person;
-import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf;
-import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs;
+
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Name;
 import org.eclipse.microprofile.graphql.Query;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
index 28b23e10229..2d8af5f48ac 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesAndMutations.java
@@ -16,35 +16,32 @@
 
 package io.helidon.microprofile.graphql.server.test.queries;
 
-import java.math.BigDecimal;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-
-import java.time.OffsetDateTime;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import java.util.UUID;
-
-import io.helidon.microprofile.graphql.server.test.types.SimpleDateTime;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.json.bind.annotation.JsonbDateFormat;
 import javax.json.bind.annotation.JsonbProperty;
 
+import io.helidon.microprofile.graphql.server.test.types.SimpleDateTime;
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
 import io.helidon.microprofile.graphql.server.test.types.DateTimePojo;
 import io.helidon.microprofile.graphql.server.test.types.MultiLevelListsAndArrays;
-
 import io.helidon.microprofile.graphql.server.test.types.Person;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithSelf;
 import io.helidon.microprofile.graphql.server.test.types.TypeWithIDs;
 
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
 import org.eclipse.microprofile.graphql.DateFormat;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Id;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java
index d02e47a3007..c69611774e0 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java
@@ -16,29 +16,28 @@
 
 package io.helidon.microprofile.graphql.server.test.queries;
 
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.time.LocalDate;
-import java.util.ArrayList;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.UUID;
-import java.util.stream.Collectors;
-
-import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
-
 import javax.json.bind.annotation.JsonbNumberFormat;
 import javax.json.bind.annotation.JsonbProperty;
 
+import io.helidon.microprofile.graphql.server.test.types.SimpleContactWithNumberFormats;
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation;
 import io.helidon.microprofile.graphql.server.test.types.ContactRelationship;
 import io.helidon.microprofile.graphql.server.test.types.Person;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
 import org.eclipse.microprofile.graphql.DateFormat;
 import org.eclipse.microprofile.graphql.GraphQLApi;
 import org.eclipse.microprofile.graphql.Id;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithSource.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithSource.java
index 7dfde02f750..39746847f6a 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithSource.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithSource.java
@@ -16,28 +16,19 @@
 
 package io.helidon.microprofile.graphql.server.test.queries;
 
-import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-import java.util.stream.Collectors;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
-import javax.json.bind.annotation.JsonbProperty;
 
 import io.helidon.microprofile.graphql.server.test.db.TestDB;
-import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithNameAnnotation;
 import io.helidon.microprofile.graphql.server.test.types.Address;
-import io.helidon.microprofile.graphql.server.test.types.ContactRelationship;
-import io.helidon.microprofile.graphql.server.test.types.Person;
 import io.helidon.microprofile.graphql.server.test.types.SimpleContact;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
 import org.eclipse.microprofile.graphql.GraphQLApi;
-import org.eclipse.microprofile.graphql.Id;
 import org.eclipse.microprofile.graphql.Name;
 import org.eclipse.microprofile.graphql.Query;
 import org.eclipse.microprofile.graphql.Source;
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java
index da127e8a271..9153ffb7571 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/DateTimePojo.java
@@ -22,9 +22,7 @@
 import java.time.OffsetDateTime;
 import java.time.OffsetTime;
 import java.time.ZonedDateTime;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 
 import javax.json.bind.annotation.JsonbDateFormat;
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/PersonWithName.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/PersonWithName.java
index ac0bdbd525e..41b5eee37d9 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/PersonWithName.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/PersonWithName.java
@@ -18,7 +18,6 @@
 
 import org.eclipse.microprofile.graphql.Type;
 
-
 /**
  * Class to test Type annotation with value.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java
index 2f62d81c63d..899e26172d1 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContact.java
@@ -17,9 +17,11 @@
 package io.helidon.microprofile.graphql.server.test.types;
 
 import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName;
-import javax.json.bind.annotation.JsonbProperty;
+
 import java.util.Objects;
 
+import javax.json.bind.annotation.JsonbProperty;
+
 /**
  * Defines a simple contact.
  */
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java
index 56e036179b8..f469d9d7cbd 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleContactWithNumberFormats.java
@@ -16,15 +16,17 @@
 
 package io.helidon.microprofile.graphql.server.test.types;
 
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Objects;
+
 import javax.json.bind.annotation.JsonbNumberFormat;
 
 import org.eclipse.microprofile.graphql.DateFormat;
 import org.eclipse.microprofile.graphql.NumberFormat;
 import org.eclipse.microprofile.graphql.Type;
-import java.math.BigDecimal;
-import java.time.LocalDate;
-import java.util.List;
-import java.util.Objects;
+
 
 /**
  * Defines a simple contact which contains a number formats.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTime.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTime.java
index 55bfc36fedb..02ba3a8e64d 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTime.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTime.java
@@ -16,11 +16,12 @@
 
 package io.helidon.microprofile.graphql.server.test.types;
 
+import java.time.LocalDate;
+import java.util.List;
+
 import org.eclipse.microprofile.graphql.DateFormat;
 import org.eclipse.microprofile.graphql.Name;
 import org.eclipse.microprofile.graphql.Type;
-import java.time.LocalDate;
-import java.util.List;
 
 /**
  * Class representing a simple date/time type.
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTimePojo.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTimePojo.java
index 2f4e9898831..fc494cae10a 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTimePojo.java
+++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/SimpleDateTimePojo.java
@@ -17,15 +17,9 @@
 package io.helidon.microprofile.graphql.server.test.types;
 
 import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.OffsetDateTime;
-import java.time.OffsetTime;
-import java.time.ZonedDateTime;
 import java.util.List;
-import javax.json.bind.annotation.JsonbDateFormat;
+
 import org.eclipse.microprofile.graphql.DateFormat;
-import org.eclipse.microprofile.graphql.Description;
 import org.eclipse.microprofile.graphql.Type;
 
 /**

From 16af2244cdb419a87c8a9c29689d0e529b8be800 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Tue, 20 Oct 2020 14:06:50 +0800
Subject: [PATCH 118/178] Move Integration tests to
 tests/integration/mp-graphql

---
 microprofile/graphql/server/pom.xml           |  24 +--
 tests/integration/mp-graphql/pom.xml          | 176 ++++++++++++++++++
 .../server/AbstractGraphQLEndpointIT.java     |   4 +-
 .../graphql/server/AbstractGraphQLIT.java     |   0
 .../server/AllDefaultsExceptionIT.java        |   0
 .../server/BLListAndWLExceptionIT.java        |   0
 .../graphql/server/BLOfIOExceptionIT.java     |   0
 .../graphql/server/DataFetcherUtilsIT.java    |   0
 .../graphql/server/DateTimeIT.java            |   0
 .../graphql/server/DateTimeScalarIT.java      |   0
 .../server/DefaultCheckedExceptionIT.java     |   0
 .../graphql/server/DefaultValuesIT.java       |   0
 .../graphql/server/DescriptionIT.java         |   0
 .../server/DifferentMessageExceptionIT.java   |   0
 .../DuplicateQueriesAndMutationsIT.java       |   0
 .../graphql/server/ExceptionHandlingIT.java   |   0
 .../graphql/server/GraphQLEndpointIT.java     |   0
 .../graphql/server/IngorableIT.java           |   0
 .../graphql/server/InputTypeIT.java           |   0
 .../server/InterfaceOnlyAnnotatedIT.java      |   0
 .../server/InterfaceTypeOnlyAnnotatedIT.java  |   0
 .../graphql/server/InvalidNamedEnumIT.java    |   0
 .../server/InvalidNamedInputTypeIT.java       |   0
 .../server/InvalidNamedInterfaceIT.java       |   0
 .../server/InvalidNamedMutationIT.java        |   0
 .../graphql/server/InvalidNamedQueryIT.java   |   0
 .../graphql/server/InvalidNamedTypeIT.java    |   0
 .../graphql/server/InvalidQueriesIT.java      |   0
 .../microprofile/graphql/server/Level0IT.java |   0
 .../graphql/server/MultiLevelArraysIT.java    |   0
 .../microprofile/graphql/server/NullIT.java   |   0
 .../graphql/server/NumberFormatIT.java        |   0
 .../server/PartialResultsExceptionIT.java     |   0
 .../graphql/server/PojoNamingIT.java          |   0
 .../graphql/server/PropertyNameIT.java        |   0
 .../SchemaPreProcessorTestContainer.java      |   0
 .../graphql/server/SimpleMutationsIT.java     |   0
 .../server/SimpleQueriesWithArgsIT.java       |   0
 .../microprofile/graphql/server/SourceIT.java |   0
 .../graphql/server/VoidMutationsIT.java       |   0
 .../graphql/server/VoidQueriesIT.java         |   0
 .../server/WLOfCheckedExceptionIT.java        |   0
 .../src/test/resources/META-INF/beans.xml     |  26 +++
 .../META-INF/microprofile-config.properties   |  18 ++
 tests/integration/pom.xml                     |   1 +
 45 files changed, 225 insertions(+), 24 deletions(-)
 create mode 100644 tests/integration/mp-graphql/pom.xml
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java (97%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java (100%)
 rename {microprofile/graphql/server => tests/integration/mp-graphql}/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java (100%)
 create mode 100644 tests/integration/mp-graphql/src/test/resources/META-INF/beans.xml
 create mode 100644 tests/integration/mp-graphql/src/test/resources/META-INF/microprofile-config.properties

diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml
index 43a18b49be7..96225e26430 100644
--- a/microprofile/graphql/server/pom.xml
+++ b/microprofile/graphql/server/pom.xml
@@ -81,7 +81,7 @@
             test
         
         
-         io.helidon.microprofile.tests
+            io.helidon.microprofile.tests
             helidon-microprofile-tests-junit5
             test
         
@@ -121,29 +121,11 @@
 
             
                 org.apache.maven.plugins
-                maven-failsafe-plugin
-                
-                    
-                        
-                            listener
-                            org.sonar.java.jacoco.JUnitListener
-                        
-                    
-                    false
-                    
-                        **/*IT.java
-                    
-                    
-                        org.slf4j:slf4j-log4j12
-                    
-                
+                maven-jar-plugin
                 
                     
-                        verify
-                        verify
                         
-                            integration-test
-                            verify
+                            test-jar
                         
                     
                 
diff --git a/tests/integration/mp-graphql/pom.xml b/tests/integration/mp-graphql/pom.xml
new file mode 100644
index 00000000000..d0ef2b9de9d
--- /dev/null
+++ b/tests/integration/mp-graphql/pom.xml
@@ -0,0 +1,176 @@
+
+
+
+
+    4.0.0
+    
+        io.helidon.tests.integration
+        helidon-tests-integration
+        2.1.1-SNAPSHOT
+        ../pom.xml
+    
+
+    helidon-tests-integration-mp-graphql
+    Helidon Integration Test MP GraphQL
+
+    
+        
+            io.helidon.microprofile.bundles
+            helidon-microprofile-core
+            test
+        
+        
+            io.helidon.microprofile.config
+            helidon-microprofile-config
+            test
+        
+        
+            io.helidon.microprofile.server
+            helidon-microprofile-server
+            test
+        
+        
+            io.helidon.microprofile.metrics
+            helidon-microprofile-metrics
+            provided
+        
+        
+            org.junit.jupiter
+            junit-jupiter-api
+            test
+        
+        
+            org.hamcrest
+            hamcrest-all
+            test
+        
+        
+            org.mockito
+            mockito-core
+            test
+        
+        
+            io.helidon.microprofile.graphql
+            helidon-microprofile-graphql-server
+            test
+        
+        
+            io.helidon.microprofile.graphql
+            helidon-microprofile-graphql-server
+            test-jar
+            tests
+            ${project.version}
+            test
+        
+        
+            
+            org.slf4j
+            slf4j-jdk14
+            test
+        
+        
+            org.glassfish.jersey.core
+            jersey-client
+            test
+        
+        
+            io.helidon.microprofile.tests
+            helidon-microprofile-tests-junit5
+            test
+        
+    
+
+    
+        
+            
+                kr.motd.maven
+                os-maven-plugin
+                ${version.plugin.os}
+            
+        
+
+        
+            
+                org.xolstice.maven.plugins
+                protobuf-maven-plugin
+                
+                    
+                        
+                            test-compile
+                            test-compile-custom
+                        
+                    
+                
+            
+              
+                org.jboss.jandex
+                jandex-maven-plugin
+                
+                    
+                        make-index
+                        
+                            jandex
+                        
+                        
+                        integration-test
+                        
+                            
+                                
+                                    ${project.build.directory}/classes
+                                
+                                
+                                    ${project.build.directory}/test-classes
+                                
+                            
+                        
+                    
+                
+            
+
+            
+                org.apache.maven.plugins
+                maven-failsafe-plugin
+                
+                    
+                        
+                            listener
+                            org.sonar.java.jacoco.JUnitListener
+                        
+                    
+                    false
+                    
+                        **/*IT.java
+                    
+                    
+                        org.slf4j:slf4j-log4j12
+                    
+                
+                
+                    
+                        verify
+                        verify
+                        
+                            integration-test
+                            verify
+                        
+                    
+                
+            
+        
+    
+
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
similarity index 97%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
index 62f0da76125..14223911444 100644
--- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
+++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLEndpointIT.java
@@ -17,7 +17,6 @@
 package io.helidon.microprofile.graphql.server;
 
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.logging.Level;
@@ -43,8 +42,7 @@
 /**
  * Abstract functionality for integration tests via /graphql endpoint.
  */
-public abstract class AbstractGraphQLEndpointIT
-        extends AbstractGraphQLTest {
+public abstract class AbstractGraphQLEndpointIT extends AbstractGraphQLTest {
 
     private static final Logger LOGGER = Logger.getLogger(AbstractGraphQLEndpointIT.class.getName());
 
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DuplicateQueriesAndMutationsIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InputTypeIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InterfaceOnlyAnnotatedIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InterfaceTypeOnlyAnnotatedIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedEnumIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInputTypeIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedInterfaceIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedMutationIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedQueryIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidNamedTypeIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidQueriesIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/Level0IT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PojoNamingIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SimpleMutationsIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/VoidMutationsIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/VoidQueriesIT.java
diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
similarity index 100%
rename from microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
rename to tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java
diff --git a/tests/integration/mp-graphql/src/test/resources/META-INF/beans.xml b/tests/integration/mp-graphql/src/test/resources/META-INF/beans.xml
new file mode 100644
index 00000000000..e5a9e54aa5e
--- /dev/null
+++ b/tests/integration/mp-graphql/src/test/resources/META-INF/beans.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/tests/integration/mp-graphql/src/test/resources/META-INF/microprofile-config.properties b/tests/integration/mp-graphql/src/test/resources/META-INF/microprofile-config.properties
new file mode 100644
index 00000000000..cd865c544ee
--- /dev/null
+++ b/tests/integration/mp-graphql/src/test/resources/META-INF/microprofile-config.properties
@@ -0,0 +1,18 @@
+#
+# Copyright (c) 2020 Oracle and/or its affiliates.
+#
+# 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.
+#
+
+mp.initializer.allow=true
+mp.initializer.no-warn=true
diff --git a/tests/integration/pom.xml b/tests/integration/pom.xml
index 4df3b32a2e9..3363ac744dd 100644
--- a/tests/integration/pom.xml
+++ b/tests/integration/pom.xml
@@ -35,6 +35,7 @@
     
         zipkin-mp-2.2
         mp-grpc
+        mp-graphql
         mp-security-client
         health
         mp-ws-services

From c995defd72e5e0f90262fc3e08d057a2ec989f92 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Tue, 20 Oct 2020 14:41:11 +0800
Subject: [PATCH 119/178] Refactor

---
 .../server/CustomSchemaPreProcessor.java      | 12 +++
 .../server/InvalidSchemaPreProcessor.java     |  9 +++
 .../server/InvalidSchemaPreProcessorIT.java   | 35 ++++++++
 .../SchemaPreProcessorTestContainer.java      | 80 -------------------
 .../server/ValidSchemaPreProcessorIT.java     | 34 ++++++++
 5 files changed, 90 insertions(+), 80 deletions(-)
 create mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java
 create mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessor.java
 create mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java
 delete mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java
 create mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java

diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java
new file mode 100644
index 00000000000..7230f217500
--- /dev/null
+++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java
@@ -0,0 +1,12 @@
+package io.helidon.microprofile.graphql.server;
+
+@SchemaProcessor
+public class CustomSchemaPreProcessor implements SchemaPreProcessor {
+
+    @Override
+    public void processSchema(Schema schema) {
+        // create a new input type from the Person type
+        SchemaInputType inputType = schema.getTypeByName("Person").createInputType("Input");
+        schema.addInputType(inputType);
+    }
+}
diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessor.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessor.java
new file mode 100644
index 00000000000..b0b511a7f22
--- /dev/null
+++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessor.java
@@ -0,0 +1,9 @@
+package io.helidon.microprofile.graphql.server;
+
+/**
+ * A {@link SchemaProcessor} which is invalid because it does not implement
+ * {@link SchemaPreProcessor}.
+ */
+@SchemaProcessor
+public class InvalidSchemaPreProcessor {
+}
diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java
new file mode 100644
index 00000000000..01e5fc07c4c
--- /dev/null
+++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java
@@ -0,0 +1,35 @@
+package io.helidon.microprofile.graphql.server;
+
+import io.helidon.microprofile.graphql.server.test.types.Person;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+import org.junit.jupiter.api.Test;
+import java.beans.IntrospectionException;
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test invalid {@link SchemaPreProcessor}.
+ */
+@HelidonTest
+@DisableDiscovery
+@AddExtension(GraphQLCdiExtension.class)
+@AddBean(Person.class)
+@AddBean(InvalidSchemaPreProcessor.class)
+public class InvalidSchemaPreProcessorIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testCustomPreProcessor() throws IOException, IntrospectionException, ClassNotFoundException {
+        setupIndex(indexFileName, Person.class);
+        SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext);
+        Schema schema = schemaGenerator.generateSchema();
+
+        // should be null as an invalid class was found
+        assertThat(schema.getInputTypeByName("PersonInput"), is(nullValue()));
+    }
+}
diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java
deleted file mode 100644
index 605a4fdad40..00000000000
--- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SchemaPreProcessorTestContainer.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package io.helidon.microprofile.graphql.server;
-
-import java.beans.IntrospectionException;
-import java.io.IOException;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import io.helidon.microprofile.graphql.server.test.types.Person;
-import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
-import org.junit.jupiter.api.Test;
-
-public class SchemaPreProcessorTestContainer {
-
-    /**
-     * Test valid {@link SchemaPreProcessor}.
-     */
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(Person.class)
-    @AddBean(SchemaPreProcessorTestContainer.CustomSchemaPreProcessor.class)
-    public static class ValidSchemaPreProcessorIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testCustomPreProcessor() throws IOException, IntrospectionException, ClassNotFoundException {
-            setupIndex(indexFileName, Person.class);
-            SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext);
-            Schema schema = schemaGenerator.generateSchema();
-
-            // SchemaPreProcessor should have been called and generated InputType for Person
-            assertThat(schema.getInputTypeByName("PersonInput"), is(notNullValue()));
-        }
-    }
-
-    /**
-     * Test invalid {@link SchemaPreProcessor}.
-     */
-    @HelidonTest
-    @DisableDiscovery
-    @AddExtension(GraphQLCdiExtension.class)
-    @AddBean(Person.class)
-    @AddBean(SchemaPreProcessorTestContainer.InvalidSchemaPreProcessor.class)
-    public static class InvalidSchemaPreProcessorIT extends AbstractGraphQLIT {
-
-        @Test
-        public void testCustomPreProcessor() throws IOException, IntrospectionException, ClassNotFoundException {
-            setupIndex(indexFileName, Person.class);
-            SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext);
-            Schema schema = schemaGenerator.generateSchema();
-
-            // should be null as an invalid class was found
-            assertThat(schema.getInputTypeByName("PersonInput"), is(nullValue()));
-        }
-    }
-
-    @SchemaProcessor
-    public static class CustomSchemaPreProcessor implements SchemaPreProcessor {
-
-        @Override
-        public void processSchema(Schema schema) {
-            // create a new input type from the Person type
-            SchemaInputType inputType = schema.getTypeByName("Person").createInputType("Input");
-            schema.addInputType(inputType);
-        }
-    }
-
-    /**
-     * A {@link SchemaProcessor} which is invalid because it does not implement
-     * {@link SchemaPreProcessor}.
-     */
-    @SchemaProcessor
-    public static class InvalidSchemaPreProcessor {
-    }
-}
diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java
new file mode 100644
index 00000000000..8183f56f195
--- /dev/null
+++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java
@@ -0,0 +1,34 @@
+package io.helidon.microprofile.graphql.server;
+
+import java.beans.IntrospectionException;
+import java.io.IOException;
+
+import io.helidon.microprofile.graphql.server.test.types.Person;
+import io.helidon.microprofile.tests.junit5.AddBean;
+import io.helidon.microprofile.tests.junit5.AddExtension;
+import io.helidon.microprofile.tests.junit5.DisableDiscovery;
+import io.helidon.microprofile.tests.junit5.HelidonTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test valid {@link SchemaPreProcessor}.
+ */
+@AddBean(Person.class)
+@AddBean(CustomSchemaPreProcessor.class)
+public class ValidSchemaPreProcessorIT extends AbstractGraphQLIT {
+
+    @Test
+    public void testCustomPreProcessor() throws IOException, IntrospectionException, ClassNotFoundException {
+        setupIndex(indexFileName, Person.class);
+        SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext);
+        Schema schema = schemaGenerator.generateSchema();
+
+        // SchemaPreProcessor should have been called and generated InputType for Person
+        assertThat(schema.getInputTypeByName("PersonInput"), is(notNullValue()));
+    }
+}

From 95bf667cbbd2f0fa864d028a4153d261523c4f4a Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Tue, 20 Oct 2020 14:49:03 +0800
Subject: [PATCH 120/178] minor

---
 .../graphql/server/InvalidSchemaPreProcessorIT.java  | 12 ++++--------
 .../graphql/server/ValidSchemaPreProcessorIT.java    |  3 ---
 2 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java
index 01e5fc07c4c..564efa2f559 100644
--- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java
+++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java
@@ -1,13 +1,12 @@
 package io.helidon.microprofile.graphql.server;
 
+import java.beans.IntrospectionException;
+import java.io.IOException;
+
 import io.helidon.microprofile.graphql.server.test.types.Person;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
+
 import org.junit.jupiter.api.Test;
-import java.beans.IntrospectionException;
-import java.io.IOException;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.nullValue;
@@ -16,9 +15,6 @@
 /**
  * Test invalid {@link SchemaPreProcessor}.
  */
-@HelidonTest
-@DisableDiscovery
-@AddExtension(GraphQLCdiExtension.class)
 @AddBean(Person.class)
 @AddBean(InvalidSchemaPreProcessor.class)
 public class InvalidSchemaPreProcessorIT extends AbstractGraphQLIT {
diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java
index 8183f56f195..3aa6d00df2d 100644
--- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java
+++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java
@@ -5,9 +5,6 @@
 
 import io.helidon.microprofile.graphql.server.test.types.Person;
 import io.helidon.microprofile.tests.junit5.AddBean;
-import io.helidon.microprofile.tests.junit5.AddExtension;
-import io.helidon.microprofile.tests.junit5.DisableDiscovery;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
 
 import org.junit.jupiter.api.Test;
 

From 1471438170abb1fbc894fb4ac13d4d8c1f6e24a6 Mon Sep 17 00:00:00 2001
From: Tim Middleton 
Date: Mon, 26 Oct 2020 15:04:40 +0800
Subject: [PATCH 121/178] Fixup how to handle Maps

---
 dependencies/pom.xml                          |  2 +-
 .../graphql/server/DataFetcherUtils.java      | 34 ++++++++-
 .../graphql/server/SchemaGenerator.java       | 10 ++-
 .../server/src/main/resources/web/index.html  |  2 +-
 .../server/test/queries/MapQueries.java       | 46 ++++++++++++
 .../server/test/types/TypeWithMap.java        | 72 +++++++++++++++++++
 .../microprofile/graphql/server/MapIT.java    | 65 +++++++++++++++++
 7 files changed, 224 insertions(+), 7 deletions(-)
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/MapQueries.java
 create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithMap.java
 create mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MapIT.java

diff --git a/dependencies/pom.xml b/dependencies/pom.xml
index 4e936849066..f8fa025658c 100644
--- a/dependencies/pom.xml
+++ b/dependencies/pom.xml
@@ -86,7 +86,7 @@
         2.10
         1.4
         2.2
-        1.0.2
+        1.0.3-SNAPSHOT
         1.0.3-SNAPSHOT
         1.1.1
         2.3.2
diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
index ab03e5002db..37f25d82052 100644
--- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
+++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java
@@ -40,6 +40,7 @@
 import java.util.UUID;
 import java.util.logging.Logger;
 
+import graphql.schema.PropertyDataFetcherHelper;
 import javax.enterprise.inject.spi.CDI;
 
 import graphql.GraphQLException;
@@ -109,6 +110,11 @@ public static  DataFetcher newMethodDataFetcher(Schema schema, Class cl
 
             if (args.length > 0) {
                 for (SchemaArgument argument : args) {
+                    // ensure a Map is not used as an input type
+                    Class originalType = argument.getOriginalType();
+                    if (originalType != null && originalType.isAssignableFrom(Map.class)) {
+                        ensureRuntimeException(LOGGER, "A Map may not be used as input to a query or mutation");
+                    }
                     listArgumentValues.add(generateArgumentValue(schema, argument.getArgumentType(),
                                                                  argument.getOriginalType(),
                                                                  argument.getOriginalArrayType(),
@@ -136,6 +142,32 @@ public static  DataFetcher newMethodDataFetcher(Schema schema, Class cl
         };
     }
 
+    /**
+     * Return a {@link DataFetcher} which converts a {@link Map} to a {@link Collection} of V.
+     * 

+ * This assumes that the key for the {@link Map} is contained within the V + * + * @param propertyName name of the property to apply to + * @param Source of the property + * @param type of the value + * @return a {@link DataFetcher} + */ + @SuppressWarnings("unchecked") + public static DataFetcher> newMapValuesDataFetcher(String propertyName) { + return environment -> { + S source = environment.getSource(); + if (source == null) { + return null; + } + + // retrieve the map and return the collection of V + Map map = (Map) PropertyDataFetcherHelper + .getPropertyValue(propertyName, source, environment.getFieldType(), environment); + return map.values(); + }; + } + + /** * Generate an argument value with the given information. This may be called recursively. * @@ -359,13 +391,11 @@ protected static Object parseArgumentValue(Class originalType, String argumen if (isDateTimeClass(originalType)) { DateTimeFormatter dateFormatter = getCorrectDateFormatter(originalType.getName(), format[1], format[0]); - LOGGER.info("Parsing [" + rawValue + "] with " + dateFormatter + " " + format[1] + ":" + format[0]); return getOriginalDateTimeValue(originalType, dateFormatter.parse(rawValue.toString())); } else { NumberFormat numberFormat = getCorrectNumberFormat(originalType.getName(), format[1], format[0]); if (numberFormat != null) { - LOGGER.info("Parsing [" + rawValue + "] with " + numberFormat + " " + format[1] + ":" + format[0]); return getOriginalValue(originalType, numberFormat.parse(rawValue.toString())); } else { return rawValue; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index cb904209740..651b0997936 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -797,8 +797,12 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth if (isArrayReturnType) { if (discoveredMethod.isMap) { - // add DataFetcher that will just retrieve the values() from the map - // dataFetcher = DataFetcherUtils.newMapValuesDataFetcher(fieldName); + // add DataFetcher that will just retrieve the values() from the Map. + // The microprofile-graphql spec does not specify how Maps are treated + // and leaves this up to the individual implementation. This implementation + // supports returning the values of the map only and does not support using + // Maps or types with Maps as return values from a query or mutation + dataFetcher = DataFetcherUtils.newMapValuesDataFetcher(propertyName); } } @@ -809,7 +813,7 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth if (!isGraphQLType(valueClassName)) { dataFetcher = retrieveFormattingDataFetcher(format, propertyName, graphQLType); - // if the format is a number format then set teh return type to String + // if the format is a number format then set the return type to String if (NUMBER.equals(format[0])) { graphQLType = STRING; } diff --git a/microprofile/graphql/server/src/main/resources/web/index.html b/microprofile/graphql/server/src/main/resources/web/index.html index e389f4c8892..a8dfcbc5538 100644 --- a/microprofile/graphql/server/src/main/resources/web/index.html +++ b/microprofile/graphql/server/src/main/resources/web/index.html @@ -20,7 +20,7 @@ - Helidon GraphiQL Interface + Helidon Microprofile GraphiQL Interface diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/MapQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/MapQueries.java new file mode 100644 index 00000000000..f23680e12f0 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/MapQueries.java @@ -0,0 +1,46 @@ +package io.helidon.microprofile.graphql.server.test.queries; + +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +import io.helidon.microprofile.graphql.server.test.enums.EnumTestWithEnumName; +import io.helidon.microprofile.graphql.server.test.types.SimpleContact; +import io.helidon.microprofile.graphql.server.test.types.TypeWithMap; +import javax.enterprise.context.ApplicationScoped; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.Query; + +/** + * Class that holds queries that have {@link Map} as return values or types with {@link Map}s. + */ +@GraphQLApi +@ApplicationScoped +public class MapQueries { + + @Query("query1") + public TypeWithMap generateTypeWithMap() { + Map mapValues = new TreeMap<>(); + mapValues.put(1, "one"); + mapValues.put(2, "two"); + + Map mapContacts = new TreeMap<>(); + + SimpleContact contact1 = new SimpleContact("c1", "Tim", 55, EnumTestWithEnumName.XL); + SimpleContact contact2 = new SimpleContact("c2", "James", 75, EnumTestWithEnumName.XXL); + mapContacts.put(contact1.getId(), contact1); + mapContacts.put(contact2.getId(), contact2); + return new TypeWithMap("id1", mapValues, mapContacts); + } + + @Query("query2") + public TypeWithMap echoTypeWithMap(@Name("value") TypeWithMap value) { + return value; + } + + @Query("query3") + public Map thisShouldRaiseError(@Name("value") Map value) { + return value; + } +} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithMap.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithMap.java new file mode 100644 index 00000000000..5cb644f5e1d --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/TypeWithMap.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import org.eclipse.microprofile.graphql.Type; +import java.util.Map; + +/** + * POJO to test a {@link Map} as a value. + */ +@Type +public class TypeWithMap { + + private String id; + private Map mapValues; + private Map mapContacts; + + public TypeWithMap(String id, + Map mapValues, + Map mapContacts) { + this.id = id; + this.mapValues = mapValues; + this.mapContacts = mapContacts; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Map getMapValues() { + return mapValues; + } + + public void setMapValues(Map mapValues) { + this.mapValues = mapValues; + } + + public Map getMapContacts() { + return mapContacts; + } + + public void setMapContacts(Map mapContacts) { + this.mapContacts = mapContacts; + } + + @Override + public String toString() { + return "TypeWithMap{" + + "id='" + id + '\'' + + ", mapValues=" + mapValues + + ", mapContacts=" + mapContacts + + '}'; + } +} diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MapIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MapIT.java new file mode 100644 index 00000000000..44761889a43 --- /dev/null +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MapIT.java @@ -0,0 +1,65 @@ +package io.helidon.microprofile.graphql.server; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import io.helidon.microprofile.graphql.server.test.queries.MapQueries; +import io.helidon.microprofile.graphql.server.test.types.SimpleContact; +import io.helidon.microprofile.graphql.server.test.types.TypeWithMap; +import io.helidon.microprofile.tests.junit5.AddBean; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests for handling a {@link Map}. + */ +@AddBean(TypeWithMap.class) +@AddBean(MapQueries.class) +@AddBean(SimpleContact.class) +public class MapIT extends AbstractGraphQLIT { + + @Test + @SuppressWarnings("unchecked") + public void testMap() throws IOException { + setupIndex(indexFileName, TypeWithMap.class, MapQueries.class, SimpleContact.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + + Map mapResults = getAndAssertResult( + executionContext.execute("query { query1 { id mapValues mapContacts { id name } } }")); + assertThat(mapResults.size(), is(1)); + + Map mapResults2 = (Map) mapResults.get("query1"); + assertThat(mapResults2, is(notNullValue())); + assertThat(mapResults2.size(), is(3)); + assertThat(mapResults2.get("id"), is("id1")); + List listString = (List) mapResults2.get("mapValues"); + assertThat(listString.size(), is(2)); + assertThat(listString.get(0), is("one")); + assertThat(listString.get(1), is("two")); + + List> listMapResults3 = (List>) mapResults2.get("mapContacts"); + assertThat(listMapResults3.size(), is(2)); + + Map mapContact1 = listMapResults3.get(0); + Map mapContact2 = listMapResults3.get(1); + assertThat(mapContact1, is(notNullValue())); + assertThat(mapContact1.get("id"), is("c1")); + assertThat(mapContact1.get("name"), is("Tim")); + assertThat(mapContact2, is(notNullValue())); + assertThat(mapContact2.get("id"), is("c2")); + assertThat(mapContact2.get("name"), is("James")); + } + + @Test + public void testMapAsInput() throws IOException { + setupIndex(indexFileName, TypeWithMap.class, MapQueries.class, SimpleContact.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Map mapResults = executionContext.execute("query { query3(value: [ \"a\" \"b\" ]) }"); + assertThat(mapResults.size(), is(2)); + assertThat(mapResults.get("errors"), is(notNullValue())); + } +} From 1f1383c3162c0649698f26b2c73d893670c0b4ef Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 27 Oct 2020 08:21:43 +0800 Subject: [PATCH 122/178] Fixup how to handle Maps - 2 --- .../graphql/server/DataFetcherUtils.java | 27 +++++++++++++------ .../microprofile/graphql/server/MapIT.java | 18 ++++++++++++- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 37f25d82052..07c8ba44d4a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -73,6 +73,12 @@ public class DataFetcherUtils { */ private static final String[] EMPTY_FORMAT = new String[] {null, null }; + /** + * Map message. + */ + private static final String MAP_MESSAGE = "This implementation does not support using a Map " + + "as input to a query or mutation"; + /** * Private constructor for utilities class. */ @@ -113,7 +119,7 @@ public static DataFetcher newMethodDataFetcher(Schema schema, Class cl // ensure a Map is not used as an input type Class originalType = argument.getOriginalType(); if (originalType != null && originalType.isAssignableFrom(Map.class)) { - ensureRuntimeException(LOGGER, "A Map may not be used as input to a query or mutation"); + ensureRuntimeException(LOGGER, MAP_MESSAGE); } listArgumentValues.add(generateArgumentValue(schema, argument.getArgumentType(), argument.getOriginalType(), @@ -211,13 +217,18 @@ protected static Object generateArgumentValue(Schema schema, String argumentType // don't deserialize using formatting as Jsonb will do this for us mapConverted.put(fdName, value); } else { - // retrieve the data fetcher and check if the property name is different as this should be used - DataFetcher dataFetcher = fd.getDataFetcher(); - if (dataFetcher instanceof PropertyDataFetcher) { - fdName = ((PropertyDataFetcher) dataFetcher).getPropertyName(); - } - mapConverted.put(fdName, generateArgumentValue(schema, fd.getReturnType(), fd.getOriginalType(), - fd.getOriginalArrayType(), value, fd.getFormat())); + // check it is not a Map + Class originalFdlType = fd.getOriginalType(); + if (originalFdlType != null && originalFdlType.isAssignableFrom(Map.class)) { + ensureRuntimeException(LOGGER, MAP_MESSAGE); + } + // retrieve the data fetcher and check if the property name is different as this should be used + DataFetcher dataFetcher = fd.getDataFetcher(); + if (dataFetcher instanceof PropertyDataFetcher) { + fdName = ((PropertyDataFetcher) dataFetcher).getPropertyName(); + } + mapConverted.put(fdName, generateArgumentValue(schema, fd.getReturnType(), fd.getOriginalType(), + fd.getOriginalArrayType(), value, fd.getFormat())); } } } diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MapIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MapIT.java index 44761889a43..d7baf0f4553 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MapIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MapIT.java @@ -58,7 +58,23 @@ public void testMap() throws IOException { public void testMapAsInput() throws IOException { setupIndex(indexFileName, TypeWithMap.class, MapQueries.class, SimpleContact.class); ExecutionContext executionContext = new ExecutionContext(defaultContext); - Map mapResults = executionContext.execute("query { query3(value: [ \"a\" \"b\" ]) }"); + String input = "{" + + "id: \"id-1\" " + + "mapValues: [ \"a\" \"b\" ] " + + "mapContacts: [{ id: \"c1\" age: 10 name: \"Tim\" tShirtSize: XL } " + + " { id: \"c3\" age: 10 name: \"Tim\" tShirtSize: XL } ] " + + "}"; + Map mapResults = executionContext.execute( + "query { query2(value: " + input + ") { id mapValues mapContacts { id } } }"); + assertThat(mapResults.size(), is(2)); + assertThat(mapResults.get("errors"), is(notNullValue())); + } + + @Test + public void testMapAsInput2() throws IOException { + setupIndex(indexFileName, TypeWithMap.class, MapQueries.class, SimpleContact.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Map mapResults = executionContext.execute("query { query3 (value: [ \"a\" \"b\" ]) }"); assertThat(mapResults.size(), is(2)); assertThat(mapResults.get("errors"), is(notNullValue())); } From 8a6cbcc0830289cfaad77a4e04ec4f55a8b56444 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 27 Oct 2020 10:20:24 +0800 Subject: [PATCH 123/178] findbugs, checkstyle and copyright --- microprofile/graphql/server/pom.xml | 3 +- .../graphql/server/CustomScalars.java | 2 +- .../graphql/server/DataFetcherUtils.java | 13 ++-- .../graphql/server/ExecutionContext.java | 73 ++++++++++--------- .../graphql/server/FormattingHelper.java | 2 +- .../graphql/server/SchemaArgument.java | 16 +++- .../graphql/server/SchemaFieldDefinition.java | 14 +++- .../graphql/server/SchemaGenerator.java | 60 ++++++++++++--- .../graphql/server/SchemaGeneratorHelper.java | 6 +- .../graphql/server/SchemaInputType.java | 2 +- .../graphql/server/SchemaScalar.java | 4 +- .../graphql/server/SchemaType.java | 1 - .../javax.enterprise.inject.spi.Extension | 16 ++++ .../server/test/queries/MapQueries.java | 19 ++++- .../server/AllDefaultsExceptionIT.java | 4 +- .../server/BLListAndWLExceptionIT.java | 10 +-- 16 files changed, 170 insertions(+), 75 deletions(-) diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index 96225e26430..e9709fde2a9 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -1,4 +1,5 @@ - - - - \ No newline at end of file + diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java index dc92a129c68..f52fbed7cc6 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java @@ -70,8 +70,7 @@ public static void setup() throws IOException { } @Test - public void basicEndpointTests() - throws UnsupportedEncodingException { + public void basicEndpointTests() { // test /graphql endpoint WebTarget webTarget = getGraphQLWebTarget().path(GRAPHQL); Map mapRequest = generateJsonRequest(QUERY_INTROSPECT, null, null); From d0b9ef6f802d50e3afd9a52fa01aa64865654241 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 27 Oct 2020 14:32:33 +0800 Subject: [PATCH 127/178] change getters/setters to Helidon style --- .../server/AbstractDescriptiveElement.java | 4 +- .../microprofile/graphql/server/Context.java | 2 +- .../graphql/server/CustomScalars.java | 4 +- .../graphql/server/DataFetcherUtils.java | 28 +- .../graphql/server/DefaultContext.java | 2 +- .../graphql/server/DescriptiveElement.java | 6 +- .../graphql/server/ExecutionContext.java | 5 +- .../graphql/server/FormattingHelper.java | 4 +- .../microprofile/graphql/server/Schema.java | 36 +- .../graphql/server/SchemaArgument.java | 50 +- .../graphql/server/SchemaDirective.java | 12 +- .../graphql/server/SchemaEnum.java | 10 +- .../graphql/server/SchemaFieldDefinition.java | 52 +- .../graphql/server/SchemaGenerator.java | 459 +++++++++--------- .../graphql/server/SchemaGeneratorHelper.java | 10 +- .../graphql/server/SchemaInputType.java | 2 +- .../graphql/server/SchemaScalar.java | 12 +- .../graphql/server/SchemaType.java | 30 +- .../graphql/server/AbstractGraphQLTest.java | 14 +- .../graphql/server/SchemaArgumentTest.java | 80 +-- .../graphql/server/SchemaDirectiveTest.java | 10 +- .../graphql/server/SchemaEnumTest.java | 12 +- .../server/SchemaFieldDefinitionTest.java | 60 +-- .../graphql/server/SchemaGeneratorTest.java | 22 +- .../graphql/server/SchemaScalarTest.java | 12 +- .../graphql/server/SchemaTest.java | 2 +- .../graphql/server/SchemaTypeTest.java | 40 +- .../graphql/server/AbstractGraphQLIT.java | 2 +- .../graphql/server/DataFetcherUtilsIT.java | 10 +- .../graphql/server/DateTimeIT.java | 36 +- .../graphql/server/DefaultValuesIT.java | 10 +- .../graphql/server/DescriptionIT.java | 32 +- .../graphql/server/IngorableIT.java | 6 +- .../graphql/server/NumberFormatIT.java | 24 +- 34 files changed, 549 insertions(+), 551 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java index 16d24a8af60..1f283b774a3 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/AbstractDescriptiveElement.java @@ -29,12 +29,12 @@ public class AbstractDescriptiveElement implements DescriptiveElement { private String description; @Override - public void setDescription(String description) { + public void description(String description) { this.description = description; } @Override - public String getDescription() { + public String description() { return this.description; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java index 25b93671326..3c7763ec3fc 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Context.java @@ -33,7 +33,7 @@ public interface Context { * * @return the {@link Throwable} */ - Throwable getPartialResultsException(); + Throwable partialResultsException(); /** * Remove partial results {@link Throwable}. diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java index 84f19afd5bf..19abe34b960 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java @@ -253,7 +253,6 @@ private static GraphQLScalarType newCustomGraphQLFloat() { * * @return a new custom BigInteger scalar */ - @SuppressWarnings("unchecked") private static GraphQLScalarType newCustomGraphQLBigInteger() { GraphQLScalarType originalScalar = GraphQLBigInteger; @@ -389,10 +388,9 @@ public BigDecimalCoercing() { } } - @SuppressWarnings("unchecked") - /** * Number implementation of {@link Coercing} interface for given classes. + * @param defines input type */ public static class NumberCoercing implements Coercing { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index cb91e0d60b8..7868720258f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -117,15 +117,15 @@ public static DataFetcher newMethodDataFetcher(Schema schema, Class cl if (args.length > 0) { for (SchemaArgument argument : args) { // ensure a Map is not used as an input type - Class originalType = argument.getOriginalType(); + Class originalType = argument.originalType(); if (originalType != null && originalType.isAssignableFrom(Map.class)) { ensureRuntimeException(LOGGER, MAP_MESSAGE); } - listArgumentValues.add(generateArgumentValue(schema, argument.getArgumentType(), - argument.getOriginalType(), - argument.getOriginalArrayType(), - environment.getArgument(argument.getArgumentName()), - argument.getFormat())); + listArgumentValues.add(generateArgumentValue(schema, argument.argumentType(), + argument.originalType(), + argument.originalArrayType(), + environment.getArgument(argument.argumentName()), + argument.format())); } } @@ -205,10 +205,10 @@ protected static Object generateArgumentValue(Schema schema, String argumentType SchemaFieldDefinition fd = inputType.getFieldDefinitionByName(fdName); // check to see if the Field Definition return type is an input type - SchemaInputType inputFdInputType = schema.getInputTypeByName(fd.getReturnType()); + SchemaInputType inputFdInputType = schema.getInputTypeByName(fd.returnType()); if (inputFdInputType != null && value instanceof Map) { - mapConverted.put(fdName, generateArgumentValue(schema, inputFdInputType.getName(), - Class.forName(inputFdInputType.getValueClassName()), + mapConverted.put(fdName, generateArgumentValue(schema, inputFdInputType.name(), + Class.forName(inputFdInputType.valueClassName()), null, value, EMPTY_FORMAT)); } else { @@ -217,17 +217,17 @@ protected static Object generateArgumentValue(Schema schema, String argumentType mapConverted.put(fdName, value); } else { // check it is not a Map - Class originalFdlType = fd.getOriginalType(); + Class originalFdlType = fd.originalType(); if (originalFdlType != null && originalFdlType.isAssignableFrom(Map.class)) { ensureRuntimeException(LOGGER, MAP_MESSAGE); } // retrieve the data fetcher and check if the property name is different as this should be used - DataFetcher dataFetcher = fd.getDataFetcher(); + DataFetcher dataFetcher = fd.dataFetcher(); if (dataFetcher instanceof PropertyDataFetcher) { fdName = ((PropertyDataFetcher) dataFetcher).getPropertyName(); } - mapConverted.put(fdName, generateArgumentValue(schema, fd.getReturnType(), fd.getOriginalType(), - fd.getOriginalArrayType(), value, fd.getFormat())); + mapConverted.put(fdName, generateArgumentValue(schema, fd.returnType(), fd.originalType(), + fd.originalArrayType(), value, fd.format())); } } } @@ -257,7 +257,7 @@ protected static Object generateArgumentValue(Schema schema, String argumentType if (inputType != null) { // handle complex types for (Object value : (Collection) rawValue) { - ((Collection) colResults).add(generateArgumentValue(schema, inputType.getName(), + ((Collection) colResults).add(generateArgumentValue(schema, inputType.name(), originalArrayType, null, value, EMPTY_FORMAT)); } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java index dfd638b68af..76ef820ae27 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java @@ -37,7 +37,7 @@ public void addPartialResultsException(Throwable throwable) { } @Override - public Throwable getPartialResultsException() { + public Throwable partialResultsException() { return currentThrowable.get(); } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java index 8cae23cac67..63e0298cd85 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DescriptiveElement.java @@ -32,14 +32,14 @@ public interface DescriptiveElement { * * @param description the description for this element */ - void setDescription(String description); + void description(String description); /** * Return the description for this element. * * @return the description for this element */ - String getDescription(); + String description(); /** * Return the description of the schema element. Only valid for Type, Field, Method, Parameter @@ -48,7 +48,7 @@ public interface DescriptiveElement { * @return the description of the schema element. */ default String getSchemaElementDescription(String[] format) { - String description = getDefaultDescription(format, getDescription()); + String description = getDefaultDescription(format, description()); if (description != null) { // if the description contains a quote or newline then use the triple quote option diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index 826f43c583a..b456949659d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -40,11 +40,8 @@ import org.eclipse.microprofile.graphql.ConfigKey; import org.eclipse.microprofile.graphql.GraphQLException; - - import static graphql.ExecutionInput.newExecutionInput; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureConfigurationException; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureRuntimeException; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getSafeClass; /** @@ -302,7 +299,7 @@ public Map execute(String query, String operationName, Map 0) { final Map mapTypes = new HashMap<>(); - getTypes().stream().filter(t -> !t.isInterface()).forEach(t -> mapTypes.put(t.getName(), t.getValueClassName())); + getTypes().stream().filter(t -> !t.isInterface()).forEach(t -> mapTypes.put(t.name(), t.valueClassName())); // generate a TypeResolver for all types that are not interfaces TypeResolver typeResolver = env -> { @@ -224,14 +224,14 @@ public RuntimeWiring getRuntimeWiring() { }; // add the type resolver to all interfaces and the Query object - setInterfaces.forEach(t -> builder.type(t.getName(), tr -> tr.typeResolver(typeResolver))); + setInterfaces.forEach(t -> builder.type(t.name(), tr -> tr.typeResolver(typeResolver))); builder.type(getQueryName(), tr -> tr.typeResolver(typeResolver)); } // register the scalars getScalars().forEach(s -> { LOGGER.finest("Register Scalar: " + s); - builder.scalar(s.getGraphQLScalarType()); + builder.scalar(s.graphQLScalarType()); }); @@ -240,12 +240,12 @@ public RuntimeWiring getRuntimeWiring() { // search for any types that have field definitions with DataFetchers getTypes().forEach(t -> { - boolean hasDataFetchers = t.getFieldDefinitions().stream().anyMatch(fd -> fd.getDataFetcher() != null); + boolean hasDataFetchers = t.fieldDefinitions().stream().anyMatch(fd -> fd.dataFetcher() != null); if (hasDataFetchers) { - final TypeRuntimeWiring.Builder runtimeBuilder = newTypeWiring(t.getName()); - t.getFieldDefinitions().stream() - .filter(fd -> fd.getDataFetcher() != null) - .forEach(fd -> runtimeBuilder.dataFetcher(fd.getName(), fd.getDataFetcher())); + final TypeRuntimeWiring.Builder runtimeBuilder = newTypeWiring(t.name()); + t.fieldDefinitions().stream() + .filter(fd -> fd.dataFetcher() != null) + .forEach(fd -> runtimeBuilder.dataFetcher(fd.name(), fd.dataFetcher())); builder.type(runtimeBuilder); } }); @@ -261,7 +261,7 @@ public RuntimeWiring getRuntimeWiring() { */ public SchemaType getTypeByName(String typeName) { for (SchemaType schemaType : listSchemaTypes) { - if (schemaType.getName().equals(typeName)) { + if (schemaType.name().equals(typeName)) { return schemaType; } } @@ -276,7 +276,7 @@ public SchemaType getTypeByName(String typeName) { */ public SchemaInputType getInputTypeByName(String inputTypeName) { for (SchemaInputType schemaInputType : listInputTypes) { - if (schemaInputType.getName().equals(inputTypeName)) { + if (schemaInputType.name().equals(inputTypeName)) { return schemaInputType; } } @@ -291,7 +291,7 @@ public SchemaInputType getInputTypeByName(String inputTypeName) { */ public SchemaType getTypeByClass(String clazz) { for (SchemaType schemaType : listSchemaTypes) { - if (clazz.equals(schemaType.getValueClassName())) { + if (clazz.equals(schemaType.valueClassName())) { return schemaType; } } @@ -306,7 +306,7 @@ public SchemaType getTypeByClass(String clazz) { */ public SchemaEnum getEnumByName(String enumName) { for (SchemaEnum schemaEnum1 : listSchemaEnums) { - if (schemaEnum1.getName().equals(enumName)) { + if (schemaEnum1.name().equals(enumName)) { return schemaEnum1; } } @@ -321,7 +321,7 @@ public SchemaEnum getEnumByName(String enumName) { */ public SchemaScalar getScalarByActualClass(String actualClazz) { for (SchemaScalar schemaScalar : getScalars()) { - if (schemaScalar.getActualClass().equals(actualClazz)) { + if (schemaScalar.actualClass().equals(actualClazz)) { return schemaScalar; } } @@ -336,7 +336,7 @@ public SchemaScalar getScalarByActualClass(String actualClazz) { */ public SchemaScalar getScalarByName(String scalarName) { for (SchemaScalar schemaScalar : getScalars()) { - if (schemaScalar.getName().equals(scalarName)) { + if (schemaScalar.name().equals(scalarName)) { return schemaScalar; } } @@ -350,7 +350,7 @@ public SchemaScalar getScalarByName(String scalarName) { * @return true if the type name is contained within the type list */ public boolean containsTypeWithName(String type) { - return listSchemaTypes.stream().filter(t -> t.getName().equals(type)).count() == 1; + return listSchemaTypes.stream().filter(t -> t.name().equals(type)).count() == 1; } /** @@ -360,7 +360,7 @@ public boolean containsTypeWithName(String type) { * @return true if the type name is contained within the input type list */ public boolean containsInputTypeWithName(String type) { - return listInputTypes.stream().filter(t -> t.getName().equals(type)).count() == 1; + return listInputTypes.stream().filter(t -> t.name().equals(type)).count() == 1; } /** @@ -370,7 +370,7 @@ public boolean containsInputTypeWithName(String type) { * @return true if the scalar name is contained within the scalar list */ public boolean containsScalarWithName(String scalar) { - return listSchemaScalars.stream().filter(s -> s.getName().equals(scalar)).count() == 1; + return listSchemaScalars.stream().filter(s -> s.name().equals(scalar)).count() == 1; } /** @@ -380,7 +380,7 @@ public boolean containsScalarWithName(String scalar) { * @return true if the enum name is contained within the enum list */ public boolean containsEnumWithName(String enumName) { - return listSchemaEnums.stream().filter(e -> e.getName().equals(enumName)).count() == 1; + return listSchemaEnums.stream().filter(e -> e.name().equals(enumName)).count() == 1; } /** diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index 4fc3560b4ea..7a273c85c9e 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -103,18 +103,18 @@ public SchemaArgument(String argumentName, String argumentType, */ @Override public String getSchemaAsString() { - StringBuilder sb = new StringBuilder(getSchemaElementDescription(getFormat())) - .append(getArgumentName()) + StringBuilder sb = new StringBuilder(getSchemaElementDescription(format())) + .append(argumentName()) .append(COLON); if (isArrayReturnType()) { - int count = getArrayLevels(); + int count = arrayLevels(); sb.append(SPACER).append(repeat(count, OPEN_SQUARE)) - .append(getArgumentType()) + .append(argumentType()) .append(isArrayReturnTypeMandatory() ? MANDATORY : NOTHING) .append(repeat(count, CLOSE_SQUARE)); } else { - sb.append(SPACER).append(getArgumentType()); + sb.append(SPACER).append(argumentType()); } if (isMandatory) { @@ -122,7 +122,7 @@ public String getSchemaAsString() { } if (defaultValue != null) { - sb.append(generateDefaultValue(defaultValue, getArgumentType())); + sb.append(generateDefaultValue(defaultValue, argumentType())); } return sb.toString(); @@ -133,7 +133,7 @@ public String getSchemaAsString() { * * @return the argument name */ - public String getArgumentName() { + public String argumentName() { return argumentName; } @@ -142,7 +142,7 @@ public String getArgumentName() { * * @return the argument type */ - public String getArgumentType() { + public String argumentType() { return argumentType; } @@ -151,7 +151,7 @@ public String getArgumentType() { * * @return indicates if the argument is mandatory */ - public boolean isMandatory() { + public boolean mandatory() { return isMandatory; } @@ -160,7 +160,7 @@ public boolean isMandatory() { * * @param argumentType the type of the argument */ - public void setArgumentType(String argumentType) { + public void argumentType(String argumentType) { this.argumentType = argumentType; } @@ -169,7 +169,7 @@ public void setArgumentType(String argumentType) { * * @return the default value for this argument */ - public Object getDefaultValue() { + public Object defaultValue() { return defaultValue; } @@ -178,7 +178,7 @@ public Object getDefaultValue() { * * @param defaultValue the default value for this argument */ - public void setDefaultValue(Object defaultValue) { + public void defaultValue(Object defaultValue) { this.defaultValue = defaultValue; } @@ -187,7 +187,7 @@ public void setDefaultValue(Object defaultValue) { * * @return the original argument type */ - public Class getOriginalType() { + public Class originalType() { return originalType; } @@ -205,7 +205,7 @@ public boolean isSourceArgument() { * * @param sourceArgument if the argument is a source argument. */ - public void setSourceArgument(boolean sourceArgument) { + public void sourceArgument(boolean sourceArgument) { this.sourceArgument = sourceArgument; } @@ -214,7 +214,7 @@ public void setSourceArgument(boolean sourceArgument) { * * @return the format for a number or date */ - public String[] getFormat() { + public String[] format() { if (format == null) { return null; } @@ -228,7 +228,7 @@ public String[] getFormat() { * * @param format the format for a number or date */ - public void setFormat(String[] format) { + public void format(String[] format) { if (format == null) { this.format = null; } else { @@ -242,7 +242,7 @@ public void setFormat(String[] format) { * * @param arrayLevels the number of array levels if return type is an array */ - public void setArrayLevels(int arrayLevels) { + public void arrayLevels(int arrayLevels) { this.arrayLevels = arrayLevels; } @@ -251,7 +251,7 @@ public void setArrayLevels(int arrayLevels) { * * @return the number of array levels if return type is an array */ - public int getArrayLevels() { + public int arrayLevels() { return arrayLevels; } @@ -259,7 +259,7 @@ public int getArrayLevels() { * Set if the return type is an array type. * @param isArrayReturnType if the return type is an array type */ - public void setArrayReturnType(boolean isArrayReturnType) { + public void arrayReturnType(boolean isArrayReturnType) { this.isArrayReturnType = isArrayReturnType; } @@ -284,7 +284,7 @@ public boolean isArrayReturnTypeMandatory() { * Sets if the array return type is mandatory. * @param arrayReturnTypeMandatory if the array return type is mandatory */ - public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { + public void arrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { isArrayReturnTypeMandatory = arrayReturnTypeMandatory; } @@ -293,7 +293,7 @@ public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { * * @param originalArrayType the original array type */ - public void setOriginalArrayType(Class originalArrayType) { + public void originalArrayType(Class originalArrayType) { this.originalArrayType = originalArrayType; } @@ -302,7 +302,7 @@ public void setOriginalArrayType(Class originalArrayType) { * * @return the original array type */ - public Class getOriginalArrayType() { + public Class originalArrayType() { return originalArrayType; } @@ -321,7 +321,7 @@ public String toString() { + ", originalArrayType=" + originalArrayType + ", arrayLevels=" + arrayLevels + ", format=" + Arrays.toString(format) - + ", description='" + getDescription() + '\'' + '}'; + + ", description='" + description() + '\'' + '}'; } @Override @@ -343,13 +343,13 @@ public boolean equals(Object o) { && Arrays.equals(format, schemaArgument.format) && Objects.equals(sourceArgument, schemaArgument.sourceArgument) && Objects.equals(originalArrayType, schemaArgument.originalArrayType) - && Objects.equals(getDescription(), schemaArgument.getDescription()) + && Objects.equals(description(), schemaArgument.description()) && Objects.equals(defaultValue, schemaArgument.defaultValue); } @Override public int hashCode() { return Objects.hash(super.hashCode(), argumentName, argumentType, sourceArgument, - isMandatory, defaultValue, getDescription(), originalType, format, originalArrayType); + isMandatory, defaultValue, description(), originalType, format, originalArrayType); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java index ab0f2bf0855..d001673859e 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java @@ -62,7 +62,7 @@ public SchemaDirective(String name) { */ @Override public String getSchemaAsString() { - StringBuilder sb = new StringBuilder("directive @" + getName()); + StringBuilder sb = new StringBuilder("directive @" + name()); if (listSchemaArguments.size() > 0) { sb.append("("); @@ -71,8 +71,8 @@ public String getSchemaAsString() { String delim = isFirst.get() ? "" : ", "; isFirst.set(false); - sb.append(delim).append(a.getArgumentName()).append(": ").append(a.getArgumentType()); - if (a.isMandatory()) { + sb.append(delim).append(a.argumentName()).append(": ").append(a.argumentType()); + if (a.mandatory()) { sb.append('!'); } }); @@ -109,7 +109,7 @@ public void addLocation(String location) { * * @return the name of the {@link SchemaDirective} */ - public String getName() { + public String name() { return name; } @@ -118,7 +118,7 @@ public String getName() { * * @return the {@link List} of {@link SchemaArgument}s */ - public List getArguments() { + public List arguments() { return listSchemaArguments; } @@ -127,7 +127,7 @@ public List getArguments() { * * @return the {@link Set} of locations */ - public Set getLocations() { + public Set locations() { return setLocations; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java index e49901f5fd0..6353d6a5be8 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java @@ -47,7 +47,7 @@ public SchemaEnum(String name) { * Return the name of the {@link SchemaEnum}. * @return the name of the {@link SchemaEnum} */ - public String getName() { + public String name() { return name; } @@ -55,7 +55,7 @@ public String getName() { * Set the name of the {@link SchemaEnum}. * @param name the name of the {@link SchemaEnum} */ - public void setName(String name) { + public void name(String name) { this.name = name; } @@ -63,7 +63,7 @@ public void setName(String name) { * Return the values for the {@link SchemaEnum}. * @return the values for the {@link SchemaEnum} */ - public List getValues() { + public List values() { return values; } @@ -80,7 +80,7 @@ public String getSchemaAsString() { StringBuilder sb = new StringBuilder(getSchemaElementDescription(null)) .append("enum") .append(SPACER) - .append(getName()) + .append(name()) .append(SPACER) .append(OPEN_CURLY) .append(NEWLINE); @@ -95,6 +95,6 @@ public String toString() { return "Enum{" + "name='" + name + '\'' + ", values=" + values - + ", description='" + getDescription() + '\'' + '}'; + + ", description='" + description() + '\'' + '}'; } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index 22f519b6b38..68e15f6a8bb 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -129,8 +129,8 @@ public SchemaFieldDefinition(String name, @Override public String getSchemaAsString() { - StringBuilder sb = new StringBuilder(getSchemaElementDescription(getFormat())) - .append(getName()); + StringBuilder sb = new StringBuilder(getSchemaElementDescription(format())) + .append(name()); if (listSchemaArguments.size() > 0) { sb.append(OPEN_PARENTHESES) @@ -144,13 +144,13 @@ public String getSchemaAsString() { sb.append(COLON); if (isArrayReturnType()) { - int count = getArrayLevels(); + int count = arrayLevels(); sb.append(SPACER).append(repeat(count, OPEN_SQUARE)) - .append(getReturnType()) + .append(returnType()) .append(isArrayReturnTypeMandatory() ? MANDATORY : NOTHING) .append(repeat(count, CLOSE_SQUARE)); } else { - sb.append(SPACER).append(getReturnType()); + sb.append(SPACER).append(returnType()); } if (isReturnTypeMandatory()) { @@ -158,7 +158,7 @@ public String getSchemaAsString() { } if (defaultValue != null) { - sb.append(generateDefaultValue(defaultValue, getReturnType())); + sb.append(generateDefaultValue(defaultValue, returnType())); } return sb.toString(); @@ -169,7 +169,7 @@ public String getSchemaAsString() { * * @return the name for the field definition */ - public String getName() { + public String name() { return name; } @@ -178,7 +178,7 @@ public String getName() { * * @return the {@link List} of {@link SchemaArgument}s */ - public List getArguments() { + public List arguments() { return listSchemaArguments; } @@ -187,7 +187,7 @@ public List getArguments() { * * @return the return type */ - public String getReturnType() { + public String returnType() { return returnType; } @@ -214,7 +214,7 @@ public boolean isReturnTypeMandatory() { * * @return he {@link DataFetcher} for this {@link SchemaFieldDefinition} */ - public DataFetcher getDataFetcher() { + public DataFetcher dataFetcher() { return dataFetcher; } @@ -223,7 +223,7 @@ public DataFetcher getDataFetcher() { * * @param sReturnType the return type */ - public void setReturnType(String sReturnType) { + public void returnType(String sReturnType) { returnType = sReturnType; } @@ -232,7 +232,7 @@ public void setReturnType(String sReturnType) { * * @param dataFetcher the {@link DataFetcher} */ - public void setDataFetcher(DataFetcher dataFetcher) { + public void dataFetcher(DataFetcher dataFetcher) { this.dataFetcher = dataFetcher; } @@ -250,7 +250,7 @@ public void addArgument(SchemaArgument schemaArgument) { * * @return the number of array levels if return type is an array */ - public int getArrayLevels() { + public int arrayLevels() { return arrayLevels; } @@ -259,7 +259,7 @@ public int getArrayLevels() { * * @return the format for a number or date */ - public String[] getFormat() { + public String[] format() { if (format == null) { return null; } @@ -273,7 +273,7 @@ public String[] getFormat() { * * @param format the format for a number or date */ - public void setFormat(String[] format) { + public void format(String[] format) { if (format == null) { this.format = null; } else { @@ -287,7 +287,7 @@ public void setFormat(String[] format) { * * @return the default value for this field definition */ - public Object getDefaultValue() { + public Object defaultValue() { return defaultValue; } @@ -296,7 +296,7 @@ public Object getDefaultValue() { * * @param defaultValue the default value for this field definition */ - public void setDefaultValue(Object defaultValue) { + public void defaultValue(Object defaultValue) { this.defaultValue = defaultValue; } @@ -305,7 +305,7 @@ public void setDefaultValue(Object defaultValue) { * * @param originalType the original return type */ - public void setOriginalType(Class originalType) { + public void originalType(Class originalType) { this.originalType = originalType; } @@ -314,7 +314,7 @@ public void setOriginalType(Class originalType) { * * @return the original return type */ - public Class getOriginalType() { + public Class originalType() { return originalType; } @@ -323,7 +323,7 @@ public Class getOriginalType() { * * @param originalArrayType the original array type */ - public void setOriginalArrayType(Class originalArrayType) { + public void originalArrayType(Class originalArrayType) { this.originalArrayType = originalArrayType; } @@ -332,7 +332,7 @@ public void setOriginalArrayType(Class originalArrayType) { * * @return the original array type */ - public Class getOriginalArrayType() { + public Class originalArrayType() { return originalArrayType; } @@ -348,7 +348,7 @@ public boolean isArrayReturnTypeMandatory() { * Sets if the array return type is mandatory. * @param arrayReturnTypeMandatory if the array return type is mandatory */ - public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { + public void arrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { isArrayReturnTypeMandatory = arrayReturnTypeMandatory; } @@ -356,7 +356,7 @@ public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { * Set if the field has a default format applied. * @param defaultFormatApplied if the field has a default format applied */ - public void setDefaultFormatApplied(boolean defaultFormatApplied) { + public void defaultFormatApplied(boolean defaultFormatApplied) { this.defaultFormatApplied = defaultFormatApplied; } @@ -372,7 +372,7 @@ public boolean isDefaultFormatApplied() { * Set if the format is of type Jsonb. * @param isJsonbFormat if the format is of type Jsonb */ - public void setJsonbFormat(boolean isJsonbFormat) { + public void jsonbFormat(boolean isJsonbFormat) { this.isJsonbFormat = isJsonbFormat; } @@ -389,7 +389,7 @@ public boolean isJsonbFormat() { * * @param isJsonbProperty if the property has a JsonbProperty annotation */ - public void setJsonbProperty(boolean isJsonbProperty) { + public void jsonbProperty(boolean isJsonbProperty) { this.isJsonbProperty = isJsonbProperty; } @@ -418,6 +418,6 @@ public String toString() { + ", format=" + Arrays.toString(format) + ", isJsonbFormat=" + isJsonbFormat + ", isJsonbProperty=" + isJsonbProperty - + ", description='" + getDescription() + '\'' + '}'; + + ", description='" + description() + '\'' + '}'; } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 9c584594d2f..b0807739ff2 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -121,12 +121,24 @@ */ public class SchemaGenerator { + /** + * "is" prefix. + */ protected static final String IS = "is"; + /** + * "get" prefix. + */ protected static final String GET = "get"; + /** + * "set" prefix. + */ protected static final String SET = "set"; - + + /** + * Logger. + */ private static final Logger LOGGER = Logger.getLogger(SchemaGenerator.class.getName()); /** @@ -247,8 +259,8 @@ protected Schema generateSchemaFromClasses(Class... clazzes) // assuming value for annotation overrides @Name String typeName = getTypeName(clazz, true); SchemaType type = new SchemaType(typeName.isBlank() ? clazz.getSimpleName() : typeName, clazz.getName()); - type.setIsInterface(clazz.isInterface()); - type.setDescription(getDescription(clazz.getAnnotation(Description.class))); + type.isInterface(clazz.isInterface()); + type.description(getDescription(clazz.getAnnotation(Description.class))); // add the discovered type addTypeToSchema(schema, type); @@ -263,11 +275,11 @@ protected Schema generateSchemaFromClasses(Class... clazzes) SchemaInputType inputType = generateType(clazzName, true).createInputType(""); // if the name of the InputType was not changed then append "Input" - if (inputType.getName().equals(simpleName)) { - inputType.setName(inputType.getName() + "Input"); + if (inputType.name().equals(simpleName)) { + inputType.name(inputType.name() + "Input"); } - if (!schema.containsInputTypeWithName(inputType.getName())) { + if (!schema.containsInputTypeWithName(inputType.name())) { schema.addInputType(inputType); checkInputType(schema, inputType); } @@ -289,13 +301,13 @@ protected Schema generateSchemaFromClasses(Class... clazzes) // look though all of interface type and see if any of the known types implement // the interface and if so, add the interface to the type schema.getTypes().stream().filter(SchemaType::isInterface).forEach(it -> { - schema.getTypes().stream().filter(t -> !t.isInterface() && t.getValueClassName() != null).forEach(type -> { - Class interfaceClass = getSafeClass(it.getValueClassName()); - Class typeClass = getSafeClass(type.getValueClassName()); + schema.getTypes().stream().filter(t -> !t.isInterface() && t.valueClassName() != null).forEach(type -> { + Class interfaceClass = getSafeClass(it.valueClassName()); + Class typeClass = getSafeClass(type.valueClassName()); if (interfaceClass != null && typeClass != null && interfaceClass.isAssignableFrom(typeClass)) { - type.setImplementingInterface(it.getName()); + type.implementingInterface(it.name()); } }); }); @@ -307,20 +319,20 @@ protected Schema generateSchemaFromClasses(Class... clazzes) if (type != null) { SchemaFieldDefinition fd = newFieldDefinition(dm, null); // add all arguments which are not source arguments - if (dm.getArguments().size() > 0) { - dm.getArguments().stream().filter(a -> !a.isSourceArgument()) + if (dm.arguments().size() > 0) { + dm.arguments().stream().filter(a -> !a.isSourceArgument()) .forEach(fd::addArgument); } // check for existing DataFetcher - fd.setDataFetcher(DataFetcherUtils.newMethodDataFetcher( + fd.dataFetcher(DataFetcherUtils.newMethodDataFetcher( schema, dm.method.getDeclaringClass(), dm.method, - dm.getSource(), fd.getArguments().toArray(new SchemaArgument[0]))); + dm.source(), fd.arguments().toArray(new SchemaArgument[0]))); type.addFieldDefinition(fd); // we are creating this as a type so ignore any Input annotation - String simpleName = getSimpleName(fd.getReturnType(), true); - String returnType = fd.getReturnType(); + String simpleName = getSimpleName(fd.returnType(), true); + String returnType = fd.returnType(); if (!simpleName.equals(returnType)) { updateLongTypes(schema, returnType, simpleName); } @@ -331,7 +343,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) processDefaultDateTimeValues(schema); // process the @GraphQLApi annotated classes - if (rootQueryType.getFieldDefinitions().size() == 0 && rootMutationType.getFieldDefinitions().size() == 0) { + if (rootQueryType.fieldDefinitions().size() == 0 && rootMutationType.fieldDefinitions().size() == 0) { LOGGER.warning("Unable to find any classes with @GraphQLApi annotation." + "Unable to build schema"); } @@ -350,65 +362,65 @@ private void processDefaultDateTimeValues(Schema schema) { Stream streamInputTypes = schema.getInputTypes().stream().map(it -> (SchemaType) it); Stream streamAll = Stream.concat(streamInputTypes, schema.getTypes().stream()); streamAll.forEach(t -> { - t.getFieldDefinitions().forEach(fd -> { - String returnType = fd.getReturnType(); + t.fieldDefinitions().forEach(fd -> { + String returnType = fd.returnType(); // only check Date/Time/DateTime scalars that are not Queries or don't have data fetchers // as default formatting has already been dealt with - if (isDateTimeScalar(returnType) && (t.getName().equals(Schema.QUERY) || fd.getDataFetcher() == null)) { - String[] existingFormat = fd.getFormat(); + if (isDateTimeScalar(returnType) && (t.name().equals(Schema.QUERY) || fd.dataFetcher() == null)) { + String[] existingFormat = fd.format(); // check if this type is an array type and if so then get the actual original type - Class clazzOriginalType = fd.getOriginalArrayType() != null - ? fd.getOriginalArrayType() : fd.getOriginalType(); + Class clazzOriginalType = fd.originalArrayType() != null + ? fd.originalArrayType() : fd.originalType(); String[] newFormat = ensureFormat(returnType, clazzOriginalType.getName(), existingFormat); if (!Arrays.equals(newFormat, existingFormat) && newFormat.length == 2) { // formats differ so set the new format and DataFetcher - fd.setFormat(newFormat); - if (fd.getDataFetcher() == null) { + fd.format(newFormat); + if (fd.dataFetcher() == null) { // create the raw array to pass to the retrieveFormattingDataFetcher method DataFetcher dataFetcher = retrieveFormattingDataFetcher( new String[] {DATE, newFormat[0], newFormat[1] }, - fd.getName(), clazzOriginalType.getName()); - fd.setDataFetcher(dataFetcher); + fd.name(), clazzOriginalType.getName()); + fd.dataFetcher(dataFetcher); } - fd.setDefaultFormatApplied(true); - SchemaScalar scalar = schema.getScalarByName(fd.getReturnType()); + fd.defaultFormatApplied(true); + SchemaScalar scalar = schema.getScalarByName(fd.returnType()); GraphQLScalarType newScalarType = null; - if (fd.getReturnType().equals(FORMATTED_DATE_SCALAR)) { - fd.setReturnType(DATE_SCALAR); + if (fd.returnType().equals(FORMATTED_DATE_SCALAR)) { + fd.returnType(DATE_SCALAR); newScalarType = CUSTOM_DATE_SCALAR; - } else if (fd.getReturnType().equals(FORMATTED_TIME_SCALAR)) { - fd.setReturnType(TIME_SCALAR); + } else if (fd.returnType().equals(FORMATTED_TIME_SCALAR)) { + fd.returnType(TIME_SCALAR); newScalarType = CUSTOM_TIME_SCALAR; - } else if (fd.getReturnType().equals(FORMATTED_DATETIME_SCALAR)) { - fd.setReturnType(DATETIME_SCALAR); + } else if (fd.returnType().equals(FORMATTED_DATETIME_SCALAR)) { + fd.returnType(DATETIME_SCALAR); newScalarType = CUSTOM_DATE_TIME_SCALAR; - } else if (fd.getReturnType().equals(FORMATTED_OFFSET_DATETIME_SCALAR)) { - fd.setReturnType(FORMATTED_OFFSET_DATETIME_SCALAR); + } else if (fd.returnType().equals(FORMATTED_OFFSET_DATETIME_SCALAR)) { + fd.returnType(FORMATTED_OFFSET_DATETIME_SCALAR); newScalarType = CUSTOM_OFFSET_DATE_TIME_SCALAR; - } else if (fd.getReturnType().equals(FORMATTED_ZONED_DATETIME_SCALAR)) { - fd.setReturnType(FORMATTED_ZONED_DATETIME_SCALAR); + } else if (fd.returnType().equals(FORMATTED_ZONED_DATETIME_SCALAR)) { + fd.returnType(FORMATTED_ZONED_DATETIME_SCALAR); newScalarType = CUSTOM_ZONED_DATE_TIME_SCALAR; } // clone the scalar with the new scalar name - SchemaScalar newScalar = new SchemaScalar(fd.getReturnType(), scalar.getActualClass(), - newScalarType, scalar.getDefaultFormat()); - if (!schema.containsScalarWithName(newScalar.getName())) { + SchemaScalar newScalar = new SchemaScalar(fd.returnType(), scalar.actualClass(), + newScalarType, scalar.defaultFormat()); + if (!schema.containsScalarWithName(newScalar.name())) { schema.addScalar(newScalar); } } } // check the SchemaArguments - fd.getArguments().forEach(a -> { - String argumentType = a.getArgumentType(); + fd.arguments().forEach(a -> { + String argumentType = a.argumentType(); if (isDateTimeScalar(argumentType)) { - String[] existingArgFormat = a.getFormat(); - Class clazzOriginalType = a.getOriginalArrayType() != null - ? a.getOriginalArrayType() : a.getOriginalType(); + String[] existingArgFormat = a.format(); + Class clazzOriginalType = a.originalArrayType() != null + ? a.originalArrayType() : a.originalType(); String[] newArgFormat = ensureFormat(argumentType, clazzOriginalType.getName(), existingArgFormat); if (!Arrays.equals(newArgFormat, existingArgFormat) && newArgFormat.length == 2) { - a.setFormat(newArgFormat); + a.format(newArgFormat); } } }); @@ -433,21 +445,21 @@ private void processUnresolvedTypes(Schema schema) { SchemaScalar scalar = getScalar(returnType); if (scalar != null) { - if (!schema.containsScalarWithName(scalar.getName())) { + if (!schema.containsScalarWithName(scalar.name())) { schema.addScalar(scalar); } // update the return type with the scalar - updateLongTypes(schema, returnType, scalar.getName()); + updateLongTypes(schema, returnType, scalar.name()); } else if (isEnumClass(returnType)) { SchemaEnum newEnum = generateEnum(Class.forName(returnType)); if (!schema.containsEnumWithName(simpleName)) { schema.addEnum(newEnum); } - updateLongTypes(schema, returnType, newEnum.getName()); + updateLongTypes(schema, returnType, newEnum.name()); } else { // we will either know this type already or need to add it boolean fExists = schema.getTypes().stream() - .filter(t -> t.getName().equals(simpleName)).count() > 0; + .filter(t -> t.name().equals(simpleName)).count() > 0; if (!fExists) { SchemaType newType = generateType(returnType, false); @@ -480,16 +492,16 @@ private SchemaType generateType(String realReturnType, boolean isInputType) // an annotated input type was also used as a return type String simpleName = getSimpleName(realReturnType, !isInputType); SchemaType type = new SchemaType(simpleName, realReturnType); - type.setDescription(getDescription(Class.forName(realReturnType).getAnnotation(Description.class))); + type.description(getDescription(Class.forName(realReturnType).getAnnotation(Description.class))); for (Map.Entry entry : retrieveGetterBeanMethods(Class.forName(realReturnType), isInputType) .entrySet()) { DiscoveredMethod discoveredMethod = entry.getValue(); - String valueTypeName = discoveredMethod.getReturnType(); + String valueTypeName = discoveredMethod.returnType(); SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod, null); type.addFieldDefinition(fd); - if (!ID.equals(valueTypeName) && valueTypeName.equals(fd.getReturnType())) { + if (!ID.equals(valueTypeName) && valueTypeName.equals(fd.returnType())) { // value class was unchanged meaning we need to resolve setUnresolvedTypes.add(valueTypeName); } @@ -514,20 +526,20 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, for (Map.Entry entry : retrieveAllAnnotatedBeanMethods(clazz).entrySet()) { DiscoveredMethod discoveredMethod = entry.getValue(); - Method method = discoveredMethod.getMethod(); + Method method = discoveredMethod.method(); SchemaFieldDefinition fd = null; // only include discovered methods in the original type where either the source is null // or the source is not null and it has a query annotation - String source = discoveredMethod.getSource(); + String source = discoveredMethod.source(); if (source == null || discoveredMethod.isQueryAnnotated()) { fd = newFieldDefinition(discoveredMethod, getMethodName(method)); } // if the source was not null, save it for later processing on the correct type if (source != null) { - String additionReturnType = getGraphQLType(discoveredMethod.getReturnType()); + String additionReturnType = getGraphQLType(discoveredMethod.returnType()); setAdditionalMethods.add(discoveredMethod); // add the unresolved type for the source if (!isGraphQLType(additionReturnType)) { @@ -541,12 +553,12 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, // add all the arguments and check to see if they contain types that are not yet known // this check is done no matter if the field definition is going to be created or not - for (SchemaArgument a : discoveredMethod.getArguments()) { - String originalTypeName = a.getArgumentType(); + for (SchemaArgument a : discoveredMethod.arguments()) { + String originalTypeName = a.argumentType(); String typeName = getGraphQLType(originalTypeName); - a.setArgumentType(typeName); - String returnType = a.getArgumentType(); + a.argumentType(typeName); + String returnType = a.argumentType(); if (originalTypeName.equals(returnType) && !ID.equals(returnType)) { // type name has not changed, so this must be either a Scalar, Enum or a Type @@ -558,15 +570,15 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, // create the input Type here SchemaInputType inputType = generateType(returnType, true).createInputType(""); // if the name of the InputType was not changed then append "Input" - if (inputType.getName().equals(Class.forName(returnType).getSimpleName())) { - inputType.setName(inputType.getName() + "Input"); + if (inputType.name().equals(Class.forName(returnType).getSimpleName())) { + inputType.name(inputType.name() + "Input"); } - if (!schema.containsInputTypeWithName(inputType.getName())) { + if (!schema.containsInputTypeWithName(inputType.name())) { schema.addInputType(inputType); checkInputType(schema, inputType); } - a.setArgumentType(inputType.getName()); + a.argumentType(inputType.name()); } // in either case, get the argument format @@ -579,14 +591,14 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, if (fd != null) { DataFetcher dataFetcher = null; - String[] format = discoveredMethod.getFormat(); - SchemaScalar dateScalar = getScalar(discoveredMethod.getReturnType()); + String[] format = discoveredMethod.format(); + SchemaScalar dateScalar = getScalar(discoveredMethod.returnType()); // if the type is a Date/Time/DateTime scalar and there is currently no format, // then use the default format if there is one - if ((dateScalar != null && isDateTimeScalar(dateScalar.getName()) && isFormatEmpty(format))) { - Class originalType = fd.isArrayReturnType() ? fd.getOriginalArrayType() : fd.getOriginalType(); - String[] newFormat = ensureFormat(dateScalar.getName(), + if ((dateScalar != null && isDateTimeScalar(dateScalar.name()) && isFormatEmpty(format))) { + Class originalType = fd.isArrayReturnType() ? fd.originalArrayType() : fd.originalType(); + String[] newFormat = ensureFormat(dateScalar.name(), originalType.getName(), new String[2]); if (newFormat.length == 2) { format = new String[] {DATE, newFormat[0], newFormat[1] }; @@ -594,13 +606,13 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, } if (!isFormatEmpty(format)) { // a format exists on the method return type so format it after returning the value - final String graphQLType = getGraphQLType(fd.getReturnType()); + final String graphQLType = getGraphQLType(fd.returnType()); final DataFetcher methodDataFetcher = DataFetcherUtils.newMethodDataFetcher(schema, clazz, method, null, - fd.getArguments().toArray( + fd.arguments().toArray( new SchemaArgument[0])); final String[] newFormat = new String[] {format[0], format[1], format[2] }; - if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) { + if (dateScalar != null && isDateTimeScalar(dateScalar.name())) { dataFetcher = DataFetcherFactories.wrapDataFetcher(methodDataFetcher, (e, v) -> { DateTimeFormatter dateTimeFormatter = @@ -615,27 +627,27 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, NumberFormat numberFormat = getCorrectNumberFormat( graphQLType, newFormat[2], newFormat[1]); boolean isScalar = SchemaGeneratorHelper.getScalar( - discoveredMethod.getReturnType()) != null; + discoveredMethod.returnType()) != null; return formatNumber(v, isScalar, numberFormat); }); - fd.setReturnType(STRING); + fd.returnType(STRING); } } else { // no formatting, just call the method dataFetcher = DataFetcherUtils.newMethodDataFetcher(schema, clazz, method, null, - fd.getArguments().toArray(new SchemaArgument[0])); + fd.arguments().toArray(new SchemaArgument[0])); } - fd.setDataFetcher(dataFetcher); - fd.setDescription(discoveredMethod.getDescription()); + fd.dataFetcher(dataFetcher); + fd.description(discoveredMethod.description()); schemaType.addFieldDefinition(fd); // check for scalar return type checkScalars(schema, schemaType); - String returnType = discoveredMethod.getReturnType(); + String returnType = discoveredMethod.returnType(); // check to see if this is a known type - if (returnType.equals(fd.getReturnType()) && !setUnresolvedTypes.contains(returnType) + if (returnType.equals(fd.returnType()) && !setUnresolvedTypes.contains(returnType) && !ID.equals(returnType)) { // value class was unchanged meaning we need to resolve setUnresolvedTypes.add(returnType); @@ -684,8 +696,8 @@ private void checkInputType(Schema schema, SchemaInputType schemaInputType) setInputTypes.remove(type); // check each field definition to see if any return types are unknownInputTypes - for (SchemaFieldDefinition fdi : type.getFieldDefinitions()) { - String fdReturnType = fdi.getReturnType(); + for (SchemaFieldDefinition fdi : type.fieldDefinitions()) { + String fdReturnType = fdi.returnType(); if (!isGraphQLType(fdReturnType)) { // must be either an unknown input type, Scalar or Enum @@ -697,15 +709,15 @@ private void checkInputType(Schema schema, SchemaInputType schemaInputType) SchemaInputType newInputType = generateType(fdReturnType, true).createInputType(""); // if the name of the InputType was not changed then append "Input" - if (newInputType.getName().equals(Class.forName(newInputType.getValueClassName()).getSimpleName())) { - newInputType.setName(newInputType.getName() + "Input"); + if (newInputType.name().equals(Class.forName(newInputType.valueClassName()).getSimpleName())) { + newInputType.name(newInputType.name() + "Input"); } - if (!schema.containsInputTypeWithName(newInputType.getName())) { + if (!schema.containsInputTypeWithName(newInputType.name())) { schema.addInputType(newInputType); setInputTypes.add(newInputType); } - fdi.setReturnType(newInputType.getName()); + fdi.returnType(newInputType.name()); } } } @@ -722,16 +734,16 @@ private void checkInputType(Schema schema, SchemaInputType schemaInputType) private void addTypeToSchema(Schema schema, SchemaType type) throws IntrospectionException, ClassNotFoundException { - String valueClassName = type.getValueClassName(); + String valueClassName = type.valueClassName(); retrieveGetterBeanMethods(Class.forName(valueClassName), false).forEach((k, v) -> { SchemaFieldDefinition fd = newFieldDefinition(v, null); type.addFieldDefinition(fd); checkScalars(schema, type); - String returnType = v.getReturnType(); + String returnType = v.returnType(); // check to see if this is a known type - if (!ID.equals(returnType) && returnType.equals(fd.getReturnType()) && !setUnresolvedTypes.contains(returnType)) { + if (!ID.equals(returnType) && returnType.equals(fd.returnType()) && !setUnresolvedTypes.contains(returnType)) { // value class was unchanged meaning we need to resolve setUnresolvedTypes.add(returnType); } @@ -774,11 +786,11 @@ private SchemaEnum generateEnum(Class clazz) { */ @SuppressWarnings("rawtypes") private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMethod, String optionalName) { - String valueClassName = discoveredMethod.getReturnType(); + String valueClassName = discoveredMethod.returnType(); String graphQLType = getGraphQLType(valueClassName); DataFetcher dataFetcher = null; - String propertyName = discoveredMethod.getPropertyName(); - String name = discoveredMethod.getName(); + String propertyName = discoveredMethod.propertyName(); + String name = discoveredMethod.name(); boolean isArrayReturnType = discoveredMethod.isArrayReturnType || discoveredMethod.isCollectionType() || discoveredMethod .isMap(); @@ -796,7 +808,7 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth // check for format on the property // note: currently the format will be an array of [3] as defined by FormattingHelper.getFormattingAnnotation - String[] format = discoveredMethod.getFormat(); + String[] format = discoveredMethod.format(); if (propertyName != null && !isFormatEmpty(format)) { if (!isGraphQLType(valueClassName)) { dataFetcher = retrieveFormattingDataFetcher(format, propertyName, graphQLType); @@ -819,20 +831,20 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth graphQLType, isArrayReturnType, discoveredMethod.isReturnTypeMandatory(), - discoveredMethod.getArrayLevels()); - fd.setDataFetcher(dataFetcher); - fd.setOriginalType(discoveredMethod.getMethod().getReturnType()); - fd.setArrayReturnTypeMandatory(discoveredMethod.isArrayReturnTypeMandatory()); - fd.setOriginalArrayType(isArrayReturnType ? discoveredMethod.getOriginalArrayType() : null); + discoveredMethod.arrayLevels()); + fd.dataFetcher(dataFetcher); + fd.originalType(discoveredMethod.method().getReturnType()); + fd.arrayReturnTypeMandatory(discoveredMethod.isArrayReturnTypeMandatory()); + fd.originalArrayType(isArrayReturnType ? discoveredMethod.originalArrayType() : null); if (format != null && format.length == 3) { - fd.setFormat(new String[] {format[1], format[2] }); + fd.format(new String[] {format[1], format[2] }); } - fd.setDescription(discoveredMethod.getDescription()); - fd.setJsonbFormat(discoveredMethod.isJsonbFormat()); - fd.setDefaultValue(discoveredMethod.getDefaultValue()); - fd.setJsonbProperty(discoveredMethod.isJsonbProperty()); + fd.description(discoveredMethod.description()); + fd.jsonbFormat(discoveredMethod.isJsonbFormat()); + fd.defaultValue(discoveredMethod.defaultValue()); + fd.jsonbProperty(discoveredMethod.isJsonbProperty()); return fd; } @@ -864,15 +876,15 @@ private void updateLongTypes(Schema schema, String longReturnType, String shortR Stream streamInputTypes = schema.getInputTypes().stream().map(it -> (SchemaType) it); Stream streamAll = Stream.concat(streamInputTypes, schema.getTypes().stream()); streamAll.forEach(t -> { - t.getFieldDefinitions().forEach(fd -> { - if (fd.getReturnType().equals(longReturnType)) { - fd.setReturnType(shortReturnType); + t.fieldDefinitions().forEach(fd -> { + if (fd.returnType().equals(longReturnType)) { + fd.returnType(shortReturnType); } // check arguments - fd.getArguments().forEach(a -> { - if (a.getArgumentType().equals(longReturnType)) { - a.setArgumentType(shortReturnType); + fd.arguments().forEach(a -> { + if (a.argumentType().equals(longReturnType)) { + a.argumentType(shortReturnType); } }); }); @@ -880,9 +892,9 @@ private void updateLongTypes(Schema schema, String longReturnType, String shortR // look through set of additional methods added for Source annotations setAdditionalMethods.forEach(m -> { - m.getArguments().forEach(a -> { - if (a.getArgumentType().equals(longReturnType)) { - a.setArgumentType(shortReturnType); + m.arguments().forEach(a -> { + if (a.argumentType().equals(longReturnType)) { + a.argumentType(shortReturnType); } }); }); @@ -910,12 +922,12 @@ protected Map retrieveAllAnnotatedBeanMethods(Class } if (isQuery || isMutation || hasSourceAnnotation) { DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null, false, true); - discoveredMethod.setMethodType(isQuery || hasSourceAnnotation ? QUERY_TYPE : MUTATION_TYPE); - String name = discoveredMethod.getName(); + discoveredMethod.methodType(isQuery || hasSourceAnnotation ? QUERY_TYPE : MUTATION_TYPE); + String name = discoveredMethod.name(); if (mapDiscoveredMethods.containsKey(name)) { ensureConfigurationException(LOGGER, "A method named " + name + " already exists on " + "the " + (isMutation ? "mutation" : "query") - + " " + discoveredMethod.getMethod().getName()); + + " " + discoveredMethod.method().getName()); } mapDiscoveredMethods.put(name, discoveredMethod); } @@ -955,7 +967,7 @@ protected Map retrieveGetterBeanMethods(Class clazz // this is a getter method, include it here DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, propertyDescriptor, isInputType, false); - mapDiscoveredMethods.put(discoveredMethod.getName(), discoveredMethod); + mapDiscoveredMethods.put(discoveredMethod.name(), discoveredMethod); } } } @@ -1132,13 +1144,13 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, } DiscoveredMethod discoveredMethod = new DiscoveredMethod(); - discoveredMethod.setName(varName); - discoveredMethod.setMethod(method); - discoveredMethod.setFormat(format); - discoveredMethod.setDefaultValue(defaultValue); - discoveredMethod.setJsonbFormat(isJsonbFormat); - discoveredMethod.setJsonbProperty(isJsonbProperty); - discoveredMethod.setPropertyName(pd != null ? pd.getName() : null); + discoveredMethod.name(varName); + discoveredMethod.method(method); + discoveredMethod.format(format); + discoveredMethod.defaultValue(defaultValue); + discoveredMethod.jsonbFormat(isJsonbFormat); + discoveredMethod.jsonbProperty(isJsonbProperty); + discoveredMethod.propertyName(pd != null ? pd.getName() : null); if (description == null && !isInputType) { description = getDescription(method.getAnnotation(Description.class)); @@ -1148,10 +1160,10 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, ReturnType realReturnType = getReturnType(returnClazz, method.getGenericReturnType(), -1, method); processReturnType(discoveredMethod, realReturnType, returnClazzName, isInputType, varName, method); - discoveredMethod.setReturnTypeMandatory(isReturnTypeMandatory); - discoveredMethod.setArrayReturnTypeMandatory(isArrayReturnTypeMandatory + discoveredMethod.returnTypeMandatory(isReturnTypeMandatory); + discoveredMethod.arrayReturnTypeMandatory(isArrayReturnTypeMandatory || realReturnType.isReturnTypeMandatory && !isInputType); - discoveredMethod.setDescription(description); + discoveredMethod.description(description); return discoveredMethod; } @@ -1183,32 +1195,32 @@ private void ensureNonVoidQueryOrMutation(String returnClazzName, Method method, private void processReturnType(DiscoveredMethod discoveredMethod, ReturnType realReturnType, String returnClazzName, boolean isInputType, String varName, Method method) throws ClassNotFoundException { - if (realReturnType.getReturnClass() != null && !ID.equals(returnClazzName)) { - discoveredMethod.setArrayReturnType(realReturnType.isArrayType()); - discoveredMethod.setCollectionType(realReturnType.getCollectionType()); - discoveredMethod.setMap(realReturnType.isMap()); - SchemaScalar dateScalar = getScalar(realReturnType.getReturnClass()); - if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) { + if (realReturnType.returnClass() != null && !ID.equals(returnClazzName)) { + discoveredMethod.arrayReturnType(realReturnType.isArrayType()); + discoveredMethod.collectionType(realReturnType.collectionType()); + discoveredMethod.map(realReturnType.isMap()); + SchemaScalar dateScalar = getScalar(realReturnType.returnClass()); + if (dateScalar != null && isDateTimeScalar(dateScalar.name())) { // only set the original array type if it's a date/time - discoveredMethod.setOriginalArrayType(Class.forName(realReturnType.returnClass)); + discoveredMethod.originalArrayType(Class.forName(realReturnType.returnClass)); } else if (discoveredMethod.isArrayReturnType) { Class originalArrayType = getSafeClass(realReturnType.returnClass); if (originalArrayType != null) { - discoveredMethod.setOriginalArrayType(originalArrayType); + discoveredMethod.originalArrayType(originalArrayType); } } - discoveredMethod.setReturnType(realReturnType.getReturnClass()); + discoveredMethod.returnType(realReturnType.returnClass()); // only override if this is not an input type - if (!isInputType && !isFormatEmpty(realReturnType.getFormat())) { - discoveredMethod.setFormat(realReturnType.format); + if (!isInputType && !isFormatEmpty(realReturnType.format())) { + discoveredMethod.format(realReturnType.format); } } else { - discoveredMethod.setName(varName); - discoveredMethod.setReturnType(returnClazzName); - discoveredMethod.setMethod(method); + discoveredMethod.name(varName); + discoveredMethod.returnType(returnClazzName); + discoveredMethod.method(method); } - discoveredMethod.setArrayLevels(realReturnType.getArrayLevels()); + discoveredMethod.arrayLevels(realReturnType.arrayLevels()); } /** @@ -1236,8 +1248,8 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM ReturnType returnType = getReturnType(paramType, genericParameterTypes[i], i, method); if (parameter.getAnnotation(Id.class) != null) { - validateIDClass(returnType.getReturnClass()); - returnType.setReturnClass(ID); + validateIDClass(returnType.returnClass()); + returnType.returnClass(ID); isID = true; } @@ -1247,10 +1259,10 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM (isPrimitive(paramType) && argumentDefaultValue == null) || (parameter.getAnnotation(NonNull.class) != null && argumentDefaultValue == null); SchemaArgument argument = - new SchemaArgument(parameterName, returnType.getReturnClass(), + new SchemaArgument(parameterName, returnType.returnClass(), isMandatory, argumentDefaultValue, paramType); String argumentDescription = getDescription(parameter.getAnnotation(Description.class)); - argument.setDescription(argumentDescription); + argument.description(argumentDescription); String[] argumentFormat = getFormattingAnnotation(parameter); String[] argumentTypeFormat = FormattingHelper.getMethodParameterFormat(parameter, 0); @@ -1262,31 +1274,31 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM argumentFormat = !isFormatEmpty(argumentTypeFormat) ? argumentTypeFormat : argumentFormat; if (argumentFormat[0] != null) { - argument.setFormat(new String[] {argumentFormat[1], argumentFormat[2] }); - argument.setArgumentType(String.class.getName()); + argument.format(new String[] {argumentFormat[1], argumentFormat[2] }); + argument.argumentType(String.class.getName()); } Source sourceAnnotation = parameter.getAnnotation(Source.class); if (sourceAnnotation != null) { // set the method name to the correct property name as it will currently be incorrect - discoveredMethod.setName(annotatedName != null ? annotatedName : stripMethodName(method, false)); - discoveredMethod.setSource(returnType.getReturnClass()); - discoveredMethod.setQueryAnnotated(method.getAnnotation(Query.class) != null); - argument.setSourceArgument(true); + discoveredMethod.name(annotatedName != null ? annotatedName : stripMethodName(method, false)); + discoveredMethod.source(returnType.returnClass()); + discoveredMethod.queryAnnotated(method.getAnnotation(Query.class) != null); + argument.sourceArgument(true); } if (!isID) { - SchemaScalar dateScalar = getScalar(returnType.getReturnClass()); - if (dateScalar != null && isDateTimeScalar(dateScalar.getName())) { + SchemaScalar dateScalar = getScalar(returnType.returnClass()); + if (dateScalar != null && isDateTimeScalar(dateScalar.name())) { // only set the original array type if it's a date/time - discoveredMethod.setOriginalArrayType(getSafeClass(returnType.returnClass)); + discoveredMethod.originalArrayType(getSafeClass(returnType.returnClass)); } - argument.setArrayReturnTypeMandatory(returnType.isReturnTypeMandatory); - argument.setArrayReturnType(returnType.isArrayType); + argument.arrayReturnTypeMandatory(returnType.isReturnTypeMandatory); + argument.arrayReturnType(returnType.isArrayType); if (returnType.isArrayType) { - argument.setOriginalArrayType(getSafeClass(returnType.returnClass)); + argument.originalArrayType(getSafeClass(returnType.returnClass)); } - argument.setArrayLevels(returnType.getArrayLevels()); + argument.arrayLevels(returnType.arrayLevels()); } discoveredMethod.addArgument(argument); @@ -1338,36 +1350,36 @@ protected ReturnType getReturnType(Class returnClazz, java.lang.reflect.Type // deal with Collection or Map if (isCollection || isMap) { if (isCollection) { - actualReturnType.setCollectionType(returnClazzName); + actualReturnType.collectionType(returnClazzName); } - actualReturnType.setMap(isMap); + actualReturnType.map(isMap); // index is 0 for Collection and 1 for Map which assumes we are not // interested in the map K, just the map V which is what our implementation will do rootTypeResult = getRootTypeName(genericReturnType, isCollection ? 0 : 1, parameterNumber, method); - String rootType = rootTypeResult.getRootTypeName(); + String rootType = rootTypeResult.rootTypeName(); // set the initial number of array levels to the levels of the root type - int arrayLevels = rootTypeResult.getLevels(); + int arrayLevels = rootTypeResult.levels(); if (isArrayType(rootType)) { - actualReturnType.setReturnClass(getRootArrayClass(rootType)); + actualReturnType.returnClass(getRootArrayClass(rootType)); arrayLevels += getArrayLevels(rootType); } else { - actualReturnType.setReturnClass(rootType); + actualReturnType.returnClass(rootType); } - actualReturnType.setArrayLevels(arrayLevels); - actualReturnType.setReturnTypeMandatory(rootTypeResult.isArrayReturnTypeMandatory()); - actualReturnType.setFormat(rootTypeResult.format); - actualReturnType.setArrayType(true); + actualReturnType.arrayLevels(arrayLevels); + actualReturnType.returnTypeMandatory(rootTypeResult.isArrayReturnTypeMandatory()); + actualReturnType.format(rootTypeResult.format); + actualReturnType.arrayType(true); } else if (!returnClazzName.isEmpty() && returnClazzName.startsWith("[")) { // return type is array of either primitives or Objects/Interface/Enum. - actualReturnType.setArrayType(true); - actualReturnType.setArrayLevels(getArrayLevels(returnClazzName)); - actualReturnType.setReturnClass(getRootArrayClass(returnClazzName)); + actualReturnType.arrayType(true); + actualReturnType.arrayLevels(getArrayLevels(returnClazzName)); + actualReturnType.returnClass(getRootArrayClass(returnClazzName)); } else { // primitive or type - actualReturnType.setReturnClass(returnClazzName); + actualReturnType.returnClass(returnClazzName); } return actualReturnType; } @@ -1556,7 +1568,7 @@ public DiscoveredMethod() { * * @return the name */ - public String getName() { + public String name() { return name; } @@ -1565,7 +1577,7 @@ public String getName() { * * @param name the name */ - public void setName(String name) { + public void name(String name) { this.name = name; } @@ -1574,7 +1586,7 @@ public void setName(String name) { * * @return the return type */ - public String getReturnType() { + public String returnType() { return returnType; } @@ -1583,7 +1595,7 @@ public String getReturnType() { * * @param returnType the return type */ - public void setReturnType(String returnType) { + public void returnType(String returnType) { this.returnType = returnType; } @@ -1592,7 +1604,7 @@ public void setReturnType(String returnType) { * * @return the collection */ - public String getCollectionType() { + public String collectionType() { return collectionType; } @@ -1601,7 +1613,7 @@ public String getCollectionType() { * * @param collectionType the collection type */ - public void setCollectionType(String collectionType) { + public void collectionType(String collectionType) { this.collectionType = collectionType; } @@ -1610,7 +1622,7 @@ public void setCollectionType(String collectionType) { * * @param isJsonbProperty if the property has a JsonbProperty annotation */ - public void setJsonbProperty(boolean isJsonbProperty) { + public void jsonbProperty(boolean isJsonbProperty) { this.isJsonbProperty = isJsonbProperty; } @@ -1637,7 +1649,7 @@ public boolean isMap() { * * @param map if the method is a map return type */ - public void setMap(boolean map) { + public void map(boolean map) { isMap = map; } @@ -1646,7 +1658,7 @@ public void setMap(boolean map) { * * @return the method type */ - public int getMethodType() { + public int methodType() { return methodType; } @@ -1655,7 +1667,7 @@ public int getMethodType() { * * @param methodType the method type */ - public void setMethodType(int methodType) { + public void methodType(int methodType) { this.methodType = methodType; } @@ -1673,7 +1685,7 @@ public boolean isArrayReturnType() { * * @param arrayReturnType if the method returns an array */ - public void setArrayReturnType(boolean arrayReturnType) { + public void arrayReturnType(boolean arrayReturnType) { isArrayReturnType = arrayReturnType; } @@ -1691,7 +1703,7 @@ public boolean isCollectionType() { * * @return the {@link Method} */ - public Method getMethod() { + public Method method() { return method; } @@ -1700,7 +1712,7 @@ public Method getMethod() { * * @param method the {@link Method} */ - public void setMethod(Method method) { + public void method(Method method) { this.method = method; } @@ -1709,7 +1721,7 @@ public void setMethod(Method method) { * * @return the {@link List} of {@link SchemaArgument} */ - public List getArguments() { + public List arguments() { return this.listArguments; } @@ -1718,7 +1730,7 @@ public List getArguments() { * * @return Return the number of levels in the Array */ - public int getArrayLevels() { + public int arrayLevels() { return arrayLevels; } @@ -1727,7 +1739,7 @@ public int getArrayLevels() { * * @param arrayLevels the number of levels in the Array */ - public void setArrayLevels(int arrayLevels) { + public void arrayLevels(int arrayLevels) { this.arrayLevels = arrayLevels; } @@ -1745,7 +1757,7 @@ public void addArgument(SchemaArgument argument) { * * @return source on which the method should be added */ - public String getSource() { + public String source() { return source; } @@ -1754,7 +1766,7 @@ public String getSource() { * * @param source source on which the method should be added */ - public void setSource(String source) { + public void source(String source) { this.source = source; } @@ -1772,7 +1784,7 @@ public boolean isQueryAnnotated() { * * @param queryAnnotated true if the {@link Query} annotation was present */ - public void setQueryAnnotated(boolean queryAnnotated) { + public void queryAnnotated(boolean queryAnnotated) { isQueryAnnotated = queryAnnotated; } @@ -1781,7 +1793,7 @@ public void setQueryAnnotated(boolean queryAnnotated) { * * @return the format for a number or date */ - public String[] getFormat() { + public String[] format() { if (format == null) { return null; } @@ -1795,7 +1807,7 @@ public String[] getFormat() { * * @param format the format for a number or date */ - public void setFormat(String[] format) { + public void format(String[] format) { if (format == null) { this.format = null; } else { @@ -1809,7 +1821,7 @@ public void setFormat(String[] format) { * * @return property name if the method is a getter */ - public String getPropertyName() { + public String propertyName() { return propertyName; } @@ -1818,7 +1830,7 @@ public String getPropertyName() { * * @param propertyName property name */ - public void setPropertyName(String propertyName) { + public void propertyName(String propertyName) { this.propertyName = propertyName; } @@ -1827,7 +1839,7 @@ public void setPropertyName(String propertyName) { * * @return the description for a method */ - public String getDescription() { + public String description() { return description; } @@ -1836,7 +1848,7 @@ public String getDescription() { * * @param description the description for a method */ - public void setDescription(String description) { + public void description(String description) { this.description = description; } @@ -1854,7 +1866,7 @@ public boolean isReturnTypeMandatory() { * * @param returnTypeMandatory if the return type is mandatory */ - public void setReturnTypeMandatory(boolean returnTypeMandatory) { + public void returnTypeMandatory(boolean returnTypeMandatory) { isReturnTypeMandatory = returnTypeMandatory; } @@ -1863,7 +1875,7 @@ public void setReturnTypeMandatory(boolean returnTypeMandatory) { * * @return the default value for this method */ - public Object getDefaultValue() { + public Object defaultValue() { return defaultValue; } @@ -1872,7 +1884,7 @@ public Object getDefaultValue() { * * @param defaultValue the default value for this method */ - public void setDefaultValue(Object defaultValue) { + public void defaultValue(Object defaultValue) { this.defaultValue = defaultValue; } @@ -1890,7 +1902,7 @@ public boolean isArrayReturnTypeMandatory() { * * @param arrayReturnTypeMandatory if the array return type is mandatory */ - public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { + public void arrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { isArrayReturnTypeMandatory = arrayReturnTypeMandatory; } @@ -1899,7 +1911,7 @@ public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { * * @param originalArrayType the original array type */ - public void setOriginalArrayType(Class originalArrayType) { + public void originalArrayType(Class originalArrayType) { this.originalArrayType = originalArrayType; } @@ -1908,7 +1920,7 @@ public void setOriginalArrayType(Class originalArrayType) { * * @return the original array type */ - public Class getOriginalArrayType() { + public Class originalArrayType() { return originalArrayType; } @@ -1917,7 +1929,7 @@ public Class getOriginalArrayType() { * * @param isJsonbFormat if the format is of type JsonB */ - public void setJsonbFormat(boolean isJsonbFormat) { + public void jsonbFormat(boolean isJsonbFormat) { this.isJsonbFormat = isJsonbFormat; } @@ -2039,7 +2051,7 @@ public ReturnType() { * * @return the return class */ - public String getReturnClass() { + public String returnClass() { return returnClass; } @@ -2048,7 +2060,7 @@ public String getReturnClass() { * * @param returnClass the return class */ - public void setReturnClass(String returnClass) { + public void returnClass(String returnClass) { this.returnClass = returnClass; } @@ -2066,7 +2078,7 @@ public boolean isArrayType() { * * @param arrayType if this is an array type */ - public void setArrayType(boolean arrayType) { + public void arrayType(boolean arrayType) { isArrayType = arrayType; } @@ -2084,7 +2096,7 @@ public boolean isMap() { * * @param map if this is a {@link Map} */ - public void setMap(boolean map) { + public void map(boolean map) { isMap = map; } @@ -2093,7 +2105,7 @@ public void setMap(boolean map) { * * @return the type of collection */ - public String getCollectionType() { + public String collectionType() { return collectionType; } @@ -2102,7 +2114,7 @@ public String getCollectionType() { * * @param collectionType the type of collection */ - public void setCollectionType(String collectionType) { + public void collectionType(String collectionType) { this.collectionType = collectionType; } @@ -2111,7 +2123,7 @@ public void setCollectionType(String collectionType) { * * @return the level of arrays */ - public int getArrayLevels() { + public int arrayLevels() { return arrayLevels; } @@ -2120,7 +2132,7 @@ public int getArrayLevels() { * * @param arrayLevels the level of arrays or 0 if not an array */ - public void setArrayLevels(int arrayLevels) { + public void arrayLevels(int arrayLevels) { this.arrayLevels = arrayLevels; } @@ -2138,7 +2150,7 @@ public boolean isReturnTypeMandatory() { * * @param returnTypeMandatory if the return type is mandatory */ - public void setReturnTypeMandatory(boolean returnTypeMandatory) { + public void returnTypeMandatory(boolean returnTypeMandatory) { isReturnTypeMandatory = returnTypeMandatory; } @@ -2147,7 +2159,7 @@ public void setReturnTypeMandatory(boolean returnTypeMandatory) { * * @return the format of the result class */ - public String[] getFormat() { + public String[] format() { if (format == null) { return null; } @@ -2161,7 +2173,7 @@ public String[] getFormat() { * * @param format the format of the result class */ - public void setFormat(String[] format) { + public void format(String[] format) { if (format == null) { this.format = null; } else { @@ -2221,7 +2233,7 @@ public RootTypeResult(String rootTypeName, int levels, boolean isArrayReturnType * * @return root type of the {@link Collection} or {@link Map} */ - public String getRootTypeName() { + public String rootTypeName() { return rootTypeName; } @@ -2230,7 +2242,7 @@ public String getRootTypeName() { * * @return the number of levels in total */ - public int getLevels() { + public int levels() { return levels; } @@ -2243,21 +2255,12 @@ public boolean isArrayReturnTypeMandatory() { return isArrayReturnTypeMandatory; } - /** - * Set if the return type is mandatory. - * - * @param arrayReturnTypeMandatory if the return type is mandatory - */ - public void setArrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { - isArrayReturnTypeMandatory = arrayReturnTypeMandatory; - } - /** * Return the format of the result class. * * @return the format of the result class */ - public String[] getFormat() { + public String[] format() { if (format == null) { return null; } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 383b3ca88a5..7c191ab9d4f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -464,7 +464,7 @@ protected static SchemaScalar getScalar(String clazzName) { * @return true if the give name is a scalar with that name */ protected static boolean isScalar(String scalarName) { - return SUPPORTED_SCALARS.values().stream().anyMatch((s -> s.getName().equals(scalarName))); + return SUPPORTED_SCALARS.values().stream().anyMatch((s -> s.name().equals(scalarName))); } /** @@ -890,11 +890,11 @@ protected static void ensureValidName(Logger logger, String name) { * @param type {@link SchemaType} to check */ protected static void checkScalars(Schema schema, SchemaType type) { - type.getFieldDefinitions().forEach(fd -> { - SchemaScalar scalar = getScalar(fd.getReturnType()); + type.fieldDefinitions().forEach(fd -> { + SchemaScalar scalar = getScalar(fd.returnType()); if (scalar != null) { - fd.setReturnType(scalar.getName()); - if (!schema.containsScalarWithName(scalar.getName())) { + fd.returnType(scalar.name()); + if (!schema.containsScalarWithName(scalar.name())) { schema.addScalar(scalar); } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java index 8f35a5e0e31..27003121ed9 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java @@ -33,7 +33,7 @@ public SchemaInputType(String name, String valueClassName) { @Override public void addFieldDefinition(SchemaFieldDefinition schemaFieldDefinition) { - if (schemaFieldDefinition.getArguments().size() > 0) { + if (schemaFieldDefinition.arguments().size() > 0) { throw new IllegalArgumentException("Input types cannot have fields with arguments"); } super.addFieldDefinition(schemaFieldDefinition); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java index d7eff96f18a..3d35246a6cb 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaScalar.java @@ -65,7 +65,7 @@ public SchemaScalar(String name, String actualClass, GraphQLScalarType graphQLSc * * @return the name of the {@link SchemaScalar} */ - public String getName() { + public String name() { return name; } @@ -74,7 +74,7 @@ public String getName() { * * @return the actual class name of the {@link SchemaScalar} */ - public String getActualClass() { + public String actualClass() { return actualClass; } @@ -83,7 +83,7 @@ public String getActualClass() { * * @return the {@link GraphQLScalarType} instance. */ - public GraphQLScalarType getGraphQLScalarType() { + public GraphQLScalarType graphQLScalarType() { return graphQLScalarType; } @@ -92,7 +92,7 @@ public GraphQLScalarType getGraphQLScalarType() { * * @return default format if none is specified */ - public String getDefaultFormat() { + public String defaultFormat() { return defaultFormat; } @@ -100,13 +100,13 @@ public String getDefaultFormat() { * Set the default format if none is specified. * @param defaultFormat default format if none is specified */ - public void setDefaultFormat(String defaultFormat) { + public void defaultFormat(String defaultFormat) { this.defaultFormat = defaultFormat; } @Override public String getSchemaAsString() { - return "scalar " + getName(); + return "scalar " + name(); } @Override diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java index 5c63047d689..47a542cb0e9 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java @@ -72,7 +72,7 @@ public String getSchemaAsString() { StringBuilder sb = new StringBuilder(getSchemaElementDescription(null)) .append(getGraphQLName()) .append(SPACER) - .append(getName()); + .append(name()); if (implementingInterface != null) { sb.append(" implements " + implementingInterface); @@ -93,9 +93,9 @@ public String getSchemaAsString() { * @return an new {@link SchemaInputType} */ public SchemaInputType createInputType(String sSuffix) { - SchemaInputType inputType = new SchemaInputType(getName() + sSuffix, getValueClassName()); - getFieldDefinitions().forEach(fd -> { - fd.getArguments().clear(); + SchemaInputType inputType = new SchemaInputType(name() + sSuffix, valueClassName()); + fieldDefinitions().forEach(fd -> { + fd.arguments().clear(); inputType.addFieldDefinition(fd); }); return inputType; @@ -106,7 +106,7 @@ public SchemaInputType createInputType(String sSuffix) { * * @param isInterface indicates if the {@link SchemaType} is an interface; */ - public void setIsInterface(boolean isInterface) { + public void isInterface(boolean isInterface) { this.isInterface = isInterface; } @@ -115,7 +115,7 @@ public void setIsInterface(boolean isInterface) { * * @return the name of the {@link SchemaType} */ - public String getName() { + public String name() { return name; } @@ -123,7 +123,7 @@ public String getName() { * Set the name of the {@link SchemaType}. * @param name the name of the {@link SchemaType} */ - public void setName(String name) { + public void name(String name) { this.name = name; } @@ -132,7 +132,7 @@ public void setName(String name) { * * @return the value class name for the @{link Type}. */ - public String getValueClassName() { + public String valueClassName() { return valueClassName; } @@ -141,7 +141,7 @@ public String getValueClassName() { * * @return the {@link List} of {@link SchemaFieldDefinition}s */ - public List getFieldDefinitions() { + public List fieldDefinitions() { return listSchemaFieldDefinitions; } @@ -159,7 +159,7 @@ public boolean isInterface() { * * @return the interface that this {@link SchemaType} implements */ - public String getImplementingInterface() { + public String implementingInterface() { return implementingInterface; } @@ -168,7 +168,7 @@ public String getImplementingInterface() { * * @param implementingInterface the interface that this {@link SchemaType} implements */ - public void setImplementingInterface(String implementingInterface) { + public void implementingInterface(String implementingInterface) { this.implementingInterface = implementingInterface; } @@ -189,7 +189,7 @@ public void addFieldDefinition(SchemaFieldDefinition schemaFieldDefinition) { */ public SchemaFieldDefinition getFieldDefinitionByName(String fdName) { for (SchemaFieldDefinition fieldDefinition : listSchemaFieldDefinitions) { - if (fieldDefinition.getName().equals(fdName)) { + if (fieldDefinition.name().equals(fdName)) { return fieldDefinition; } } @@ -210,7 +210,7 @@ public boolean equals(Object o) { && Objects.equals(valueClassName, schemaType.valueClassName) && Objects.equals(implementingInterface, schemaType.implementingInterface) && Objects.equals(listSchemaFieldDefinitions, schemaType.listSchemaFieldDefinitions) - && Objects.equals(getDescription(), schemaType.getDescription()); + && Objects.equals(description(), schemaType.description()); } @Override @@ -219,7 +219,7 @@ public int hashCode() { valueClassName, isInterface, implementingInterface, - getDescription(), + description(), listSchemaFieldDefinitions); return result; } @@ -239,7 +239,7 @@ protected String toStringInternal() { + "name='" + name + '\'' + ", valueClassName='" + valueClassName + '\'' + ", isInterface='" + isInterface + '\'' - + ", description='" + getDescription() + '\'' + + ", description='" + description() + '\'' + ", implementingInterface='" + implementingInterface + '\'' + ", listFieldDefinitions=" + listSchemaFieldDefinitions + '}'; } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java index 29dec18fcaa..42b94364985 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java @@ -228,15 +228,15 @@ protected void assertDefaultFormat(SchemaType type, String fdName, String defaul SchemaFieldDefinition fd = getFieldDefinition(type, fdName); assertThat(fd, CoreMatchers.is(notNullValue())); assertThat(fd.isDefaultFormatApplied(), is(isDefaultFormatApplied)); - String[] format = fd.getFormat(); + String[] format = fd.format(); assertThat(format, CoreMatchers.is(notNullValue())); assertThat(format.length == 2, CoreMatchers.is(notNullValue())); assertThat(format[0], CoreMatchers.is(defaultFormat)); } protected SchemaFieldDefinition getFieldDefinition(SchemaType type, String name) { - for (SchemaFieldDefinition fd : type.getFieldDefinitions()) { - if (fd.getName().equals(name)) { + for (SchemaFieldDefinition fd : type.fieldDefinitions()) { + if (fd.name().equals(name)) { return fd; } } @@ -245,8 +245,8 @@ protected SchemaFieldDefinition getFieldDefinition(SchemaType type, String name) protected SchemaArgument getArgument(SchemaFieldDefinition fd, String name) { assertThat(fd, CoreMatchers.is(notNullValue())); - for (SchemaArgument argument : fd.getArguments()) { - if (argument.getArgumentName().equals(name)) { + for (SchemaArgument argument : fd.arguments()) { + if (argument.argumentName().equals(name)) { return argument; } } @@ -258,7 +258,7 @@ protected void assertReturnTypeDefaultValue(SchemaType type, String fdName, Stri SchemaFieldDefinition fd = getFieldDefinition(type, fdName); assertThat(fd, CoreMatchers.is(notNullValue())); assertThat("Default value for " + fdName + " should be " + defaultValue + - " but is " + fd.getDefaultValue(), fd.getDefaultValue(), CoreMatchers.is(defaultValue)); + " but is " + fd.defaultValue(), fd.defaultValue(), CoreMatchers.is(defaultValue)); } protected void assertReturnTypeMandatory(SchemaType type, String fdName, boolean mandatory) { @@ -285,7 +285,7 @@ protected void assertReturnTypeArgumentMandatory(SchemaType type, String fdName, SchemaArgument argument = getArgument(fd, argumentName); assertThat(argument, CoreMatchers.is(notNullValue())); assertThat("Return type for argument " + argumentName + " should be mandatory=" - + mandatory + " but is " + argument.isMandatory(), argument.isMandatory(), CoreMatchers.is(mandatory)); + + mandatory + " but is " + argument.mandatory(), argument.mandatory(), CoreMatchers.is(mandatory)); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index 3307eda73ca..b6056903242 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -34,47 +34,47 @@ class SchemaArgumentTest { @Test public void testConstructors() { SchemaArgument schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); - assertThat(schemaArgument.getArgumentName(), is("name")); - assertThat(schemaArgument.getArgumentType(), is("Int")); - assertThat(schemaArgument.isMandatory(), is(true)); - assertThat(schemaArgument.getDefaultValue(), is(nullValue())); - assertThat(schemaArgument.getOriginalType().getName(), is(Integer.class.getName())); + assertThat(schemaArgument.argumentName(), is("name")); + assertThat(schemaArgument.argumentType(), is("Int")); + assertThat(schemaArgument.mandatory(), is(true)); + assertThat(schemaArgument.defaultValue(), is(nullValue())); + assertThat(schemaArgument.originalType().getName(), is(Integer.class.getName())); schemaArgument = new SchemaArgument("name2", "String", false, "Default", STRING); - assertThat(schemaArgument.getArgumentName(), is("name2")); - assertThat(schemaArgument.getArgumentType(), is("String")); - assertThat(schemaArgument.isMandatory(), is(false)); - assertThat(schemaArgument.getDefaultValue(), is("Default")); - assertThat(schemaArgument.getOriginalType().getName(), is(STRING.getName())); + assertThat(schemaArgument.argumentName(), is("name2")); + assertThat(schemaArgument.argumentType(), is("String")); + assertThat(schemaArgument.mandatory(), is(false)); + assertThat(schemaArgument.defaultValue(), is("Default")); + assertThat(schemaArgument.originalType().getName(), is(STRING.getName())); - schemaArgument.setArgumentType("XYZ"); - assertThat(schemaArgument.getArgumentType(), is("XYZ")); + schemaArgument.argumentType("XYZ"); + assertThat(schemaArgument.argumentType(), is("XYZ")); - assertThat(schemaArgument.getDescription(), is(nullValue())); - schemaArgument.setDescription("description"); - assertThat(schemaArgument.getDescription(), is("description")); + assertThat(schemaArgument.description(), is(nullValue())); + schemaArgument.description("description"); + assertThat(schemaArgument.description(), is("description")); assertThat(schemaArgument.isSourceArgument(), is(false)); - schemaArgument.setSourceArgument(true); + schemaArgument.sourceArgument(true); assertThat(schemaArgument.isSourceArgument(), is(true)); - assertThat(schemaArgument.getFormat(), is(nullValue())); - schemaArgument.setFormat(new String[] { "value-1", "value-2"}); - String[] format = schemaArgument.getFormat(); + assertThat(schemaArgument.format(), is(nullValue())); + schemaArgument.format(new String[] { "value-1", "value-2"}); + String[] format = schemaArgument.format(); assertThat(format, is(notNullValue())); assertThat(format.length, is(2)); assertThat(format[0], is("value-1")); assertThat(format[1], is("value-2")); - schemaArgument.setDefaultValue("hello"); - assertThat(schemaArgument.getDefaultValue(), is("hello")); + schemaArgument.defaultValue("hello"); + assertThat(schemaArgument.defaultValue(), is("hello")); - schemaArgument.setDefaultValue("1"); - assertThat(schemaArgument.getDefaultValue(), is("1")); + schemaArgument.defaultValue("1"); + assertThat(schemaArgument.defaultValue(), is("1")); - assertThat(schemaArgument.getOriginalArrayType(), is(nullValue())); - schemaArgument.setOriginalArrayType(String.class); - assertThat(schemaArgument.getOriginalArrayType().getName(), is(String.class.getName())); + assertThat(schemaArgument.originalArrayType(), is(nullValue())); + schemaArgument.originalArrayType(String.class); + assertThat(schemaArgument.originalArrayType().getName(), is(String.class.getName())); } @Test @@ -82,15 +82,15 @@ public void testSchemaArgumentArrayTypes() { SchemaArgument schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); assertThat(schemaArgument.isArrayReturnType(), is(false)); assertThat(schemaArgument.isArrayReturnTypeMandatory(), is(false)); - assertThat(schemaArgument.getArrayLevels(), is(0)); + assertThat(schemaArgument.arrayLevels(), is(0)); } @Test public void testSchemaGenerationWithArrays() { SchemaArgument schemaArgument = new SchemaArgument("name", "String", false, null, STRING); - schemaArgument.setArrayLevels(1); - schemaArgument.setArrayReturnTypeMandatory(true); - schemaArgument.setArrayReturnType(true); + schemaArgument.arrayLevels(1); + schemaArgument.arrayReturnTypeMandatory(true); + schemaArgument.arrayReturnType(true); assertThat(schemaArgument.getSchemaAsString(), is("name: [String!]")); } @@ -115,28 +115,28 @@ public void testSchemaGeneration() { assertThat(schemaArgument.getSchemaAsString(), is("name: String! = \"The Default Value\"")); schemaArgument = new SchemaArgument("name", "Int", false, 10, INTEGER); - schemaArgument.setDescription("Description"); + schemaArgument.description("Description"); assertThat(schemaArgument.getSchemaAsString(), is("\"Description\"\nname: Int = 10")); // test array return types schemaArgument = new SchemaArgument("name", "Int", false, null, INTEGER); - schemaArgument.setArrayReturnType(true); - schemaArgument.setArrayLevels(1); + schemaArgument.arrayReturnType(true); + schemaArgument.arrayLevels(1); assertThat(schemaArgument.getSchemaAsString(), is("name: [Int]")); - schemaArgument.setArrayReturnTypeMandatory(true); + schemaArgument.arrayReturnTypeMandatory(true); assertThat(schemaArgument.getSchemaAsString(), is("name: [Int!]")); schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); - schemaArgument.setArrayReturnType(true); - schemaArgument.setArrayLevels(1); - schemaArgument.setArrayReturnTypeMandatory(true); + schemaArgument.arrayReturnType(true); + schemaArgument.arrayLevels(1); + schemaArgument.arrayReturnTypeMandatory(true); assertThat(schemaArgument.getSchemaAsString(), is("name: [Int!]!")); schemaArgument = new SchemaArgument("name", "String", true, "Hello", STRING); - schemaArgument.setArrayReturnType(true); - schemaArgument.setArrayLevels(3); - schemaArgument.setArrayReturnTypeMandatory(true); + schemaArgument.arrayReturnType(true); + schemaArgument.arrayLevels(3); + schemaArgument.arrayReturnTypeMandatory(true); assertThat(schemaArgument.getSchemaAsString(), is("name: [[[String!]]]! = \"Hello\"")); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java index 06374a43633..e258fdf861a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java @@ -36,16 +36,16 @@ class SchemaDirectiveTest { @Test public void testConstructors() { SchemaDirective schemaDirective = new SchemaDirective("auth"); - assertThat(schemaDirective.getName(), is("auth")); - assertThat(schemaDirective.getArguments().size(), is(0)); - assertThat(schemaDirective.getLocations().size(), is(0)); + assertThat(schemaDirective.name(), is("auth")); + assertThat(schemaDirective.arguments().size(), is(0)); + assertThat(schemaDirective.locations().size(), is(0)); SchemaArgument arg = new SchemaArgument("name", "String", true, null, STRING); schemaDirective.addArgument(arg); - assertThat(schemaDirective.getArguments().contains(arg), is(true)); + assertThat(schemaDirective.arguments().contains(arg), is(true)); schemaDirective.addLocation(FIELD_DEFINITION.name()); - assertThat(schemaDirective.getLocations().contains(FIELD_DEFINITION.name()), is(true)); + assertThat(schemaDirective.locations().contains(FIELD_DEFINITION.name()), is(true)); assertThat(schemaDirective.getSchemaAsString(), is(notNullValue())); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java index 1a3b7279b67..34c4897fc97 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java @@ -30,7 +30,7 @@ class SchemaEnumTest extends AbstractGraphQLTest { @Test public void testConstructor() { SchemaEnum schemaEnum1 = new SchemaEnum("ShirtSize"); - schemaEnum1.setDescription("This is the description of the Enum"); + schemaEnum1.description("This is the description of the Enum"); schemaEnum1.addValue("S"); schemaEnum1.addValue("M"); schemaEnum1.addValue("L"); @@ -38,15 +38,15 @@ public void testConstructor() { schemaEnum1.addValue("XXL"); schemaEnum1.addValue("3XL"); - assertThat(schemaEnum1.getDescription(), is("This is the description of the Enum")); - assertThat(schemaEnum1.getValues(), is(notNullValue())); - assertThat(schemaEnum1.getValues().size(), is(6)); + assertThat(schemaEnum1.description(), is("This is the description of the Enum")); + assertThat(schemaEnum1.values(), is(notNullValue())); + assertThat(schemaEnum1.values().size(), is(6)); } @Test public void testSchemaGenerationWithDescription() { SchemaEnum schemaEnum1 = new SchemaEnum("ShirtSize"); - schemaEnum1.setDescription("T Shirt Size"); + schemaEnum1.description("T Shirt Size"); schemaEnum1.addValue("Small"); schemaEnum1.addValue("Medium"); schemaEnum1.addValue("Large"); @@ -73,7 +73,7 @@ public void testSchemaGenerationWithDescriptionAndQuote() { schemaEnum1.addValue("Medium"); schemaEnum1.addValue("Large"); schemaEnum1.addValue("XLarge"); - schemaEnum1.setDescription("Description\""); + schemaEnum1.description("Description\""); assertResultsMatch(schemaEnum1.getSchemaAsString(), "test-results/enum-test-03.txt"); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java index 55655240dd3..c5570a59801 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java @@ -37,50 +37,50 @@ class SchemaFieldDefinitionTest { @Test public void testConstructors() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("name", "Integer", true, true, 1); - assertThat(schemaFieldDefinition.getName(), is("name")); - assertThat(schemaFieldDefinition.getReturnType(), is("Integer")); - assertThat(schemaFieldDefinition.getArguments(), is(notNullValue())); + assertThat(schemaFieldDefinition.name(), is("name")); + assertThat(schemaFieldDefinition.returnType(), is("Integer")); + assertThat(schemaFieldDefinition.arguments(), is(notNullValue())); assertThat(schemaFieldDefinition.isArrayReturnType(), is(true)); - assertThat(schemaFieldDefinition.getArrayLevels(), is(1)); + assertThat(schemaFieldDefinition.arrayLevels(), is(1)); SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null, STRING); schemaFieldDefinition.addArgument(schemaArgument); - assertThat(schemaFieldDefinition.getArguments().size(), is(1)); - assertThat(schemaFieldDefinition.getArguments().get(0), is(schemaArgument)); + assertThat(schemaFieldDefinition.arguments().size(), is(1)); + assertThat(schemaFieldDefinition.arguments().get(0), is(schemaArgument)); SchemaArgument schemaArgument2 = new SchemaArgument("filter2", "Integer", true, null, INTEGER); schemaFieldDefinition.addArgument(schemaArgument2); - assertThat(schemaFieldDefinition.getArguments().size(), is(2)); - assertThat(schemaFieldDefinition.getArguments().contains(schemaArgument2), is(true)); + assertThat(schemaFieldDefinition.arguments().size(), is(2)); + assertThat(schemaFieldDefinition.arguments().contains(schemaArgument2), is(true)); schemaFieldDefinition = new SchemaFieldDefinition("name2", "String", false, false, 0); - assertThat(schemaFieldDefinition.getName(), is("name2")); - assertThat(schemaFieldDefinition.getReturnType(), is("String")); + assertThat(schemaFieldDefinition.name(), is("name2")); + assertThat(schemaFieldDefinition.returnType(), is("String")); assertThat(schemaFieldDefinition.isArrayReturnType(), is(false)); assertThat(schemaFieldDefinition.isReturnTypeMandatory(), is(false)); - assertThat(schemaFieldDefinition.getArrayLevels(), is(0)); + assertThat(schemaFieldDefinition.arrayLevels(), is(0)); - schemaFieldDefinition.setReturnType("BLAH"); - assertThat(schemaFieldDefinition.getReturnType(), is("BLAH")); + schemaFieldDefinition.returnType("BLAH"); + assertThat(schemaFieldDefinition.returnType(), is("BLAH")); - assertThat(schemaFieldDefinition.getFormat(), is(nullValue())); - schemaFieldDefinition.setFormat(new String[] {"a", "b"}); - String[] format = schemaFieldDefinition.getFormat(); + assertThat(schemaFieldDefinition.format(), is(nullValue())); + schemaFieldDefinition.format(new String[] {"a", "b"}); + String[] format = schemaFieldDefinition.format(); assertThat(format, is(notNullValue())); assertThat(format.length, is(2)); assertThat(format[0], is("a")); assertThat(format[1], is("b")); assertThat(schemaFieldDefinition.isArrayReturnTypeMandatory(), is(false)); - schemaFieldDefinition.setArrayReturnTypeMandatory(true); + schemaFieldDefinition.arrayReturnTypeMandatory(true); assertThat(schemaFieldDefinition.isArrayReturnTypeMandatory(), is(true)); assertThat(schemaFieldDefinition.isDefaultFormatApplied(), is(false)); - schemaFieldDefinition.setDefaultFormatApplied(true); + schemaFieldDefinition.defaultFormatApplied(true); assertThat(schemaFieldDefinition.isDefaultFormatApplied(), is(true)); assertThat(schemaFieldDefinition.isJsonbFormat(), is(false)); - schemaFieldDefinition.setJsonbFormat(true); + schemaFieldDefinition.jsonbFormat(true); assertThat(schemaFieldDefinition.isJsonbFormat(), is(true)); } @@ -93,7 +93,7 @@ public void testFieldDefinitionWithNoArguments() { @Test public void testFieldDefinitionWithNoArgumentsAndDescription() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); - schemaFieldDefinition.setDescription("Description"); + schemaFieldDefinition.description("Description"); assertThat(schemaFieldDefinition.getSchemaAsString(), is("\"Description\"\nperson: Person!")); } @@ -126,7 +126,7 @@ public void testFieldDefinitionWith1Argument() { public void testFieldDefinitionWith1ArgumentAndDescription() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null, STRING); - schemaArgument.setDescription("Optional Filter"); + schemaArgument.description("Optional Filter"); schemaFieldDefinition.addArgument(schemaArgument); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\n\"Optional Filter\"\nfilter: String\n): Person!")); } @@ -141,7 +141,7 @@ public void testFieldDefinitionWith1ArgumentAndArrayType() { @Test public void testFieldDefinitionWithNoArgumentsAndMandatoryArrayType() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("superPowers", "String", true, false, 1); - schemaFieldDefinition.setArrayReturnTypeMandatory(true); + schemaFieldDefinition.arrayReturnTypeMandatory(true); assertThat(schemaFieldDefinition.getSchemaAsString(), is("superPowers: [String!]")); } @@ -171,9 +171,9 @@ public void testFieldDefinitionWithMultipleArguments() { public void testFieldDefinitionWithMultipleArgumentsAndDescription() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); - schemaArgument1.setDescription("Optional filter"); + schemaArgument1.description("Optional filter"); SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); - schemaArgument2.setDescription("Mandatory age"); + schemaArgument2.description("Mandatory age"); schemaFieldDefinition.addArgument(schemaArgument1); schemaFieldDefinition.addArgument(schemaArgument2); assertThat(schemaFieldDefinition.getSchemaAsString(), @@ -183,11 +183,11 @@ public void testFieldDefinitionWithMultipleArgumentsAndDescription() { @Test public void testFieldDefinitionWithMultipleArgumentsAndBothDescriptions() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); - schemaFieldDefinition.setDescription("Description of field definition"); + schemaFieldDefinition.description("Description of field definition"); SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); - schemaArgument1.setDescription("Optional filter"); + schemaArgument1.description("Optional filter"); SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); - schemaArgument2.setDescription("Mandatory age"); + schemaArgument2.description("Mandatory age"); schemaFieldDefinition.addArgument(schemaArgument1); schemaFieldDefinition.addArgument(schemaArgument2); assertThat(schemaFieldDefinition.getSchemaAsString(), @@ -208,9 +208,9 @@ public void testFieldDefinitionWithMultipleArgumentsWithArray() { @SuppressWarnings("unchecked") public void testDataFetchers() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, false, 0); - assertThat(schemaFieldDefinition.getDataFetcher(), is(nullValue())); - schemaFieldDefinition.setDataFetcher(new StaticDataFetcher("Value")); - DataFetcher dataFetcher = schemaFieldDefinition.getDataFetcher(); + assertThat(schemaFieldDefinition.dataFetcher(), is(nullValue())); + schemaFieldDefinition.dataFetcher(new StaticDataFetcher("Value")); + DataFetcher dataFetcher = schemaFieldDefinition.dataFetcher(); assertThat(dataFetcher, is(notNullValue())); assertThat(dataFetcher instanceof StaticDataFetcher, is(true)); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index 2ac56c15c4c..181e449087a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -461,9 +461,9 @@ private void assertDiscoveredMethod(SchemaGenerator.DiscoveredMethod discoveredM assertThat(discoveredMethod.isCollectionType(), is(isCollectionType)); assertThat(discoveredMethod.isArrayReturnType(), is(isArrayReturnType)); assertThat(discoveredMethod.isMap(), is(isMap)); - assertThat(discoveredMethod.getName(), is(name)); - assertThat(discoveredMethod.getReturnType(), is(returnType)); - assertThat(discoveredMethod.getCollectionType(), is(collectionType)); + assertThat(discoveredMethod.name(), is(name)); + assertThat(discoveredMethod.returnType(), is(returnType)); + assertThat(discoveredMethod.collectionType(), is(collectionType)); } @Test @@ -509,22 +509,22 @@ public void testGetRootType() throws NoSuchFieldException, NoSuchMethodException schemaGenerator.getRootTypeName(stringArrayListType.getActualTypeArguments()[0], 0, -1, SchemaGeneratorTest.class.getMethod("getListStringArray")); - assertThat(rootTypeName.getRootTypeName(), is(String[].class.getName())); - assertThat(rootTypeName.getLevels(), is(1)); + assertThat(rootTypeName.rootTypeName(), is(String[].class.getName())); + assertThat(rootTypeName.levels(), is(1)); ParameterizedType stringListType = getParameterizedType("listString"); rootTypeName = schemaGenerator.getRootTypeName(stringListType.getActualTypeArguments()[0], 0, -1, SchemaGeneratorTest.class.getMethod("getListStringArray")); - assertThat(rootTypeName.getRootTypeName(), is(STRING)); - assertThat(rootTypeName.getLevels(), is(1)); + assertThat(rootTypeName.rootTypeName(), is(STRING)); + assertThat(rootTypeName.levels(), is(1)); ParameterizedType listListStringType = getParameterizedType("listListString"); rootTypeName = schemaGenerator.getRootTypeName(listListStringType.getActualTypeArguments()[0], 0, -1, SchemaGeneratorTest.class.getMethod("getListListString")); - assertThat(rootTypeName.getRootTypeName(), is(STRING)); - assertThat(rootTypeName.getLevels(), is(2)); + assertThat(rootTypeName.rootTypeName(), is(STRING)); + assertThat(rootTypeName.levels(), is(2)); } @Test @@ -693,10 +693,10 @@ private void testEnum(Class clazz, String expectedName) throws IntrospectionE SchemaEnum schemaEnumResult = schema.getEnumByName(expectedName); assertThat(schemaEnumResult, is(notNullValue())); - assertThat(schemaEnumResult.getValues().size(), is(6)); + assertThat(schemaEnumResult.values().size(), is(6)); Arrays.stream(new String[] { "S", "M", "L", "XL", "XXL", "XXXL" }) - .forEach(v -> assertThat(schemaEnumResult.getValues().contains(v), is(true))); + .forEach(v -> assertThat(schemaEnumResult.values().contains(v), is(true))); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java index eb008d3dae8..34c6379db84 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaScalarTest.java @@ -34,12 +34,12 @@ class SchemaScalarTest { @Test public void testConstructors() { SchemaScalar schemaScalar = new SchemaScalar("myName", Integer.class.getName(), ExtendedScalars.DateTime, null); - assertThat(schemaScalar.getName(), is("myName")); - assertThat(schemaScalar.getActualClass(), is(Integer.class.getName())); - assertThat(schemaScalar.getGraphQLScalarType().equals(ExtendedScalars.DateTime), is(true)); - assertThat(schemaScalar.getDefaultFormat(), is(nullValue())); - schemaScalar.setDefaultFormat("ABC"); - assertThat(schemaScalar.getDefaultFormat(), is("ABC")); + assertThat(schemaScalar.name(), is("myName")); + assertThat(schemaScalar.actualClass(), is(Integer.class.getName())); + assertThat(schemaScalar.graphQLScalarType().equals(ExtendedScalars.DateTime), is(true)); + assertThat(schemaScalar.defaultFormat(), is(nullValue())); + schemaScalar.defaultFormat("ABC"); + assertThat(schemaScalar.defaultFormat(), is("ABC")); } @Test diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java index e633a5bc68a..ec5d4ff7dff 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java @@ -131,7 +131,7 @@ public void testEnums() { listString.add("JEDI"); listString.add("EMPIRE"); SchemaEnum schemaEnum1 = new SchemaEnum("Episode"); - schemaEnum1.getValues().addAll(listString); + schemaEnum1.values().addAll(listString); schema.addEnum(schemaEnum1); assertThat(schema.getEnums().size(), is(1)); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java index d010b4e1112..b469d819f82 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java @@ -34,9 +34,9 @@ class SchemaTypeTest { @Test public void testConstructors() { SchemaType schemaType = new SchemaType("Name", "com.oracle.test.Value"); - assertThat(schemaType.getName(), is("Name")); - assertThat(schemaType.getValueClassName(), is("com.oracle.test.Value")); - assertThat(schemaType.getFieldDefinitions(), is(notNullValue())); + assertThat(schemaType.name(), is("Name")); + assertThat(schemaType.valueClassName(), is("com.oracle.test.Value")); + assertThat(schemaType.fieldDefinitions(), is(notNullValue())); schemaType.addFieldDefinition(new SchemaFieldDefinition("orderId", "Integer", false, true, 0)); schemaType.addFieldDefinition(new SchemaFieldDefinition("personId", "Integer", false, true, 0)); @@ -45,15 +45,15 @@ public void testConstructors() { schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); - assertThat(schemaType.getFieldDefinitions().size(), is(4)); - assertThat(schemaType.getFieldDefinitions().contains(schemaFieldDefinition), is(true)); + assertThat(schemaType.fieldDefinitions().size(), is(4)); + assertThat(schemaType.fieldDefinitions().contains(schemaFieldDefinition), is(true)); - assertThat(schemaType.getImplementingInterface(), is(nullValue())); - schemaType.setImplementingInterface("Contact"); - assertThat(schemaType.getImplementingInterface(), is("Contact")); + assertThat(schemaType.implementingInterface(), is(nullValue())); + schemaType.implementingInterface("Contact"); + assertThat(schemaType.implementingInterface(), is("Contact")); - schemaType.setName("Name"); - assertThat(schemaType.getName(), is("Name")); + schemaType.name("Name"); + assertThat(schemaType.name(), is("Name")); } @Test @@ -63,7 +63,7 @@ public void testImplementingInterface() { schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); - schemaType.setImplementingInterface("Contact"); + schemaType.implementingInterface("Contact"); assertThat(schemaType.getSchemaAsString(), is("type Person implements Contact {\n" + "person(\nfilter: String, \nage: Int!\n): Person!\n" + @@ -77,7 +77,7 @@ public void testTypeSchemaOutput() { schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); - assertThat(schemaType.getFieldDefinitions().get(0).equals(schemaFieldDefinition), is(true)); + assertThat(schemaType.fieldDefinitions().get(0).equals(schemaFieldDefinition), is(true)); assertThat(schemaType.getSchemaAsString(), is("type Person {\n" + "person(\nfilter: String\n): Person!\n" + @@ -91,9 +91,9 @@ public void testTypeSchemaOutputWithDescription() { SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); - schemaType.setDescription("Type Description"); + schemaType.description("Type Description"); - assertThat(schemaType.getFieldDefinitions().get(0).equals(schemaFieldDefinition), is(true)); + assertThat(schemaType.fieldDefinitions().get(0).equals(schemaFieldDefinition), is(true)); assertThat(schemaType.getSchemaAsString(), is("\"Type Description\"\ntype Person {\n" + "person(\nfilter: String\n): Person!\n" + @@ -119,11 +119,11 @@ public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); - schemaArgument1.setDescription("Argument1 Description"); + schemaArgument1.description("Argument1 Description"); schemaFieldDefinition.addArgument(schemaArgument1); SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); - schemaArgument2.setDescription("Argument 2 Description"); + schemaArgument2.description("Argument 2 Description"); schemaFieldDefinition.addArgument(schemaArgument2); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -140,7 +140,7 @@ public void testTypeInterfaceStringOutputWith2Arguments() { schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, 30, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); - schemaType.setIsInterface(true); + schemaType.isInterface(true); assertThat(schemaType.getGraphQLName(), is("interface")); assertThat(schemaType.getSchemaAsString(), is("interface Person {\n" + @@ -155,7 +155,7 @@ public void testTypeInterfaceStringOutputWith2ArgumentsAndStringDefault() { schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, "hello", STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, 30, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); - schemaType.setIsInterface(true); + schemaType.isInterface(true); assertThat(schemaType.getGraphQLName(), is("interface")); assertThat(schemaType.getSchemaAsString(), is("interface Person {\n" + @@ -192,8 +192,8 @@ public void testCreatingInputTypeFromType() { SchemaInputType inputType = schemaType.createInputType("Input"); assertThat(inputType, is(notNullValue())); - assertThat(inputType.getFieldDefinitions().size(), is(1)); - assertThat(inputType.getFieldDefinitions().get(0).getArguments().size(), is(0)); + assertThat(inputType.fieldDefinitions().size(), is(1)); + assertThat(inputType.fieldDefinitions().get(0).arguments().size(), is(0)); assertThat(inputType.getSchemaAsString(), is("input PersonInput {\n" + "person: Person!\n" + "}\n")); diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java index cb338a5553a..cc1ac439940 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -82,7 +82,7 @@ protected void assertInterfaceResults() throws IntrospectionException, ClassNotF SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); Schema schema = schemaGenerator.generateSchema(); assertThat(schema, CoreMatchers.is(notNullValue())); - schema.getTypes().forEach(t -> System.out.println(t.getName())); + schema.getTypes().forEach(t -> System.out.println(t.name())); assertThat(schema.getTypes().size(), CoreMatchers.is(6)); assertThat(schema.getTypeByName("Vehicle"), CoreMatchers.is(notNullValue())); assertThat(schema.getTypeByName("Car"), CoreMatchers.is(notNullValue())); diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java index f8d265f1f7b..ce5e496db1e 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java @@ -275,9 +275,9 @@ protected void assertArgumentResult(Schema schema, String fdName, String argumentName, Object input, Object expected) throws Exception { SchemaArgument argument = getArgument(schema, "Query", fdName, argumentName); assertThat(argument, is(notNullValue())); - Object result = DataFetcherUtils.generateArgumentValue(schema, argument.getArgumentType(), - argument.getOriginalType(), argument.getOriginalArrayType(), - input, argument.getFormat()); + Object result = DataFetcherUtils.generateArgumentValue(schema, argument.argumentType(), + argument.originalType(), argument.originalArrayType(), + input, argument.format()); if (input instanceof Collection) { // compare each value @@ -299,8 +299,8 @@ protected SchemaArgument getArgument(Schema schema, String typeName, String fdNa if (type != null) { SchemaFieldDefinition fd = getFieldDefinition(type, fdName); if (fd != null) { - return fd.getArguments().stream() - .filter(a -> a.getArgumentName().equals(argumentName)) + return fd.arguments().stream() + .filter(a -> a.argumentName().equals(argumentName)) .findFirst() .orElse(null); } diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java index f178881c6eb..0d3d6e255ca 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java @@ -78,24 +78,24 @@ public void testDateAndTime() throws IOException { SchemaFieldDefinition fd = getFieldDefinition(type, "localDate"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("MM/dd/yyyy")); - assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.format()[0], is("MM/dd/yyyy")); + assertThat(fd.description(), is(nullValue())); assertThat(fd.isDefaultFormatApplied(), is(false)); - assertThat(fd.getReturnType(), is(FORMATTED_DATE_SCALAR)); + assertThat(fd.returnType(), is(FORMATTED_DATE_SCALAR)); fd = getFieldDefinition(type, "localTime"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("hh:mm[:ss]")); - assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.format()[0], is("hh:mm[:ss]")); + assertThat(fd.description(), is(nullValue())); assertThat(fd.isDefaultFormatApplied(), is(false)); - assertThat(fd.getReturnType(), is(FORMATTED_TIME_SCALAR)); + assertThat(fd.returnType(), is(FORMATTED_TIME_SCALAR)); fd = getFieldDefinition(type, "localDate2"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("MM/dd/yyyy")); - assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.format()[0], is("MM/dd/yyyy")); + assertThat(fd.description(), is(nullValue())); assertThat(fd.isDefaultFormatApplied(), is(false)); - assertThat(fd.getReturnType(), is(FORMATTED_DATE_SCALAR)); + assertThat(fd.returnType(), is(FORMATTED_DATE_SCALAR)); // test default values for date and time assertDefaultFormat(type, "offsetTime", "HH[:mm][:ss]Z", true); @@ -115,17 +115,17 @@ public void testDateAndTime() throws IOException { fd = getFieldDefinition(type, "localDateTime"); assertThat(fd, is(notNullValue())); assertThat(fd.isDefaultFormatApplied(), is(true)); - assertThat(fd.getReturnType(), is(DATETIME_SCALAR)); + assertThat(fd.returnType(), is(DATETIME_SCALAR)); fd = getFieldDefinition(type, "localDateNoFormat"); assertThat(fd, is(notNullValue())); assertThat(fd.isDefaultFormatApplied(), is(true)); - assertThat(fd.getReturnType(), is(DATE_SCALAR)); + assertThat(fd.returnType(), is(DATE_SCALAR)); fd = getFieldDefinition(type, "localTimeNoFormat"); assertThat(fd, is(notNullValue())); assertThat(fd.isDefaultFormatApplied(), is(true)); - assertThat(fd.getReturnType(), is(TIME_SCALAR)); + assertThat(fd.returnType(), is(TIME_SCALAR)); Map mapResults = getAndAssertResult( executionContext.execute("query { dateAndTimePOJOQuery { offsetDateTime offsetTime zonedDateTime " @@ -165,10 +165,10 @@ public void testDateAndTime() throws IOException { SchemaType typeQuery = schema.getTypeByName("Query"); fd = getFieldDefinition(typeQuery, "localDateNoFormat"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("yyyy-MM-dd")); - assertThat(fd.getDescription(), is(nullValue())); + assertThat(fd.format()[0], is("yyyy-MM-dd")); + assertThat(fd.description(), is(nullValue())); assertThat(fd.isDefaultFormatApplied(), is(true)); - assertThat(fd.getReturnType(), is(DATE_SCALAR)); + assertThat(fd.returnType(), is(DATE_SCALAR)); mapResults = getAndAssertResult( executionContext @@ -182,11 +182,11 @@ public void testDateAndTime() throws IOException { SchemaType typeMutation = schema.getTypeByName("Mutation"); fd = getFieldDefinition(typeMutation, "echoFormattedDateWithJsonB"); assertThat(fd, is(notNullValue())); - Optional argument = fd.getArguments() - .stream().filter(a -> a.getArgumentName().equals("dates")).findFirst(); + Optional argument = fd.arguments() + .stream().filter(a -> a.argumentName().equals("dates")).findFirst(); assertThat(argument.isPresent(), is(true)); SchemaArgument a = argument.get(); - assertThat(a.getFormat()[0], is("MM/dd/yyyy")); + assertThat(a.format()[0], is("MM/dd/yyyy")); mapResults = getAndAssertResult( executionContext.execute("mutation { echoFormattedDateWithJsonB(dates: [ \"09/22/2020\", \"09/23/2020\" ]) }")); diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java index f97d7843e3a..662df06a2c4 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java @@ -51,8 +51,8 @@ public void setOddNamedQueriesAndMutations() throws IOException { SchemaType query = schema.getTypeByName("Query"); SchemaType mutation = schema.getTypeByName("Mutation"); assertThat(query, is(notNullValue())); - assertThat(query.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("settlement")).count(), is(1L)); - assertThat(mutation.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("getaway")).count(), is(1L)); + assertThat(query.fieldDefinitions().stream().filter(fd -> fd.name().equals("settlement")).count(), is(1L)); + assertThat(mutation.fieldDefinitions().stream().filter(fd -> fd.name().equals("getaway")).count(), is(1L)); } @Test @@ -92,9 +92,9 @@ public void testDefaultValues() throws IOException { assertThat(query, is(notNullValue())); SchemaFieldDefinition fd = query.getFieldDefinitionByName("echoDefaultValuePOJO"); assertThat(fd, is(notNullValue())); - SchemaArgument argument = fd.getArguments().get(0); + SchemaArgument argument = fd.arguments().get(0); assertThat(argument, is(notNullValue())); - assertThat(argument.getDefaultValue(), is( + assertThat(argument.defaultValue(), is( "{ \"id\": \"ID-1\", \"value\": 1000, \"booleanValue\": true, \"dateObject\": \"1968-02-17\"," + " \"formattedIntWithDefault\": \"2 value\", \"offsetDateTime\": \"29 Jan 2020 at 09:45 in zone +0200\"}")); @@ -122,6 +122,6 @@ public void testDefaultValues() throws IOException { fd = getFieldDefinition(type, "value"); assertThat(fd, is(notNullValue())); - assertThat(fd.getDefaultValue(), is("111222")); + assertThat(fd.defaultValue(), is("111222")); } } diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java index db6dfe6743b..0daa9cc5416 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java @@ -48,29 +48,29 @@ public void testDescriptions() throws IOException { assertThat(schema, is(notNullValue())); SchemaType type = schema.getTypeByName("DescriptionType"); assertThat(type, is(notNullValue())); - type.getFieldDefinitions().forEach(fd -> { - if (fd.getName().equals("id")) { - assertThat(fd.getDescription(), is("this is the description")); + type.fieldDefinitions().forEach(fd -> { + if (fd.name().equals("id")) { + assertThat(fd.description(), is("this is the description")); } - if (fd.getName().equals("value")) { - assertThat(fd.getDescription(), is("description of value")); + if (fd.name().equals("value")) { + assertThat(fd.description(), is("description of value")); } - if (fd.getName().equals("longValue1")) { + if (fd.name().equals("longValue1")) { // no description so include the format - assertThat(fd.getDescription(), is(nullValue())); - assertThat(fd.getFormat()[0], is("L-########")); + assertThat(fd.description(), is(nullValue())); + assertThat(fd.format()[0], is("L-########")); } - if (fd.getName().equals("longValue2")) { + if (fd.name().equals("longValue2")) { // both description and formatting - assertThat(fd.getDescription(), is("Description")); + assertThat(fd.description(), is("Description")); } }); SchemaInputType inputType = schema.getInputTypeByName("DescriptionTypeInput"); assertThat(inputType, is(notNullValue())); - inputType.getFieldDefinitions().forEach(fd -> { - if (fd.getName().equals("value")) { - assertThat(fd.getDescription(), is("description on set for input")); + inputType.fieldDefinitions().forEach(fd -> { + if (fd.name().equals("value")) { + assertThat(fd.description(), is("description on set for input")); } }); @@ -79,9 +79,9 @@ public void testDescriptions() throws IOException { SchemaFieldDefinition fd = getFieldDefinition(query, "descriptionOnParam"); assertThat(fd, (is(notNullValue()))); - fd.getArguments().forEach(a -> { - if (a.getArgumentName().equals("param1")) { - assertThat(a.getDescription(), is("Description for param1")); + fd.arguments().forEach(a -> { + if (a.argumentName().equals("param1")) { + assertThat(a.description(), is("Description for param1")); } }); } diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java index 345e325d992..b94931279e4 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java @@ -64,12 +64,12 @@ public void testIgnorable() throws IOException { Schema schema = executionContext.getSchema(); SchemaType type = schema.getTypeByName("ObjectWithIgnorableFieldsAndMethods"); assertThat(type, is(notNullValue())); - assertThat(type.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreGetMethod")).count(), is(0L)); + assertThat(type.fieldDefinitions().stream().filter(fd -> fd.name().equals("ignoreGetMethod")).count(), is(0L)); SchemaInputType inputType = schema.getInputTypeByName("ObjectWithIgnorableFieldsAndMethodsInput"); assertThat(inputType, is(notNullValue())); - assertThat(inputType.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("ignoreBecauseOfMethod")).count(), + assertThat(inputType.fieldDefinitions().stream().filter(fd -> fd.name().equals("ignoreBecauseOfMethod")).count(), is(0L)); - assertThat(inputType.getFieldDefinitions().stream().filter(fd -> fd.getName().equals("valueSetter")).count(), is(1L)); + assertThat(inputType.fieldDefinitions().stream().filter(fd -> fd.name().equals("valueSetter")).count(), is(1L)); } } diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java index 983eea71572..ab4bf4c2bc2 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NumberFormatIT.java @@ -150,36 +150,36 @@ public void testCorrectNumberScalarTypesAndFormats() throws IOException { SchemaFieldDefinition fd = getFieldDefinition(type, "id"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("0 'id'")); - assertThat(fd.getReturnType(), is(STRING)); + assertThat(fd.format()[0], is("0 'id'")); + assertThat(fd.returnType(), is(STRING)); fd = getFieldDefinition(type, "age"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("0 'years old'")); - assertThat(fd.getReturnType(), is(STRING)); + assertThat(fd.format()[0], is("0 'years old'")); + assertThat(fd.returnType(), is(STRING)); // validate the formats on the Input Type SchemaType inputType = schema.getInputTypeByName("SimpleContactWithNumberFormatsInput"); assertThat(inputType, is(notNullValue())); fd = getFieldDefinition(inputType, "id"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is(nullValue())); - assertThat(fd.getReturnType(), is(INT)); + assertThat(fd.format()[0], is(nullValue())); + assertThat(fd.returnType(), is(INT)); fd = getFieldDefinition(inputType, "longValue"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("LongValue-##########")); - assertThat(fd.getReturnType(), is(STRING)); + assertThat(fd.format()[0], is("LongValue-##########")); + assertThat(fd.returnType(), is(STRING)); fd = getFieldDefinition(inputType, "age"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("0 'years old'")); - assertThat(fd.getReturnType(), is(STRING)); + assertThat(fd.format()[0], is("0 'years old'")); + assertThat(fd.returnType(), is(STRING)); fd = getFieldDefinition(inputType, "listDates"); assertThat(fd, is(notNullValue())); - assertThat(fd.getFormat()[0], is("DD-MM-YYYY")); - assertThat(fd.getReturnType(), is(FORMATTED_DATE_SCALAR)); + assertThat(fd.format()[0], is("DD-MM-YYYY")); + assertThat(fd.returnType(), is(FORMATTED_DATE_SCALAR)); } } From f10d39038a8b59a6d0aafbe99c9617b9fafbd823 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 27 Oct 2020 16:30:18 +0800 Subject: [PATCH 128/178] Progress on adding Builders --- .../graphql/server/SchemaDirective.java | 68 ++++- .../graphql/server/SchemaEnum.java | 71 ++++- .../graphql/server/SchemaFieldDefinition.java | 250 ++++++++++++++++-- .../graphql/server/SchemaGenerator.java | 26 +- .../graphql/server/SchemaDirectiveTest.java | 58 ++-- .../graphql/server/SchemaEnumTest.java | 59 +++-- .../server/SchemaFieldDefinitionTest.java | 198 +++++++++++--- .../graphql/server/SchemaTest.java | 16 +- .../graphql/server/SchemaTypeTest.java | 138 ++++++++-- 9 files changed, 736 insertions(+), 148 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java index d001673859e..f840e263679 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaDirective.java @@ -47,12 +47,21 @@ public class SchemaDirective implements ElementGenerator { /** * Construct a {@link SchemaDirective}. * - * @param name name of the directive + * @param builder the {@link Builder} to construct from */ - public SchemaDirective(String name) { - this.name = name; - this.listSchemaArguments = new ArrayList<>(); - this.setLocations = new LinkedHashSet<>(); + private SchemaDirective(Builder builder) { + this.name = builder.name; + this.listSchemaArguments = builder.listSchemaArguments; + this.setLocations = builder.setLocations; + } + + /** + * Fluent API builder to create {@link SchemaDirective}. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder(); } /** @@ -158,4 +167,53 @@ public String toString() { + ", setLocations=" + setLocations + '}'; } + + /** + * A fluent API {@link io.helidon.common.Builder} to build instances of {@link SchemaDirective}. + */ + public static class Builder implements io.helidon.common.Builder { + + private String name; + private List listSchemaArguments = new ArrayList<>(); + private Set setLocations = new LinkedHashSet<>(); + + /** + * Set the name. + * + * @param name the name + * @return updated builder instance + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Add an argument to the {@link SchemaDirective}. + * + * @param argument the argument to add to the {@link SchemaDirective} + * @return updated builder instance + */ + public Builder addArgument(SchemaArgument argument) { + listSchemaArguments.add(argument); + return this; + } + + /** + * Add a location to the {@link SchemaDirective}. + * + * @param location the location to add to the {@link SchemaDirective} + * @return updated builder instance + */ + public Builder addLocation(String location) { + this.setLocations.add(location); + return this; + } + + @Override + public SchemaDirective build() { + Objects.requireNonNull(name, "Name must be specified"); + return new SchemaDirective(this); + } + } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java index 6353d6a5be8..9c1c6b27dc1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaEnum.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * The representation of a GraphQL Enum. @@ -35,12 +36,23 @@ public class SchemaEnum extends AbstractDescriptiveElement implements ElementGen private List values; /** - * Construct an Enum. - * @param name name for the enum + * Construct a {@link SchemaEnum}. + * + * @param builder the {@link SchemaDirective.Builder} to construct from */ - public SchemaEnum(String name) { - this.name = name; - this.values = new ArrayList<>(); + private SchemaEnum(Builder builder) { + this.name = builder.name; + this.values = builder.values; + description(builder.description); + } + + /** + * Fluent API builder to create {@link SchemaEnum}. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder(); } /** @@ -97,4 +109,53 @@ public String toString() { + ", values=" + values + ", description='" + description() + '\'' + '}'; } + + /** + * A fluent API {@link io.helidon.common.Builder} to build instances of {@link SchemaDirective}. + */ + public static class Builder implements io.helidon.common.Builder { + + private String name; + private List values = new ArrayList<>(); + private String description; + + /** + * Set the name. + * + * @param name the name of the {@link SchemaEnum} + * @return updated builder instance + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Add a value to the {@link SchemaEnum}. + * + * @param value value to add + * @return updated builder instance + */ + public Builder addValue(String value) { + this.values.add(value); + return this; + } + + /** + * Set the description. + * @param description the description of the {@link SchemaEnum} + * @return updated builder instance + */ + public Builder description(String description) { + this.description = description; + return this; + } + + @Override + public SchemaEnum build() { + Objects.requireNonNull(name, "Name must be specified"); + return new SchemaEnum(this); + } + } + } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index 68e15f6a8bb..d3f96c94da7 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import graphql.schema.DataFetcher; @@ -103,28 +104,37 @@ public class SchemaFieldDefinition extends AbstractDescriptiveElement implements */ private boolean isJsonbProperty; - /** * Construct a {@link SchemaFieldDefinition}. * - * @param name field definition name - * @param returnType return type - * @param isArrayReturnType indicates if the return type is an array type such as a native array([]) or a List, - * Collection, etc - * @param isReturnTypeMandatory indicates if the return type is mandatory. - * @param arrayLevels the number of array levels if return type is an array. - */ - public SchemaFieldDefinition(String name, - String returnType, - boolean isArrayReturnType, - boolean isReturnTypeMandatory, - int arrayLevels) { - this.name = name; - this.returnType = returnType; - this.listSchemaArguments = new ArrayList<>(); - this.isArrayReturnType = isArrayReturnType; - this.isReturnTypeMandatory = isReturnTypeMandatory; - this.arrayLevels = arrayLevels; + * @param builder the {@link Builder} to construct from + */ + private SchemaFieldDefinition(Builder builder) { + this.name = builder.name; + this.returnType = builder.returnType; + this.isArrayReturnType = builder.isArrayReturnType; + this.arrayLevels = builder.arrayLevels; + this.isReturnTypeMandatory = builder.isReturnTypeMandatory; + this.listSchemaArguments = builder.listSchemaArguments; + this.isArrayReturnTypeMandatory = builder.isArrayReturnTypeMandatory; + this.dataFetcher = builder.dataFetcher; + this.originalType = builder.originalType; + this.originalArrayType = builder.originalArrayType; + this.format = builder.format; + this.defaultValue = builder.defaultValue; + this.defaultFormatApplied = builder.defaultFormatApplied; + this.isJsonbFormat = builder.isJsonbFormat; + this.isJsonbProperty = builder.isJsonbProperty; + description(builder.description); + } + + /** + * Fluent API builder to create {@link SchemaFieldDefinition}. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder(); } @Override @@ -420,4 +430,206 @@ public String toString() { + ", isJsonbProperty=" + isJsonbProperty + ", description='" + description() + '\'' + '}'; } + + + /** + * A fluent API {@link io.helidon.common.Builder} to build instances of {@link SchemaFieldDefinition}. + */ + public static class Builder implements io.helidon.common.Builder { + private String name; + private String returnType; + private boolean isArrayReturnType; + private int arrayLevels; + private boolean isReturnTypeMandatory; + private List listSchemaArguments = new ArrayList<>(); + private boolean isArrayReturnTypeMandatory; + private DataFetcher dataFetcher; + private Class originalType; + private Class originalArrayType; + private String[] format; + private Object defaultValue; + private boolean defaultFormatApplied; + private boolean isJsonbFormat; + private boolean isJsonbProperty; + private String description; + + /** + * Set the name of the {@link SchemaFieldDefinition}. + * + * @param name the name of the {@link SchemaFieldDefinition} + * @return updated builder instance + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Set the returnType.. + * + * @param returnType the returnType + * @return updated builder instance + */ + public Builder returnType(String returnType) { + this.returnType = returnType; + return this; + } + + /** + * Set if the return type is an array type such as a native array([]) or a List, Collection. + * + * @param isArrayReturnType true if the return type is an array type + * @return updated builder instance + */ + public Builder arrayReturnType(boolean isArrayReturnType) { + this.isArrayReturnType = isArrayReturnType; + return this; + } + + /** + * Set if the return type is mandatory. + * + * @param isReturnTypeMandatory true if the return type is mandatory. + * @return updated builder instance + */ + public Builder returnTypeMandatory(boolean isReturnTypeMandatory) { + this.isReturnTypeMandatory = isReturnTypeMandatory; + return this; + } + + /** + * Set the number of array levels if return type is an array. + * + * @param arrayLevels the number of array levels if return type is an array + * @return updated builder instance + */ + public Builder arrayLevels(int arrayLevels) { + this.arrayLevels = arrayLevels; + return this; + } + + /** + * Add an argument to the {@link SchemaFieldDefinition}. + * + * @param argument the argument to add to the {@link SchemaFieldDefinition} + * @return updated builder instance + */ + public Builder addArgument(SchemaArgument argument) { + listSchemaArguments.add(argument); + return this; + } + + /** + * Set if the value of the array is mandatory. + * + * @param isArrayReturnTypeMandatory If the return type is an array then indicates if the value in the + * array is mandatory + * @return updated builder instance + */ + public Builder arrayReturnTypeMandatory(boolean isArrayReturnTypeMandatory) { + this.isArrayReturnTypeMandatory = isArrayReturnTypeMandatory; + return this; + } + + /** + * Set the {@link DataFetcher} to override default behaviour of field. + * @param dataFetcher {@link DataFetcher} to override default behaviour of field + * @return updated builder instance + */ + public Builder dataFetcher(DataFetcher dataFetcher) { + this.dataFetcher = dataFetcher; + return this; + } + + /** + * Set the original return type. + * @param originalType the original return type + * @return updated builder instance + */ + public Builder originalType(Class originalType) { + this.originalType = originalType; + return this; + } + + /** + * Set the original array inner type if it is array type. + * @param originalArrayType the original array inner type if it is array type + * @return updated builder instance + */ + public Builder originalArrayType(Class originalArrayType) { + this.originalArrayType = originalArrayType; + return this; + } + + /** + * Set the format for a number or date. + * @param format the format for a number or date + * @return updated builder instance + */ + public Builder format(String[] format) { + if (format == null) { + this.format = null; + } else { + this.format = new String[format.length]; + System.arraycopy(format, 0, this.format, 0, this.format.length); + } + + return this; + } + + /** + * Set the default value for this field definition. Only valid for field definitions of an input type. + * @param defaultValue the default value for this field definition + * @return updated builder instance + */ + public Builder defaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + /** + * Set if the field has a default format applied. + * @param defaultFormatApplied true if the field has a default format applied + * @return updated builder instance + */ + public Builder defaultFormatApplied(boolean defaultFormatApplied) { + this.defaultFormatApplied = defaultFormatApplied; + return this; + } + + /** + * Set if the format is of type Jsonb. + * @param isJsonbFormat if the format is of type Jsonb. + * @return updated builder instance + */ + public Builder jsonbFormat(boolean isJsonbFormat) { + this.isJsonbFormat = isJsonbFormat; + return this; + } + /** + * Set if the property name is of type Jsonb. + * @param isJsonbProperty if the property name is of type Jsonb. + * @return updated builder instance + */ + public Builder jsonbProperty(boolean isJsonbProperty) { + this.isJsonbProperty = isJsonbProperty; + return this; + } + + /** + * Set the description. + * @param description the description of the {@link SchemaFieldDefinition} + * @return updated builder instance + */ + public Builder description(String description) { + this.description = description; + return this; + } + + @Override + public SchemaFieldDefinition build() { + Objects.requireNonNull(name, "Name must be specified"); + return new SchemaFieldDefinition(this); + } + } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index b0807739ff2..b5b4201b2cb 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -767,7 +767,7 @@ private void addTypeToSchema(Schema schema, SchemaType type) */ private SchemaEnum generateEnum(Class clazz) { if (clazz.isEnum()) { - SchemaEnum newSchemaEnum = new SchemaEnum(getTypeName(clazz)); + SchemaEnum newSchemaEnum = SchemaEnum.builder().name(getTypeName(clazz)).build(); Arrays.stream(clazz.getEnumConstants()) .map(Object::toString) @@ -825,17 +825,19 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth } } - SchemaFieldDefinition fd = new SchemaFieldDefinition(optionalName != null - ? optionalName - : discoveredMethod.name, - graphQLType, - isArrayReturnType, - discoveredMethod.isReturnTypeMandatory(), - discoveredMethod.arrayLevels()); - fd.dataFetcher(dataFetcher); - fd.originalType(discoveredMethod.method().getReturnType()); - fd.arrayReturnTypeMandatory(discoveredMethod.isArrayReturnTypeMandatory()); - fd.originalArrayType(isArrayReturnType ? discoveredMethod.originalArrayType() : null); + SchemaFieldDefinition fd = SchemaFieldDefinition.builder() + .name(optionalName != null + ? optionalName + : discoveredMethod.name) + .returnType(graphQLType) + .arrayReturnType(isArrayReturnType) + .returnTypeMandatory(discoveredMethod.isReturnTypeMandatory()) + .arrayLevels(discoveredMethod.arrayLevels()) + .dataFetcher(dataFetcher) + .originalType(discoveredMethod.method().getReturnType()) + .arrayReturnTypeMandatory(discoveredMethod.isArrayReturnTypeMandatory()) + .originalArrayType(isArrayReturnType ? discoveredMethod.originalArrayType() : null) + .build(); if (format != null && format.length == 3) { fd.format(new String[] {format[1], format[2] }); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java index e258fdf861a..8f1d6a9ed2f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java @@ -35,7 +35,7 @@ class SchemaDirectiveTest { @Test public void testConstructors() { - SchemaDirective schemaDirective = new SchemaDirective("auth"); + SchemaDirective schemaDirective = SchemaDirective.builder().name("auth").build(); assertThat(schemaDirective.name(), is("auth")); assertThat(schemaDirective.arguments().size(), is(0)); assertThat(schemaDirective.locations().size(), is(0)); @@ -52,17 +52,20 @@ public void testConstructors() { @Test public void testDirectiveWith0Argument1Location() { - SchemaDirective schemaDirective = new SchemaDirective("directiveName"); - schemaDirective.addLocation(FIELD_DEFINITION.name()); + SchemaDirective schemaDirective = SchemaDirective.builder() + .name("directiveName") + .addLocation(FIELD_DEFINITION.name()).build(); assertThat(schemaDirective.getSchemaAsString(), is("directive @directiveName on " + FIELD_DEFINITION.name())); } @Test public void testDirectiveWith1Argument1Location() { SchemaArgument arg = new SchemaArgument("name", "String", true, null, STRING); - SchemaDirective schemaDirective = new SchemaDirective("directiveName"); - schemaDirective.addArgument(arg); - schemaDirective.addLocation(FIELD_DEFINITION.name()); + SchemaDirective schemaDirective = SchemaDirective.builder() + .name("directiveName") + .addArgument(arg) + .addLocation(FIELD_DEFINITION.name()) + .build(); assertThat(schemaDirective.getSchemaAsString(), is("directive @directiveName(name: String!) on " + FIELD_DEFINITION.name())); } @@ -70,10 +73,12 @@ public void testDirectiveWith1Argument1Location() { public void testDirectiveWith2Argument1Location() { SchemaArgument arg1 = new SchemaArgument("name", "String", true, null, STRING); SchemaArgument arg2 = new SchemaArgument("name1", "Int", false, null, INTEGER); - SchemaDirective schemaDirective = new SchemaDirective("directiveName"); - schemaDirective.addArgument(arg1); - schemaDirective.addArgument(arg2); - schemaDirective.addLocation(FIELD_DEFINITION.name()); + SchemaDirective schemaDirective = SchemaDirective.builder() + .name("directiveName") + .addArgument(arg1) + .addArgument(arg2) + .addLocation(FIELD_DEFINITION.name()) + .build(); assertThat(schemaDirective.getSchemaAsString(), is("directive @directiveName(name: String!, name1: Int) on " + FIELD_DEFINITION.name())); } @@ -82,31 +87,38 @@ public void testDirectiveWith2Argument1Location() { public void testDirectiveWith2Argument2Location() { SchemaArgument arg1 = new SchemaArgument("name", "String", true, null, STRING); SchemaArgument arg2 = new SchemaArgument("name1", "Int", false, null,INTEGER); - SchemaDirective schemaDirective = new SchemaDirective("directiveName"); - schemaDirective.addArgument(arg1); - schemaDirective.addArgument(arg2); - schemaDirective.addLocation(FIELD_DEFINITION.name()); - schemaDirective.addLocation(FIELD.name()); + SchemaDirective schemaDirective = SchemaDirective.builder() + .name("directiveName") + .addArgument(arg1) + .addArgument(arg2) + .addLocation(FIELD_DEFINITION.name()) + .addLocation(FIELD.name()) + .build(); + assertThat(schemaDirective.getSchemaAsString(), is("directive @directiveName(name: String!, name1: Int) on " + FIELD_DEFINITION.name() + "|" + FIELD.name())); } @Test public void testDirectiveWith0Argument2Location() { - SchemaDirective schemaDirective = new SchemaDirective("directiveName"); - schemaDirective.addLocation(FIELD_DEFINITION.name()); - schemaDirective.addLocation(FIELD.name()); + SchemaDirective schemaDirective = SchemaDirective.builder() + .name("directiveName") + .addLocation(FIELD_DEFINITION.name()) + .addLocation(FIELD.name()) + .build(); + assertThat(schemaDirective.getSchemaAsString(), is("directive @directiveName on " + FIELD_DEFINITION.name() + "|" + FIELD.name())); } @Test - public void testDirectiveWith0Argument3Location() { - SchemaDirective schemaDirective = new SchemaDirective("directiveName"); - schemaDirective.addLocation(FIELD_DEFINITION.name()); - schemaDirective.addLocation(FIELD.name()); - schemaDirective.addLocation(QUERY.name()); + SchemaDirective schemaDirective = SchemaDirective.builder() + .name("directiveName") + .addLocation(FIELD_DEFINITION.name()) + .addLocation(FIELD.name()) + .addLocation(QUERY.name()) + .build(); assertThat(schemaDirective.getSchemaAsString(), is("directive @directiveName on " + FIELD_DEFINITION.name() + "|" + FIELD.name() + "|" + QUERY.name())); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java index 34c4897fc97..7dcc7e6bd2e 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaEnumTest.java @@ -29,14 +29,17 @@ class SchemaEnumTest extends AbstractGraphQLTest { @Test public void testConstructor() { - SchemaEnum schemaEnum1 = new SchemaEnum("ShirtSize"); - schemaEnum1.description("This is the description of the Enum"); - schemaEnum1.addValue("S"); - schemaEnum1.addValue("M"); - schemaEnum1.addValue("L"); - schemaEnum1.addValue("XL"); - schemaEnum1.addValue("XXL"); - schemaEnum1.addValue("3XL"); + + SchemaEnum schemaEnum1 = SchemaEnum.builder() + .name("ShirtSize") + .description("This is the description of the Enum") + .addValue("S") + .addValue("M") + .addValue("L") + .addValue("XL") + .addValue("XXL") + .addValue("3XL") + .build(); assertThat(schemaEnum1.description(), is("This is the description of the Enum")); assertThat(schemaEnum1.values(), is(notNullValue())); @@ -45,35 +48,41 @@ public void testConstructor() { @Test public void testSchemaGenerationWithDescription() { - SchemaEnum schemaEnum1 = new SchemaEnum("ShirtSize"); - schemaEnum1.description("T Shirt Size"); - schemaEnum1.addValue("Small"); - schemaEnum1.addValue("Medium"); - schemaEnum1.addValue("Large"); - schemaEnum1.addValue("XLarge"); + SchemaEnum schemaEnum1 = SchemaEnum.builder() + .name("ShirtSize") + .description("T Shirt Size") + .addValue("Small") + .addValue("Medium") + .addValue("Large") + .addValue("XLarge") + .build(); assertResultsMatch(schemaEnum1.getSchemaAsString(), "test-results/enum-test-01.txt"); } @Test public void testSchemaGenerationWithoutDescription() { - SchemaEnum schemaEnum1 = new SchemaEnum("ShirtSize"); - schemaEnum1.addValue("Small"); - schemaEnum1.addValue("Medium"); - schemaEnum1.addValue("Large"); - schemaEnum1.addValue("XLarge"); + SchemaEnum schemaEnum1 = SchemaEnum.builder() + .name("ShirtSize") + .addValue("Small") + .addValue("Medium") + .addValue("Large") + .addValue("XLarge") + .build(); assertResultsMatch(schemaEnum1.getSchemaAsString(), "test-results/enum-test-02.txt"); } @Test public void testSchemaGenerationWithDescriptionAndQuote() { - SchemaEnum schemaEnum1 = new SchemaEnum("ShirtSize"); - schemaEnum1.addValue("Small"); - schemaEnum1.addValue("Medium"); - schemaEnum1.addValue("Large"); - schemaEnum1.addValue("XLarge"); - schemaEnum1.description("Description\""); + SchemaEnum schemaEnum1 = SchemaEnum.builder() + .name("ShirtSize") + .addValue("Small") + .addValue("Medium") + .addValue("Large") + .addValue("XLarge") + .description("Description\"") + .build(); assertResultsMatch(schemaEnum1.getSchemaAsString(), "test-results/enum-test-03.txt"); } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java index c5570a59801..b001276397a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java @@ -30,13 +30,20 @@ * Tests for {@link SchemaArgument} class. */ class SchemaFieldDefinitionTest { - + private static final Class STRING = String.class; private static final Class INTEGER = Integer.class; @Test public void testConstructors() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("name", "Integer", true, true, 1); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("name") + .returnType("Integer") + .arrayReturnType(true) + .returnTypeMandatory(true) + .arrayLevels(1) + .build(); + assertThat(schemaFieldDefinition.name(), is("name")); assertThat(schemaFieldDefinition.returnType(), is("Integer")); assertThat(schemaFieldDefinition.arguments(), is(notNullValue())); @@ -53,7 +60,13 @@ public void testConstructors() { assertThat(schemaFieldDefinition.arguments().size(), is(2)); assertThat(schemaFieldDefinition.arguments().contains(schemaArgument2), is(true)); - schemaFieldDefinition = new SchemaFieldDefinition("name2", "String", false, false, 0); + schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("name2") + .returnType("String") + .arrayReturnType(false) + .returnTypeMandatory(false) + .arrayLevels(0) + .build(); assertThat(schemaFieldDefinition.name(), is("name2")); assertThat(schemaFieldDefinition.returnType(), is("String")); assertThat(schemaFieldDefinition.isArrayReturnType(), is(false)); @@ -64,7 +77,7 @@ public void testConstructors() { assertThat(schemaFieldDefinition.returnType(), is("BLAH")); assertThat(schemaFieldDefinition.format(), is(nullValue())); - schemaFieldDefinition.format(new String[] {"a", "b"}); + schemaFieldDefinition.format(new String[] { "a", "b" }); String[] format = schemaFieldDefinition.format(); assertThat(format, is(notNullValue())); assertThat(format.length, is(2)); @@ -86,90 +99,181 @@ public void testConstructors() { @Test public void testFieldDefinitionWithNoArguments() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person: Person!")); } @Test public void testFieldDefinitionWithNoArgumentsAndDescription() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); - schemaFieldDefinition.description("Description"); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .description("Description") + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("\"Description\"\nperson: Person!")); } @Test public void testFieldDefinitionWithNoArgumentsAndArrayType1ArrayLevel() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true, 1); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(true) + .returnTypeMandatory(true) + .arrayLevels(1) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person: [Person]!")); } @Test public void testFieldDefinitionWithNoArgumentsAndArrayType2ArrayLevels() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true, 2); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(true) + .returnTypeMandatory(true) + .arrayLevels(2) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person: [[Person]]!")); } @Test public void testFieldDefinitionWithNoArgumentsAndArrayType3ArrayLevels() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("result", "String", true, true, 3); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("result") + .returnType("String") + .arrayReturnType(true) + .returnTypeMandatory(true) + .arrayLevels(3) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("result: [[[String]]]!")); } @Test public void testFieldDefinitionWith1Argument() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): Person!")); } @Test public void testFieldDefinitionWith1ArgumentAndDescription() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument.description("Optional Filter"); - schemaFieldDefinition.addArgument(schemaArgument); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .addArgument(schemaArgument) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\n\"Optional Filter\"\nfilter: String\n): Person!")); } @Test public void testFieldDefinitionWith1ArgumentAndArrayType() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true, 1); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(true) + .returnTypeMandatory(true) + .arrayLevels(1) + .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): [Person]!")); } @Test public void testFieldDefinitionWithNoArgumentsAndMandatoryArrayType() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("superPowers", "String", true, false, 1); - schemaFieldDefinition.arrayReturnTypeMandatory(true); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("superPowers") + .returnType("String") + .arrayReturnType(true) + .returnTypeMandatory(false) + .arrayLevels(1) + .arrayReturnTypeMandatory(true) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("superPowers: [String!]")); } @Test public void testFieldDefinitionWith1MandatoryArgument() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", true, null, STRING)); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .addArgument(new SchemaArgument("filter", "String", true, null, STRING)) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String!\n): Person!")); } @Test public void testFieldDefinitionWith1MandatoryArgumentAndArrayType() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, true, 1); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(true) + .returnTypeMandatory(true) + .arrayLevels(1) + .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): [Person]!")); } @Test public void testFieldDefinitionWithMultipleArguments() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)) + .build(); + assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String, \nage: Int!\n): Person!")); } @Test public void testFieldDefinitionWithMultipleArgumentsAndDescription() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument1.description("Optional filter"); SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); @@ -182,8 +286,15 @@ public void testFieldDefinitionWithMultipleArgumentsAndDescription() { @Test public void testFieldDefinitionWithMultipleArgumentsAndBothDescriptions() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); - schemaFieldDefinition.description("Description of field definition"); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .description("Description of field definition") + .build(); + SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument1.description("Optional filter"); SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); @@ -191,23 +302,38 @@ public void testFieldDefinitionWithMultipleArgumentsAndBothDescriptions() { schemaFieldDefinition.addArgument(schemaArgument1); schemaFieldDefinition.addArgument(schemaArgument2); assertThat(schemaFieldDefinition.getSchemaAsString(), - is("\"Description of field definition\"\nperson(\n\"Optional filter\"\nfilter: String, \n\"Mandatory age\"\nage: " + is("\"Description of field definition\"\nperson(\n\"Optional filter\"\nfilter: String, \n\"Mandatory " + + "age\"\nage: " + "Int!\n): Person!")); } @Test public void testFieldDefinitionWithMultipleArgumentsWithArray() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, false, 1); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); - schemaFieldDefinition.addArgument(new SchemaArgument("job", "String", false, null, STRING)); - assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String, \nage: Int!, \njob: String\n): [Person]")); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(true) + .returnTypeMandatory(false) + .arrayLevels(1) + .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)) + .addArgument(new SchemaArgument("job", "String", false, null, STRING)) + .build(); + + assertThat(schemaFieldDefinition.getSchemaAsString(), + is("person(\nfilter: String, \nage: Int!, \njob: String\n): [Person]")); } @Test - @SuppressWarnings("unchecked") public void testDataFetchers() { - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", true, false, 0); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(true) + .returnTypeMandatory(false) + .arrayLevels(0) + .build(); + assertThat(schemaFieldDefinition.dataFetcher(), is(nullValue())); schemaFieldDefinition.dataFetcher(new StaticDataFetcher("Value")); DataFetcher dataFetcher = schemaFieldDefinition.dataFetcher(); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java index ec5d4ff7dff..70d245af09f 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java @@ -57,9 +57,13 @@ public void testTopLevelSchemaAsString() { schema.addType(new SchemaType("Subscription", "")); assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-03.txt"); - SchemaDirective schemaDirective = new SchemaDirective("format"); - schemaDirective.addLocation(FIELD_DEFINITION.name()); - schemaDirective.addArgument(new SchemaArgument("dateFormat", "String", true, null, STRING)); + SchemaArgument argument = new SchemaArgument("dateFormat", "String", true, null, STRING); + + SchemaDirective schemaDirective = SchemaDirective.builder() + .name("format") + .addLocation(FIELD_DEFINITION.name()) + .addArgument(argument).build(); + schema.addDirective(schemaDirective); assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-04.txt"); @@ -86,8 +90,8 @@ public void testDirectives() { Schema schema = new Schema(); assertThat(schema.getDirectives().size(), is(0)); - SchemaDirective schemaDirective1 = new SchemaDirective("directive1"); - SchemaDirective schemaDirective2 = new SchemaDirective("directive2"); + SchemaDirective schemaDirective1 = SchemaDirective.builder().name("directive1").build(); + SchemaDirective schemaDirective2 = SchemaDirective.builder().name("directive2").build(); schema.addDirective(schemaDirective1); schema.addDirective(schemaDirective2); assertThat(schema.getDirectives().contains(schemaDirective1), is(true)); @@ -130,7 +134,7 @@ public void testEnums() { listString.add("NEWHOPE"); listString.add("JEDI"); listString.add("EMPIRE"); - SchemaEnum schemaEnum1 = new SchemaEnum("Episode"); + SchemaEnum schemaEnum1 = SchemaEnum.builder().name("Episode").build(); schemaEnum1.values().addAll(listString); schema.addEnum(schemaEnum1); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java index b469d819f82..06cca797f69 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java @@ -32,17 +32,43 @@ class SchemaTypeTest { private static final Class INTEGER = Integer.class; @Test - public void testConstructors() { + public void testBuilders() { SchemaType schemaType = new SchemaType("Name", "com.oracle.test.Value"); assertThat(schemaType.name(), is("Name")); assertThat(schemaType.valueClassName(), is("com.oracle.test.Value")); assertThat(schemaType.fieldDefinitions(), is(notNullValue())); - schemaType.addFieldDefinition(new SchemaFieldDefinition("orderId", "Integer", false, true, 0)); - schemaType.addFieldDefinition(new SchemaFieldDefinition("personId", "Integer", false, true, 0)); - schemaType.addFieldDefinition(new SchemaFieldDefinition("personId", "Integer", false, true, 0)); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("orders", "Order", true, true, 0); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + schemaType.addFieldDefinition(SchemaFieldDefinition.builder() + .name("orderId") + .returnType("Integer") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build()); + schemaType.addFieldDefinition(SchemaFieldDefinition.builder() + .name("personId") + .returnType("Integer") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build()); + schemaType.addFieldDefinition(SchemaFieldDefinition.builder() + .name("personId") + .returnType("Integer") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build()); + + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("orders") + .returnType("Order") + .arrayReturnType(true) + .returnTypeMandatory(true) + .arrayLevels(0) + .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .build(); + schemaType.addFieldDefinition(schemaFieldDefinition); assertThat(schemaType.fieldDefinitions().size(), is(4)); @@ -59,7 +85,15 @@ public void testConstructors() { @Test public void testImplementingInterface() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -73,7 +107,15 @@ public void testImplementingInterface() { @Test public void testTypeSchemaOutput() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -88,7 +130,15 @@ public void testTypeSchemaOutput() { @Test public void testTypeSchemaOutputWithDescription() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); schemaType.description("Type Description"); @@ -104,7 +154,15 @@ public void testTypeSchemaOutputWithDescription() { @Test public void testTypeStringOutputWith2Arguments() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -117,7 +175,15 @@ public void testTypeStringOutputWith2Arguments() { @Test public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); schemaArgument1.description("Argument1 Description"); schemaFieldDefinition.addArgument(schemaArgument1); @@ -136,7 +202,15 @@ public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { @Test public void testTypeInterfaceStringOutputWith2Arguments() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, 30, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -151,7 +225,15 @@ public void testTypeInterfaceStringOutputWith2Arguments() { @Test public void testTypeInterfaceStringOutputWith2ArgumentsAndStringDefault() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, "hello", STRING)); schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, 30, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -159,7 +241,8 @@ public void testTypeInterfaceStringOutputWith2ArgumentsAndStringDefault() { assertThat(schemaType.getGraphQLName(), is("interface")); assertThat(schemaType.getSchemaAsString(), is("interface Person {\n" + - "person(\nfilter: String = \"hello\", \nage: Int! = 30\n): Person!\n" + + "person(\nfilter: String = \"hello\", \nage: Int! = 30\n): " + + "Person!\n" + "}\n")); } @@ -167,11 +250,25 @@ public void testTypeInterfaceStringOutputWith2ArgumentsAndStringDefault() { public void testTypeStringOutputWith2Fields() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); - SchemaFieldDefinition schemaFieldDefinition2 = new SchemaFieldDefinition("people", "Person", true, false, 1); + SchemaFieldDefinition schemaFieldDefinition2 = SchemaFieldDefinition.builder() + .name("people") + .returnType("Person") + .arrayReturnType(true) + .returnTypeMandatory(false) + .arrayLevels(1) + .build(); + schemaFieldDefinition2.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); schemaFieldDefinition2.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition2); @@ -186,7 +283,14 @@ public void testTypeStringOutputWith2Fields() { public void testCreatingInputTypeFromType() { SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = new SchemaFieldDefinition("person", "Person", false, true, 0); + SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() + .name("person") + .returnType("Person") + .arrayReturnType(false) + .returnTypeMandatory(true) + .arrayLevels(0) + .build(); + schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); From 20299faa67f7bf1046f442ce4b0e52aab8614d79 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 28 Oct 2020 09:50:06 +0800 Subject: [PATCH 129/178] further work on builders --- .../graphql/server/CustomScalars.java | 1 + .../graphql/server/DefaultContext.java | 12 +- .../graphql/server/ExecutionContext.java | 2 +- .../graphql/server/GraphQLResource.java | 3 +- .../graphql/server/JandexUtils.java | 10 +- .../microprofile/graphql/server/Schema.java | 100 ++- .../graphql/server/SchemaArgument.java | 205 +++++- .../graphql/server/SchemaGenerator.java | 695 ++---------------- .../graphql/server/SchemaGeneratorHelper.java | 589 +++++++++++++++ .../graphql/server/SchemaInputType.java | 9 +- .../graphql/server/SchemaType.java | 132 +++- .../graphql/server/AbstractGraphQLTest.java | 2 +- .../graphql/server/JandexUtilsTest.java | 6 +- .../graphql/server/SchemaArgumentTest.java | 145 +++- .../graphql/server/SchemaDirectiveTest.java | 13 +- .../server/SchemaFieldDefinitionTest.java | 33 +- .../graphql/server/SchemaGeneratorTest.java | 26 +- .../graphql/server/SchemaTest.java | 41 +- .../graphql/server/SchemaTypeTest.java | 101 ++- .../graphql/server/TestHelper.java | 56 ++ .../graphql/server/AbstractGraphQLIT.java | 2 +- .../graphql/server/ExceptionHandlingIT.java | 8 +- 22 files changed, 1363 insertions(+), 828 deletions(-) create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/TestHelper.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java index 19abe34b960..c839d8efd33 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java @@ -392,6 +392,7 @@ public BigDecimalCoercing() { * Number implementation of {@link Coercing} interface for given classes. * @param defines input type */ + @SuppressWarnings("unchecked") public static class NumberCoercing implements Coercing { /** diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java index 76ef820ae27..338d53be0f1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java @@ -26,9 +26,17 @@ public class DefaultContext private ThreadLocal currentThrowable = new ThreadLocal<>(); /** - * Construct a default context. + * Private no-args constructor. */ - public DefaultContext() { + private DefaultContext() { + } + + /** + * Create a new {@link DefaultContext}. + * @return a new {@link DefaultContext} + */ + public static DefaultContext create() { + return new DefaultContext(); } @Override diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index b456949659d..c159f5bda17 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -249,7 +249,7 @@ private void configureExceptionHandling() { * @return a new {@link DefaultContext} */ public static Context getDefaultContext() { - return new DefaultContext(); + return DefaultContext.create(); } /** diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java index 9404c6086f6..01bd475709f 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java @@ -114,7 +114,8 @@ public Response processGraphQLQueryPOST(String body) { @PostConstruct public void init() { try { - context = new ExecutionContext(new DefaultContext()); + // TODO: Need to make this injectable + context = new ExecutionContext(DefaultContext.create()); schemaPrinter = context.getSchemaPrinter(); } catch (Exception e) { ensureRuntimeException(LOGGER, "Unable to build GraphQL Schema: ", e); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java index fb48beddbc6..4f9123db893 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/JandexUtils.java @@ -64,10 +64,18 @@ public class JandexUtils { /** * Construct an instance of the utilities class.. */ - public JandexUtils() { + private JandexUtils() { indexFile = System.getProperty(PROP_INDEX_FILE, DEFAULT_INDEX_FILE); } + /** + * Create a new {@link JandexUtils}. + * @return a new {@link JandexUtils} + */ + public static JandexUtils create() { + return new JandexUtils(); + } + /** * Load all the index files of the given name. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java index 4e6452a01e2..d6e337f1f2d 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/Schema.java @@ -100,28 +100,37 @@ public class Schema implements ElementGenerator { private final List listSchemaEnums; /** - * Construct the DiscoveredSchema using the defaults. - */ - public Schema() { - this(QUERY, MUTATION, SUBSCRIPTION); - } - - /** - * Construct the DiscoveredSchema using the the provided values. + * Construct a {@link Schema}. * - * @param queryName name for the query type - * @param mutationName name for the mutation type - * @param subscriptionName name for the subscription type - */ - public Schema(String queryName, String mutationName, String subscriptionName) { - this.queryName = queryName; - this.mutationName = mutationName; - this.subscriptionName = subscriptionName; + * @param builder the {@link Builder} to construct from + */ + private Schema(Builder builder) { this.listSchemaTypes = new ArrayList<>(); this.listSchemaScalars = new ArrayList<>(); this.listSchemaDirectives = new ArrayList<>(); this.listInputTypes = new ArrayList<>(); this.listSchemaEnums = new ArrayList<>(); + this.queryName = builder.queryName; + this.subscriptionName = builder.subscriptionName; + this.mutationName = builder.mutationName; + } + + /** + * Fluent API builder to create {@link Schema}. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Build a new {@link Schema}. + * + * @return a new {@link Schema} + */ + public static Schema create() { + return builder().build(); } /** @@ -234,7 +243,6 @@ public RuntimeWiring getRuntimeWiring() { builder.scalar(s.graphQLScalarType()); }); - // we should now have the query runtime binding builder.type(typeRuntimeBuilder); @@ -490,4 +498,62 @@ public String getQueryName() { public String getMutationName() { return mutationName; } + + /** + * A fluent API {@link io.helidon.common.Builder} to build instances of {@link Schema}. + */ + public static class Builder implements io.helidon.common.Builder { + + private String queryName; + private String mutationName; + private String subscriptionName; + + /** + * Set the name for the query type. + * + * @param queryName name for the query type + * @return updated builder instance + */ + public Builder queryName(String queryName) { + this.queryName = queryName; + return this; + } + + /** + * Set the name for the mutation type. + * + * @param mutationName name for the query type + * @return updated builder instance + */ + public Builder mutationName(String mutationName) { + this.mutationName = mutationName; + return this; + } + + /** + * Set the name for the subscription type. + * + * @param subscriptionName name for the query type + * @return updated builder instance + */ + public Builder subscriptionName(String subscriptionName) { + this.subscriptionName = subscriptionName; + return this; + } + + @Override + public Schema build() { + if (this.queryName == null) { + this.queryName = QUERY; + } + if (this.mutationName == null) { + this.mutationName = MUTATION; + } + if (this.subscriptionName == null) { + this.subscriptionName = SUBSCRIPTION; + } + return new Schema(this); + } + } + } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index 7a273c85c9e..1b8d51e95e1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -79,21 +79,32 @@ public class SchemaArgument extends AbstractDescriptiveElement implements Elemen private Class originalArrayType; /** - * Construct a {@link SchemaArgument} instance. + * Construct a {@link SchemaArgument}. * - * @param argumentName name of the argument - * @param argumentType type of the argument - * @param isMandatory indicates if the argument is mandatory - * @param defaultValue default value for the argument - * @param originalType original argument type before it was converted to a GraphQL representation. - */ - public SchemaArgument(String argumentName, String argumentType, - boolean isMandatory, Object defaultValue, Class originalType) { - this.argumentName = argumentName; - this.argumentType = argumentType; - this.isMandatory = isMandatory; - this.defaultValue = defaultValue; - this.originalType = originalType; + * @param builder the {@link Builder} to construct from + */ + private SchemaArgument(Builder builder) { + this.argumentName = builder.argumentName; + this.argumentType = builder.argumentType; + this.isMandatory = builder.isMandatory; + this.defaultValue = builder.defaultValue; + this.originalType = builder.originalType; + this.sourceArgument = builder.sourceArgument; + this.format = builder.format; + this.isArrayReturnType = builder.isArrayReturnType; + this.arrayLevels = builder.arrayLevels; + this.isArrayReturnTypeMandatory = builder.isArrayReturnTypeMandatory; + this.originalArrayType = builder.originalArrayType; + description(builder.description); + } + + /** + * Fluent API builder to create {@link SchemaArgument}. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder(); } /** @@ -306,7 +317,6 @@ public Class originalArrayType() { return originalArrayType; } - @Override public String toString() { return "Argument{" @@ -352,4 +362,169 @@ public int hashCode() { return Objects.hash(super.hashCode(), argumentName, argumentType, sourceArgument, isMandatory, defaultValue, description(), originalType, format, originalArrayType); } + + /** + * A fluent API {@link io.helidon.common.Builder} to build instances of {@link SchemaArgument}. + */ + public static class Builder implements io.helidon.common.Builder { + + private String argumentName; + private String argumentType; + private String description; + private boolean isMandatory; + private Object defaultValue; + private Class originalType; + private boolean sourceArgument; + private String[] format; + private boolean isArrayReturnType; + private int arrayLevels; + private boolean isArrayReturnTypeMandatory; + private Class originalArrayType; + + /** + * Set the argument name. + * @param argumentName the argument name + * @return updated builder instance + */ + public Builder argumentName(String argumentName) { + this.argumentName = argumentName; + return this; + } + + /** + * Set the argument type. + * + * @param argumentType the argument type + * @return updated builder instance + */ + public Builder argumentType(String argumentType) { + this.argumentType = argumentType; + return this; + } + + /** + * Set if the argument is mandatory. + * + * @param isMandatory true if the argument is mandatory + * @return updated builder instance + */ + public Builder mandatory(boolean isMandatory) { + this.isMandatory = isMandatory; + return this; + } + + /** + * Set the default value for this argument. + * @param defaultValue the default value for this argument + * @return updated builder instance + */ + public Builder defaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + /** + * Set the description of the {@link SchemaArgument}. + * @param description the description of the {@link SchemaArgument} + * @return updated builder instance + */ + public Builder description(String description) { + this.description = description; + return this; + } + + /** + * Set the original return type. + * + * @param originalType the original return type + * @return updated builder instance + */ + public Builder originalType(Class originalType) { + this.originalType = originalType; + return this; + } + + /** + * Set if the argument is a source argument. + * @param sourceArgument true if the argument is a source argument + * @return updated builder instance + */ + public Builder sourceArgument(boolean sourceArgument) { + this.sourceArgument = sourceArgument; + return this; + } + + /** + * Set the format for a number or date. + * @param format the format for a number or date + * @return updated builder instance + */ + public Builder format(String[] format) { + if (format == null) { + this.format = null; + } else { + this.format = new String[format.length]; + System.arraycopy(format, 0, this.format, 0, this.format.length); + } + + return this; + } + + /** + * Set if the return type is an array type such as a native array([]) or a List, Collection. + * + * @param isArrayReturnType true if the return type is an array type + * @return updated builder instance + */ + public Builder arrayReturnType(boolean isArrayReturnType) { + this.isArrayReturnType = isArrayReturnType; + return this; + } + + /** + * Set the number of array levels if return type is an array. + * + * @param arrayLevels the number of array levels if return type is an array + * @return updated builder instance + */ + public Builder arrayLevels(int arrayLevels) { + this.arrayLevels = arrayLevels; + return this; + } + + /** + * Set if the value of the array is mandatory. + * + * @param isArrayReturnTypeMandatory If the return type is an array then indicates if the value in the + * array is mandatory + * @return updated builder instance + */ + public Builder arrayReturnTypeMandatory(boolean isArrayReturnTypeMandatory) { + this.isArrayReturnTypeMandatory = isArrayReturnTypeMandatory; + return this; + } + + /** + * Set the original array inner type if it is array type. + * @param originalArrayType the original array inner type if it is array type + * @return updated builder instance + */ + public Builder originalArrayType(Class originalArrayType) { + this.originalArrayType = originalArrayType; + return this; + } + + /** + * Build the instance from this builder. + * + * @return instance of the built type + */ + @Override + public SchemaArgument build() { + Objects.requireNonNull(argumentName, "Argument name must be specified"); + Objects.requireNonNull(argumentType, "Argument type must be specified"); + return new SchemaArgument(this); + } + } + } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index b5b4201b2cb..c315e6bd224 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -28,14 +28,12 @@ import java.lang.reflect.ParameterizedType; import java.text.NumberFormat; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.logging.Logger; @@ -66,6 +64,8 @@ import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_OFFSET_DATE_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_ZONED_DATE_TIME_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DiscoveredMethod.MUTATION_TYPE; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DiscoveredMethod.QUERY_TYPE; import static io.helidon.microprofile.graphql.server.FormattingHelper.DATE; import static io.helidon.microprofile.graphql.server.FormattingHelper.NO_FORMATTING; import static io.helidon.microprofile.graphql.server.FormattingHelper.NUMBER; @@ -75,8 +75,6 @@ import static io.helidon.microprofile.graphql.server.FormattingHelper.getCorrectNumberFormat; import static io.helidon.microprofile.graphql.server.FormattingHelper.getFormattingAnnotation; import static io.helidon.microprofile.graphql.server.FormattingHelper.isJsonbAnnotationPresent; -import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.MUTATION_TYPE; -import static io.helidon.microprofile.graphql.server.SchemaGenerator.DiscoveredMethod.QUERY_TYPE; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATETIME_SCALAR; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATETIME_SCALAR; @@ -111,10 +109,10 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isEnumClass; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isGraphQLType; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isPrimitive; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.isValidIDType; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.shouldIgnoreField; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.shouldIgnoreMethod; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.stripMethodName; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.validateIDClass; /** * Various utilities for generating {@link Schema}s from classes. @@ -154,7 +152,7 @@ public class SchemaGenerator { /** * Holds the {@link Set} of additional methods that need to be added to types. */ - private Set setAdditionalMethods = new HashSet<>(); + private Set setAdditionalMethods = new HashSet<>(); /** * A {@link Context} to be passed to execution. @@ -168,7 +166,7 @@ public class SchemaGenerator { */ public SchemaGenerator(Context context) { this.context = context; - jandexUtils = new JandexUtils(); + jandexUtils = JandexUtils.create(); jandexUtils.loadIndexes(); if (!jandexUtils.hasIndex()) { String message = "Unable to find or load jandex index file: " @@ -223,12 +221,13 @@ public Schema generateSchema() throws IntrospectionException, ClassNotFoundExcep */ protected Schema generateSchemaFromClasses(Class... clazzes) throws IntrospectionException, ClassNotFoundException { - Schema schema = new Schema(); + Schema schema = Schema.create(); setUnresolvedTypes.clear(); setAdditionalMethods.clear(); - SchemaType rootQueryType = new SchemaType(schema.getQueryName(), null); - SchemaType rootMutationType = new SchemaType(schema.getMutationName(), null); + + SchemaType rootQueryType = SchemaType.builder().name(schema.getQueryName()).build(); + SchemaType rootMutationType = SchemaType.builder().name(schema.getMutationName()).build(); // process any specific classes with the Input, Type or Interface annotations for (Class clazz : clazzes) { @@ -258,7 +257,9 @@ protected Schema generateSchemaFromClasses(Class... clazzes) // assuming value for annotation overrides @Name String typeName = getTypeName(clazz, true); - SchemaType type = new SchemaType(typeName.isBlank() ? clazz.getSimpleName() : typeName, clazz.getName()); + SchemaType type = SchemaType.builder() + .name(typeName.isBlank() ? clazz.getSimpleName() : typeName) + .valueClassName(clazz.getName()).build(); type.isInterface(clazz.isInterface()); type.description(getDescription(clazz.getAnnotation(Description.class))); @@ -313,9 +314,9 @@ protected Schema generateSchemaFromClasses(Class... clazzes) }); // process any additional methods require via the @Source annotation - for (DiscoveredMethod dm : setAdditionalMethods) { + for (SchemaGeneratorHelper.DiscoveredMethod dm : setAdditionalMethods) { // add the discovered method to the type - SchemaType type = schema.getTypeByClass(dm.source); + SchemaType type = schema.getTypeByClass(dm.source()); if (type != null) { SchemaFieldDefinition fd = newFieldDefinition(dm, null); // add all arguments which are not source arguments @@ -326,7 +327,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) // check for existing DataFetcher fd.dataFetcher(DataFetcherUtils.newMethodDataFetcher( - schema, dm.method.getDeclaringClass(), dm.method, + schema, dm.method().getDeclaringClass(), dm.method(), dm.source(), fd.arguments().toArray(new SchemaArgument[0]))); type.addFieldDefinition(fd); @@ -482,8 +483,8 @@ private void processUnresolvedTypes(Schema schema) { * @param realReturnType the class to generate type from * @param isInputType indicates if the type is an input type and if not the Input annotation will be ignored * @return a {@link SchemaType} - * @throws IntrospectionException - * @throws ClassNotFoundException + * @throws IntrospectionException if any errors with introspection + * @throws ClassNotFoundException if any classes are not found */ private SchemaType generateType(String realReturnType, boolean isInputType) throws IntrospectionException, ClassNotFoundException { @@ -491,12 +492,12 @@ private SchemaType generateType(String realReturnType, boolean isInputType) // if isInputType=false then we ignore the name annotation in case // an annotated input type was also used as a return type String simpleName = getSimpleName(realReturnType, !isInputType); - SchemaType type = new SchemaType(simpleName, realReturnType); + SchemaType type = SchemaType.builder().name(simpleName).valueClassName(realReturnType).build(); type.description(getDescription(Class.forName(realReturnType).getAnnotation(Description.class))); - for (Map.Entry entry : retrieveGetterBeanMethods(Class.forName(realReturnType), isInputType) + for (Map.Entry entry : retrieveGetterBeanMethods(Class.forName(realReturnType), isInputType) .entrySet()) { - DiscoveredMethod discoveredMethod = entry.getValue(); + SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = entry.getValue(); String valueTypeName = discoveredMethod.returnType(); SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod, null); type.addFieldDefinition(fd); @@ -524,8 +525,8 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, Class clazz) throws IntrospectionException, ClassNotFoundException { - for (Map.Entry entry : retrieveAllAnnotatedBeanMethods(clazz).entrySet()) { - DiscoveredMethod discoveredMethod = entry.getValue(); + for (Map.Entry entry : retrieveAllAnnotatedBeanMethods(clazz).entrySet()) { + SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = entry.getValue(); Method method = discoveredMethod.method(); SchemaFieldDefinition fd = null; @@ -547,7 +548,7 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, } } - SchemaType schemaType = discoveredMethod.methodType == QUERY_TYPE + SchemaType schemaType = discoveredMethod.methodType() == QUERY_TYPE ? rootQueryType : rootMutationType; @@ -780,23 +781,23 @@ private SchemaEnum generateEnum(Class clazz) { /** * Return a new {@link SchemaFieldDefinition} with the given field and class. * - * @param discoveredMethod the {@link DiscoveredMethod} + * @param discoveredMethod the {@link SchemaGeneratorHelper.DiscoveredMethod} * @param optionalName optional name for the field definition * @return a {@link SchemaFieldDefinition} */ @SuppressWarnings("rawtypes") - private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMethod, String optionalName) { + private SchemaFieldDefinition newFieldDefinition(SchemaGeneratorHelper.DiscoveredMethod discoveredMethod, String optionalName) { String valueClassName = discoveredMethod.returnType(); String graphQLType = getGraphQLType(valueClassName); DataFetcher dataFetcher = null; String propertyName = discoveredMethod.propertyName(); String name = discoveredMethod.name(); - boolean isArrayReturnType = discoveredMethod.isArrayReturnType || discoveredMethod.isCollectionType() || discoveredMethod - .isMap(); + boolean isArrayReturnType = discoveredMethod.isArrayReturnType() || discoveredMethod.isCollectionType() + || discoveredMethod.isMap(); if (isArrayReturnType) { - if (discoveredMethod.isMap) { + if (discoveredMethod.isMap()) { // add DataFetcher that will just retrieve the values() from the Map. // The microprofile-graphql spec does not specify how Maps are treated // and leaves this up to the individual implementation. This implementation @@ -828,7 +829,7 @@ private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMeth SchemaFieldDefinition fd = SchemaFieldDefinition.builder() .name(optionalName != null ? optionalName - : discoveredMethod.name) + : discoveredMethod.name()) .returnType(graphQLType) .arrayReturnType(isArrayReturnType) .returnTypeMandatory(discoveredMethod.isReturnTypeMandatory()) @@ -911,9 +912,9 @@ private void updateLongTypes(Schema schema, String longReturnType, String shortR * @throws IntrospectionException if any errors with introspection * @throws ClassNotFoundException if any classes are not found */ - protected Map retrieveAllAnnotatedBeanMethods(Class clazz) + protected Map retrieveAllAnnotatedBeanMethods(Class clazz) throws IntrospectionException, ClassNotFoundException { - Map mapDiscoveredMethods = new HashMap<>(); + Map mapDiscoveredMethods = new HashMap<>(); for (Method m : getAllMethods(clazz)) { boolean isQuery = m.getAnnotation(Query.class) != null; boolean isMutation = m.getAnnotation(Mutation.class) != null; @@ -923,7 +924,7 @@ protected Map retrieveAllAnnotatedBeanMethods(Class + " may not have both a Query and Mutation annotation"); } if (isQuery || isMutation || hasSourceAnnotation) { - DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null, false, true); + SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null, false, true); discoveredMethod.methodType(isQuery || hasSourceAnnotation ? QUERY_TYPE : MUTATION_TYPE); String name = discoveredMethod.name(); if (mapDiscoveredMethods.containsKey(name)) { @@ -945,10 +946,10 @@ protected Map retrieveAllAnnotatedBeanMethods(Class * @return a {@link Map} of the methods and return types * @throws IntrospectionException if there were errors introspecting classes */ - protected Map retrieveGetterBeanMethods(Class clazz, - boolean isInputType) + protected Map retrieveGetterBeanMethods(Class clazz, + boolean isInputType) throws IntrospectionException, ClassNotFoundException { - Map mapDiscoveredMethods = new HashMap<>(); + Map mapDiscoveredMethods = new HashMap<>(); for (Method m : getAllMethods(clazz)) { if (m.getName().equals("getClass") || shouldIgnoreMethod(m, isInputType)) { @@ -967,8 +968,8 @@ protected Map retrieveGetterBeanMethods(Class clazz // only include if the field should not be ignored if (!shouldIgnoreField(clazz, propertyDescriptor.getName()) && !ignoreWriteMethod) { // this is a getter method, include it here - DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, propertyDescriptor, isInputType, - false); + SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, propertyDescriptor, isInputType, + false); mapDiscoveredMethods.put(discoveredMethod.name(), discoveredMethod); } } @@ -992,7 +993,7 @@ protected List getAllMethods(Class clazz) throws IntrospectionExcepti } /** - * Generate a {@link DiscoveredMethod} from the given arguments. + * Generate a {@link SchemaGeneratorHelper.DiscoveredMethod} from the given arguments. * * @param method {@link Method} being introspected * @param clazz {@link Class} being introspected @@ -1000,11 +1001,11 @@ protected List getAllMethods(Class clazz) throws IntrospectionExcepti * methods as in the case for a {@link Query} annotation) * @param isInputType indicates if the method is part of an input type * @param isQueryOrMutation indicates if this is for a query or mutation - * @return a {@link DiscoveredMethod} + * @return a {@link SchemaGeneratorHelper.DiscoveredMethod} */ - private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, - PropertyDescriptor pd, boolean isInputType, - boolean isQueryOrMutation) throws ClassNotFoundException { + private SchemaGeneratorHelper.DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, + PropertyDescriptor pd, boolean isInputType, + boolean isQueryOrMutation) throws ClassNotFoundException { String[] format = new String[0]; String description = null; boolean isReturnTypeMandatory = false; @@ -1145,7 +1146,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, format = methodFormat; } - DiscoveredMethod discoveredMethod = new DiscoveredMethod(); + SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = new SchemaGeneratorHelper.DiscoveredMethod(); discoveredMethod.name(varName); discoveredMethod.method(method); discoveredMethod.format(format); @@ -1184,8 +1185,8 @@ private void ensureNonVoidQueryOrMutation(String returnClazzName, Method method, } /** - * Process the {@link ReturnType} and update {@link DiscoveredMethod} as required. - * @param discoveredMethod {@link DiscoveredMethod} + * Process the {@link ReturnType} and update {@link SchemaGeneratorHelper.DiscoveredMethod} as required. + * @param discoveredMethod {@link SchemaGeneratorHelper.DiscoveredMethod} * @param realReturnType {@link ReturnType} with details of the return types * @param returnClazzName return class name * @param isInputType indicates if the method is part of an input type @@ -1194,7 +1195,7 @@ private void ensureNonVoidQueryOrMutation(String returnClazzName, Method method, * * @throws ClassNotFoundException if any class not found */ - private void processReturnType(DiscoveredMethod discoveredMethod, ReturnType realReturnType, + private void processReturnType(SchemaGeneratorHelper.DiscoveredMethod discoveredMethod, ReturnType realReturnType, String returnClazzName, boolean isInputType, String varName, Method method) throws ClassNotFoundException { if (realReturnType.returnClass() != null && !ID.equals(returnClazzName)) { @@ -1205,7 +1206,7 @@ private void processReturnType(DiscoveredMethod discoveredMethod, ReturnType rea if (dateScalar != null && isDateTimeScalar(dateScalar.name())) { // only set the original array type if it's a date/time discoveredMethod.originalArrayType(Class.forName(realReturnType.returnClass)); - } else if (discoveredMethod.isArrayReturnType) { + } else if (discoveredMethod.isArrayReturnType()) { Class originalArrayType = getSafeClass(realReturnType.returnClass); if (originalArrayType != null) { discoveredMethod.originalArrayType(originalArrayType); @@ -1229,10 +1230,10 @@ private void processReturnType(DiscoveredMethod discoveredMethod, ReturnType rea * Process parameters for the given method. * * @param method {@link Method} to process - * @param discoveredMethod {@link DiscoveredMethod} to update + * @param discoveredMethod {@link SchemaGeneratorHelper.DiscoveredMethod} to update * @param annotatedName annotated name or null */ - private void processMethodParameters(Method method, DiscoveredMethod discoveredMethod, String annotatedName) { + private void processMethodParameters(Method method, SchemaGeneratorHelper.DiscoveredMethod discoveredMethod, String annotatedName) { Parameter[] parameters = method.getParameters(); if (parameters != null && parameters.length > 0) { java.lang.reflect.Type[] genericParameterTypes = method.getGenericParameterTypes(); @@ -1260,11 +1261,14 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM boolean isMandatory = (isPrimitive(paramType) && argumentDefaultValue == null) || (parameter.getAnnotation(NonNull.class) != null && argumentDefaultValue == null); - SchemaArgument argument = - new SchemaArgument(parameterName, returnType.returnClass(), - isMandatory, argumentDefaultValue, paramType); - String argumentDescription = getDescription(parameter.getAnnotation(Description.class)); - argument.description(argumentDescription); + SchemaArgument argument = SchemaArgument.builder() + .argumentName(parameterName) + .argumentType(returnType.returnClass()) + .mandatory(isMandatory) + .defaultValue(argumentDefaultValue) + .originalType(paramType) + .description(getDescription(parameter.getAnnotation(Description.class))) + .build(); String[] argumentFormat = getFormattingAnnotation(parameter); String[] argumentTypeFormat = FormattingHelper.getMethodParameterFormat(parameter, 0); @@ -1309,30 +1313,6 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM } } - /** - * Validate that a {@link Class} annotated with ID is a valid type. - * - * @param returnClazz {@link Class} to check - */ - private static void validateIDClass(Class returnClazz) { - if (!isValidIDType(returnClazz)) { - ensureConfigurationException(LOGGER, "A class of type " + returnClazz + " is not allowed to be an @Id"); - } - } - - /** - * Validate that a class annotated with ID is a valid type. - * - * @param returnClazz class to check - */ - private static void validateIDClass(String returnClazz) { - try { - validateIDClass(Class.forName(returnClazz)); - } catch (ClassNotFoundException e) { - // ignore - } - } - /** * Return the {@link ReturnType} for this return class and method. * @@ -1441,567 +1421,6 @@ protected JandexUtils getJandexUtils() { return jandexUtils; } - /** - * Defines discovered methods for a class. - */ - public static class DiscoveredMethod { - - /** - * Indicates query Type. - */ - public static final int QUERY_TYPE = 0; - - /** - * Indicates write method. - */ - public static final int MUTATION_TYPE = 1; - - /** - * Name of the discovered method. - */ - private String name; - - /** - * Return type of the method. - */ - private String returnType; - - /** - * type of method. - */ - private int methodType; - - /** - * If the return type is a {@link Collection} then this is the type of {@link Collection} and the returnType will be - * return type for the collection. - */ - private String collectionType; - - /** - * Indicates if the return type is an array. - */ - private boolean isArrayReturnType; - - /** - * Indicates if the return type is a {@link Map}. Note: In the 1.0.1 microprofile spec the behaviour of {@link Map} is - * undefined. - */ - private boolean isMap; - - /** - * The {@link List} of {@link SchemaArgument}s for this method. - */ - private List listArguments = new ArrayList<>(); - - /** - * The actual method. - */ - private Method method; - - /** - * Number of levels in the Array. - */ - private int arrayLevels = 0; - - /** - * The source on which the method should be added. - */ - private String source; - - /** - * The property name if the method is a getter. - */ - private String propertyName; - - /** - * Indicates if the method containing the {@link Source} annotation was also annotated with the {@link Query} annotation. - * If true, then this indicates that a top level query should also be created as well as the field in the type. - */ - private boolean isQueryAnnotated = false; - - /** - * Defines the format for a number or date. - */ - private String[] format = new String[0]; - - /** - * A description for a method. - */ - private String description; - - /** - * Indicates id the return type is mandatory. - */ - private boolean isReturnTypeMandatory; - - /** - * The default value for this discovered method. - */ - private Object defaultValue; - - /** - * If the return type is an array then indicates if the value in the array is mandatory. - */ - private boolean isArrayReturnTypeMandatory; - - /** - * Original array inner type if it is array type. - */ - private Class originalArrayType; - - /** - * Indicates if the format is of type Jsonb. - */ - private boolean isJsonbFormat; - - /** - * Indicates if the property name is of type Jsonb. - */ - private boolean isJsonbProperty; - - /** - * Default constructor. - */ - public DiscoveredMethod() { - } - - /** - * Return the name. - * - * @return the name - */ - public String name() { - return name; - } - - /** - * Set the name. - * - * @param name the name - */ - public void name(String name) { - this.name = name; - } - - /** - * Return the return type. - * - * @return the return type - */ - public String returnType() { - return returnType; - } - - /** - * Set the return type. - * - * @param returnType the return type - */ - public void returnType(String returnType) { - this.returnType = returnType; - } - - /** - * Return the collection type. - * - * @return the collection - */ - public String collectionType() { - return collectionType; - } - - /** - * Set the collection type. - * - * @param collectionType the collection type - */ - public void collectionType(String collectionType) { - this.collectionType = collectionType; - } - - /** - * Sets if the property has a JsonbProperty annotation. - * - * @param isJsonbProperty if the property has a JsonbProperty annotation - */ - public void jsonbProperty(boolean isJsonbProperty) { - this.isJsonbProperty = isJsonbProperty; - } - - /** - * Indicates if the property has a JsonbProperty annotation. - * - * @return true if the property has a JsonbProperty annotation - */ - public boolean isJsonbProperty() { - return isJsonbProperty; - } - - /** - * Indicates if the method is a map return type. - * - * @return if the method is a map return type - */ - public boolean isMap() { - return isMap; - } - - /** - * Set if the method is a map return type. - * - * @param map if the method is a map return type - */ - public void map(boolean map) { - isMap = map; - } - - /** - * Return the method type. - * - * @return the method type - */ - public int methodType() { - return methodType; - } - - /** - * Set the method type either READ or WRITE. - * - * @param methodType the method type - */ - public void methodType(int methodType) { - this.methodType = methodType; - } - - /** - * Indicates if the method returns an array. - * - * @return if the method returns an array. - */ - public boolean isArrayReturnType() { - return isArrayReturnType; - } - - /** - * Indicates if the method returns an array. - * - * @param arrayReturnType if the method returns an array - */ - public void arrayReturnType(boolean arrayReturnType) { - isArrayReturnType = arrayReturnType; - } - - /** - * Indicates if the method is a collection type. - * - * @return if the method is a collection type - */ - public boolean isCollectionType() { - return collectionType != null; - } - - /** - * Returns the {@link Method}. - * - * @return the {@link Method} - */ - public Method method() { - return method; - } - - /** - * Sets the {@link Method}. - * - * @param method the {@link Method} - */ - public void method(Method method) { - this.method = method; - } - - /** - * Return the {@link List} of {@link SchemaArgument}s. - * - * @return the {@link List} of {@link SchemaArgument} - */ - public List arguments() { - return this.listArguments; - } - - /** - * Return the number of levels in the Array. - * - * @return Return the number of levels in the Array - */ - public int arrayLevels() { - return arrayLevels; - } - - /** - * Sets the number of levels in the Array. - * - * @param arrayLevels the number of levels in the Array - */ - public void arrayLevels(int arrayLevels) { - this.arrayLevels = arrayLevels; - } - - /** - * Add a {@link SchemaArgument}. - * - * @param argument a {@link SchemaArgument} - */ - public void addArgument(SchemaArgument argument) { - listArguments.add(argument); - } - - /** - * Return the source on which the method should be added. - * - * @return source on which the method should be added - */ - public String source() { - return source; - } - - /** - * Set the source on which the method should be added. - * - * @param source source on which the method should be added - */ - public void source(String source) { - this.source = source; - } - - /** - * Indicates if the method containing the {@link Source} annotation was also annotated with the {@link Query} annotation. - * - * @return true if the {@link Query} annotation was present - */ - public boolean isQueryAnnotated() { - return isQueryAnnotated; - } - - /** - * Set if the method containing the {@link Source} annotation was * also annotated with the {@link Query} annotation. - * - * @param queryAnnotated true if the {@link Query} annotation was present - */ - public void queryAnnotated(boolean queryAnnotated) { - isQueryAnnotated = queryAnnotated; - } - - /** - * Return the format for a number or date. - * - * @return the format for a number or date - */ - public String[] format() { - if (format == null) { - return null; - } - String[] copy = new String[format.length]; - System.arraycopy(format, 0, copy, 0, copy.length); - return copy; - } - - /** - * Set the format for a number or date. - * - * @param format the format for a number or date - */ - public void format(String[] format) { - if (format == null) { - this.format = null; - } else { - this.format = new String[format.length]; - System.arraycopy(format, 0, this.format, 0, this.format.length); - } - } - - /** - * Return the property name if the method is a getter. - * - * @return property name if the method is a getter - */ - public String propertyName() { - return propertyName; - } - - /** - * Set the property name if the method is a getter. - * - * @param propertyName property name - */ - public void propertyName(String propertyName) { - this.propertyName = propertyName; - } - - /** - * Return the description for a method. - * - * @return the description for a method - */ - public String description() { - return description; - } - - /** - * Set the description for a method. - * - * @param description the description for a method - */ - public void description(String description) { - this.description = description; - } - - /** - * Indicates if the return type is mandatory. - * - * @return if the return type is mandatory - */ - public boolean isReturnTypeMandatory() { - return isReturnTypeMandatory; - } - - /** - * Set if the return type is mandatory. - * - * @param returnTypeMandatory if the return type is mandatory - */ - public void returnTypeMandatory(boolean returnTypeMandatory) { - isReturnTypeMandatory = returnTypeMandatory; - } - - /** - * Return the default value for this method. - * - * @return the default value for this method - */ - public Object defaultValue() { - return defaultValue; - } - - /** - * Set the default value for this method. - * - * @param defaultValue the default value for this method - */ - public void defaultValue(Object defaultValue) { - this.defaultValue = defaultValue; - } - - /** - * Return if the array return type is mandatory. - * - * @return if the array return type is mandatory - */ - public boolean isArrayReturnTypeMandatory() { - return isArrayReturnTypeMandatory; - } - - /** - * Sets if the array return type is mandatory. - * - * @param arrayReturnTypeMandatory if the array return type is mandatory - */ - public void arrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { - isArrayReturnTypeMandatory = arrayReturnTypeMandatory; - } - - /** - * Sets the original array type. - * - * @param originalArrayType the original array type - */ - public void originalArrayType(Class originalArrayType) { - this.originalArrayType = originalArrayType; - } - - /** - * Returns the original array type. - * - * @return the original array type - */ - public Class originalArrayType() { - return originalArrayType; - } - - /** - * Set if the format is of type JsonB. - * - * @param isJsonbFormat if the format is of type JsonB - */ - public void jsonbFormat(boolean isJsonbFormat) { - this.isJsonbFormat = isJsonbFormat; - } - - /** - * Returns true if the format is of type JsonB. - * - * @return true if the format is of type JsonB - */ - public boolean isJsonbFormat() { - return isJsonbFormat; - } - - @Override - public String toString() { - return "DiscoveredMethod{" - + "name='" + name + '\'' - + ", returnType='" + returnType + '\'' - + ", methodType=" + methodType - + ", collectionType='" + collectionType + '\'' - + ", isArrayReturnType=" + isArrayReturnType - + ", isMap=" + isMap - + ", listArguments=" + listArguments - + ", arrayLevels=" + arrayLevels - + ", source=" + source - + ", isQueryAnnotated=" + isQueryAnnotated - + ", isReturnTypeMandatory=" + isReturnTypeMandatory - + ", isArrayReturnTypeMandatory=" + isArrayReturnTypeMandatory - + ", description=" + description - + ", originalArrayType=" + originalArrayType - + ", defaultValue=" + defaultValue - + ", isJsonbFormat=" + isJsonbFormat - + ", isJsonbProperty=" + isJsonbProperty - + ", method=" + method + '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - DiscoveredMethod that = (DiscoveredMethod) o; - return methodType == that.methodType - && isArrayReturnType == that.isArrayReturnType - && isMap == that.isMap - && arrayLevels == that.arrayLevels - && Objects.equals(name, that.name) - && Objects.equals(returnType, that.returnType) - && Objects.equals(source, that.source) - && Objects.equals(isQueryAnnotated, that.isQueryAnnotated) - && Objects.equals(method, that.method) - && Objects.equals(description, that.description) - && Objects.equals(isReturnTypeMandatory, that.isReturnTypeMandatory) - && Objects.equals(isArrayReturnTypeMandatory, that.isArrayReturnTypeMandatory) - && Objects.equals(defaultValue, that.defaultValue) - && Objects.equals(isJsonbFormat, that.isJsonbFormat) - && Objects.equals(collectionType, that.collectionType); - } - - @Override - public int hashCode() { - return Objects.hash(name, returnType, methodType, method, arrayLevels, isQueryAnnotated, - collectionType, isArrayReturnType, isMap, source, description, - isReturnTypeMandatory, defaultValue, isArrayReturnTypeMandatory, isJsonbFormat, - isJsonbProperty); - } - } - /** * Defines return types for methods or parameters. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 7c191ab9d4f..8aeeccfc8e1 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -35,9 +35,11 @@ import java.time.OffsetTime; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.logging.Logger; import javax.json.bind.annotation.JsonbProperty; @@ -54,6 +56,7 @@ import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Source; import org.eclipse.microprofile.graphql.Type; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_BIGDECIMAL_SCALAR; @@ -1088,6 +1091,7 @@ protected static void ensureConfigurationException(Logger logger, String message /** * Returns the stacktrace of the given {@link Throwable}. + * * @param throwable {@link Throwable} to get stack tracek for * @return the stacktrace of the given {@link Throwable} */ @@ -1096,4 +1100,589 @@ protected static String getStackTrace(Throwable throwable) { throwable.printStackTrace(new PrintWriter(stack)); return stack.toString(); } + + /** + * Validate that a {@link Class} annotated with ID is a valid type. + * + * @param returnClazz {@link Class} to check + */ + protected static void validateIDClass(Class returnClazz) { + if (!isValidIDType(returnClazz)) { + ensureConfigurationException(LOGGER, "A class of type " + returnClazz + " is not allowed to be an @Id"); + } + } + + /** + * Validate that a class annotated with ID is a valid type. + * + * @param returnClazz class to check + */ + protected static void validateIDClass(String returnClazz) { + try { + validateIDClass(Class.forName(returnClazz)); + } catch (ClassNotFoundException e) { + // ignore + } + } + + /** + * Defines discovered methods for a class. + */ + public static class DiscoveredMethod { + + /** + * Indicates query Type. + */ + public static final int QUERY_TYPE = 0; + + /** + * Indicates write method. + */ + public static final int MUTATION_TYPE = 1; + + /** + * Name of the discovered method. + */ + private String name; + + /** + * Return type of the method. + */ + private String returnType; + + /** + * type of method. + */ + private int methodType; + + /** + * If the return type is a {@link Collection} then this is the type of {@link Collection} and the returnType will be + * return type for the collection. + */ + private String collectionType; + + /** + * Indicates if the return type is an array. + */ + private boolean isArrayReturnType; + + /** + * Indicates if the return type is a {@link Map}. Note: In the 1.0.1 microprofile spec the behaviour of {@link Map} is + * undefined. + */ + private boolean isMap; + + /** + * The {@link List} of {@link SchemaArgument}s for this method. + */ + private List listArguments = new ArrayList<>(); + + /** + * The actual method. + */ + private Method method; + + /** + * Number of levels in the Array. + */ + private int arrayLevels = 0; + + /** + * The source on which the method should be added. + */ + private String source; + + /** + * The property name if the method is a getter. + */ + private String propertyName; + + /** + * Indicates if the method containing the {@link Source} annotation was also annotated with the {@link Query} annotation. + * If true, then this indicates that a top level query should also be created as well as the field in the type. + */ + private boolean isQueryAnnotated = false; + + /** + * Defines the format for a number or date. + */ + private String[] format = new String[0]; + + /** + * A description for a method. + */ + private String description; + + /** + * Indicates id the return type is mandatory. + */ + private boolean isReturnTypeMandatory; + + /** + * The default value for this discovered method. + */ + private Object defaultValue; + + /** + * If the return type is an array then indicates if the value in the array is mandatory. + */ + private boolean isArrayReturnTypeMandatory; + + /** + * Original array inner type if it is array type. + */ + private Class originalArrayType; + + /** + * Indicates if the format is of type Jsonb. + */ + private boolean isJsonbFormat; + + /** + * Indicates if the property name is of type Jsonb. + */ + private boolean isJsonbProperty; + + /** + * Default constructor. + */ + public DiscoveredMethod() { + } + + /** + * Return the name. + * + * @return the name + */ + public String name() { + return name; + } + + /** + * Set the name. + * + * @param name the name + */ + public void name(String name) { + this.name = name; + } + + /** + * Return the return type. + * + * @return the return type + */ + public String returnType() { + return returnType; + } + + /** + * Set the return type. + * + * @param returnType the return type + */ + public void returnType(String returnType) { + this.returnType = returnType; + } + + /** + * Return the collection type. + * + * @return the collection + */ + public String collectionType() { + return collectionType; + } + + /** + * Set the collection type. + * + * @param collectionType the collection type + */ + public void collectionType(String collectionType) { + this.collectionType = collectionType; + } + + /** + * Sets if the property has a JsonbProperty annotation. + * + * @param isJsonbProperty if the property has a JsonbProperty annotation + */ + public void jsonbProperty(boolean isJsonbProperty) { + this.isJsonbProperty = isJsonbProperty; + } + + /** + * Indicates if the property has a JsonbProperty annotation. + * + * @return true if the property has a JsonbProperty annotation + */ + public boolean isJsonbProperty() { + return isJsonbProperty; + } + + /** + * Indicates if the method is a map return type. + * + * @return if the method is a map return type + */ + public boolean isMap() { + return isMap; + } + + /** + * Set if the method is a map return type. + * + * @param map if the method is a map return type + */ + public void map(boolean map) { + isMap = map; + } + + /** + * Return the method type. + * + * @return the method type + */ + public int methodType() { + return methodType; + } + + /** + * Set the method type either READ or WRITE. + * + * @param methodType the method type + */ + public void methodType(int methodType) { + this.methodType = methodType; + } + + /** + * Indicates if the method returns an array. + * + * @return if the method returns an array. + */ + public boolean isArrayReturnType() { + return isArrayReturnType; + } + + /** + * Indicates if the method returns an array. + * + * @param arrayReturnType if the method returns an array + */ + public void arrayReturnType(boolean arrayReturnType) { + isArrayReturnType = arrayReturnType; + } + + /** + * Indicates if the method is a collection type. + * + * @return if the method is a collection type + */ + public boolean isCollectionType() { + return collectionType != null; + } + + /** + * Returns the {@link Method}. + * + * @return the {@link Method} + */ + public Method method() { + return method; + } + + /** + * Sets the {@link Method}. + * + * @param method the {@link Method} + */ + public void method(Method method) { + this.method = method; + } + + /** + * Return the {@link List} of {@link SchemaArgument}s. + * + * @return the {@link List} of {@link SchemaArgument} + */ + public List arguments() { + return this.listArguments; + } + + /** + * Return the number of levels in the Array. + * + * @return Return the number of levels in the Array + */ + public int arrayLevels() { + return arrayLevels; + } + + /** + * Sets the number of levels in the Array. + * + * @param arrayLevels the number of levels in the Array + */ + public void arrayLevels(int arrayLevels) { + this.arrayLevels = arrayLevels; + } + + /** + * Add a {@link SchemaArgument}. + * + * @param argument a {@link SchemaArgument} + */ + public void addArgument(SchemaArgument argument) { + listArguments.add(argument); + } + + /** + * Return the source on which the method should be added. + * + * @return source on which the method should be added + */ + public String source() { + return source; + } + + /** + * Set the source on which the method should be added. + * + * @param source source on which the method should be added + */ + public void source(String source) { + this.source = source; + } + + /** + * Indicates if the method containing the {@link Source} annotation was also annotated with the {@link Query} annotation. + * + * @return true if the {@link Query} annotation was present + */ + public boolean isQueryAnnotated() { + return isQueryAnnotated; + } + + /** + * Set if the method containing the {@link Source} annotation was * also annotated with the {@link Query} annotation. + * + * @param queryAnnotated true if the {@link Query} annotation was present + */ + public void queryAnnotated(boolean queryAnnotated) { + isQueryAnnotated = queryAnnotated; + } + + /** + * Return the format for a number or date. + * + * @return the format for a number or date + */ + public String[] format() { + if (format == null) { + return null; + } + String[] copy = new String[format.length]; + System.arraycopy(format, 0, copy, 0, copy.length); + return copy; + } + + /** + * Set the format for a number or date. + * + * @param format the format for a number or date + */ + public void format(String[] format) { + if (format == null) { + this.format = null; + } else { + this.format = new String[format.length]; + System.arraycopy(format, 0, this.format, 0, this.format.length); + } + } + + /** + * Return the property name if the method is a getter. + * + * @return property name if the method is a getter + */ + public String propertyName() { + return propertyName; + } + + /** + * Set the property name if the method is a getter. + * + * @param propertyName property name + */ + public void propertyName(String propertyName) { + this.propertyName = propertyName; + } + + /** + * Return the description for a method. + * + * @return the description for a method + */ + public String description() { + return description; + } + + /** + * Set the description for a method. + * + * @param description the description for a method + */ + public void description(String description) { + this.description = description; + } + + /** + * Indicates if the return type is mandatory. + * + * @return if the return type is mandatory + */ + public boolean isReturnTypeMandatory() { + return isReturnTypeMandatory; + } + + /** + * Set if the return type is mandatory. + * + * @param returnTypeMandatory if the return type is mandatory + */ + public void returnTypeMandatory(boolean returnTypeMandatory) { + isReturnTypeMandatory = returnTypeMandatory; + } + + /** + * Return the default value for this method. + * + * @return the default value for this method + */ + public Object defaultValue() { + return defaultValue; + } + + /** + * Set the default value for this method. + * + * @param defaultValue the default value for this method + */ + public void defaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + } + + /** + * Return if the array return type is mandatory. + * + * @return if the array return type is mandatory + */ + public boolean isArrayReturnTypeMandatory() { + return isArrayReturnTypeMandatory; + } + + /** + * Sets if the array return type is mandatory. + * + * @param arrayReturnTypeMandatory if the array return type is mandatory + */ + public void arrayReturnTypeMandatory(boolean arrayReturnTypeMandatory) { + isArrayReturnTypeMandatory = arrayReturnTypeMandatory; + } + + /** + * Sets the original array type. + * + * @param originalArrayType the original array type + */ + public void originalArrayType(Class originalArrayType) { + this.originalArrayType = originalArrayType; + } + + /** + * Returns the original array type. + * + * @return the original array type + */ + public Class originalArrayType() { + return originalArrayType; + } + + /** + * Set if the format is of type JsonB. + * + * @param isJsonbFormat if the format is of type JsonB + */ + public void jsonbFormat(boolean isJsonbFormat) { + this.isJsonbFormat = isJsonbFormat; + } + + /** + * Returns true if the format is of type JsonB. + * + * @return true if the format is of type JsonB + */ + public boolean isJsonbFormat() { + return isJsonbFormat; + } + + @Override + public String toString() { + return "DiscoveredMethod{" + + "name='" + name + '\'' + + ", returnType='" + returnType + '\'' + + ", methodType=" + methodType + + ", collectionType='" + collectionType + '\'' + + ", isArrayReturnType=" + isArrayReturnType + + ", isMap=" + isMap + + ", listArguments=" + listArguments + + ", arrayLevels=" + arrayLevels + + ", source=" + source + + ", isQueryAnnotated=" + isQueryAnnotated + + ", isReturnTypeMandatory=" + isReturnTypeMandatory + + ", isArrayReturnTypeMandatory=" + isArrayReturnTypeMandatory + + ", description=" + description + + ", originalArrayType=" + originalArrayType + + ", defaultValue=" + defaultValue + + ", isJsonbFormat=" + isJsonbFormat + + ", isJsonbProperty=" + isJsonbProperty + + ", method=" + method + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DiscoveredMethod that = (DiscoveredMethod) o; + return methodType == that.methodType + && isArrayReturnType == that.isArrayReturnType + && isMap == that.isMap + && arrayLevels == that.arrayLevels + && Objects.equals(name, that.name) + && Objects.equals(returnType, that.returnType) + && Objects.equals(source, that.source) + && Objects.equals(isQueryAnnotated, that.isQueryAnnotated) + && Objects.equals(method, that.method) + && Objects.equals(description, that.description) + && Objects.equals(isReturnTypeMandatory, that.isReturnTypeMandatory) + && Objects.equals(isArrayReturnTypeMandatory, that.isArrayReturnTypeMandatory) + && Objects.equals(defaultValue, that.defaultValue) + && Objects.equals(isJsonbFormat, that.isJsonbFormat) + && Objects.equals(collectionType, that.collectionType); + } + + @Override + public int hashCode() { + return Objects.hash(name, returnType, methodType, method, arrayLevels, isQueryAnnotated, + collectionType, isArrayReturnType, isMap, source, description, + isReturnTypeMandatory, defaultValue, isArrayReturnTypeMandatory, isJsonbFormat, + isJsonbProperty); + } + } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java index 27003121ed9..8646297b4ee 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaInputType.java @@ -22,12 +22,11 @@ public class SchemaInputType extends SchemaType { /** - * Construct an {@link SchemaInputType}. - * - * @param name name for the input type - * @param valueClassName fully qualified value name + * Private no-args constructor. + * @param name name of the type + * @param valueClassName value class name */ - public SchemaInputType(String name, String valueClassName) { + protected SchemaInputType(String name, String valueClassName) { super(name, valueClassName); } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java index 47a542cb0e9..a4b33eae747 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaType.java @@ -33,7 +33,7 @@ public class SchemaType extends AbstractDescriptiveElement implements ElementGen /** * Value class name. */ - private final String valueClassName; + private String valueClassName; /** * Indicates if the {@link SchemaType} is an interface. @@ -48,20 +48,42 @@ public class SchemaType extends AbstractDescriptiveElement implements ElementGen /** * {@link List} of {@link SchemaFieldDefinition}. */ - private final List listSchemaFieldDefinitions; + private List listSchemaFieldDefinitions; /** - * Construct a @{link Type} with the given arguments. - * - * @param name name of the Type - * @param valueClassName value class name + * Private no-args constructor only use by subclass {@link SchemaInputType}. + * @param name name of the type + * @param valueClassName value class name */ - public SchemaType(String name, String valueClassName) { + protected SchemaType(String name, String valueClassName) { this.name = name; this.valueClassName = valueClassName; this.listSchemaFieldDefinitions = new ArrayList<>(); } + /** + * Construct a {@link SchemaType}. + * + * @param builder the {@link Builder} to construct from + */ + private SchemaType(Builder builder) { + this.name = builder.name; + this.valueClassName = builder.valueClassName; + this.isInterface = builder.isInterface; + this.implementingInterface = builder.implementingInterface; + this.listSchemaFieldDefinitions = builder.listSchemaFieldDefinitions; + description(builder.description); + } + + /** + * Fluent API builder to create {@link SchemaType}. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder(); + } + /** * Return the GraphQL schema representation of the element. * @@ -75,7 +97,7 @@ public String getSchemaAsString() { .append(name()); if (implementingInterface != null) { - sb.append(" implements " + implementingInterface); + sb.append(" implements ").append(implementingInterface); } sb.append(SPACER).append(OPEN_CURLY).append(NEWLINE); @@ -121,6 +143,7 @@ public String name() { /** * Set the name of the {@link SchemaType}. + * * @param name the name of the {@link SchemaType} */ public void name(String name) { @@ -215,13 +238,13 @@ public boolean equals(Object o) { @Override public int hashCode() { - int result = Objects.hash(name, - valueClassName, - isInterface, - implementingInterface, - description(), - listSchemaFieldDefinitions); - return result; + return Objects.hash(name, + valueClassName, + isInterface, + implementingInterface, + description(), + listSchemaFieldDefinitions); + } @Override @@ -253,4 +276,83 @@ protected String getGraphQLName() { return isInterface() ? "interface" : "type"; } + /** + * A fluent API {@link io.helidon.common.Builder} to build instances of {@link SchemaType}. + */ + public static class Builder implements io.helidon.common.Builder { + + private String name; + private String valueClassName; + private String description; + private boolean isInterface; + private String implementingInterface; + private List listSchemaFieldDefinitions = new ArrayList<>(); + + /** + * Set the name of the {@link SchemaType}. + * + * @param name the name of the {@link SchemaType} + * @return updated builder instance + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Set the the value class name. + * @param valueClassName the value class name + * @return updated builder instance + */ + public Builder valueClassName(String valueClassName) { + this.valueClassName = valueClassName; + return this; + } + + /** + * Set the description of the {@link SchemaType}. + * @param description the description of the {@link SchemaType} + * @return updated builder instance + */ + public Builder description(String description) { + this.description = description; + return this; + } + + /** + * Set if the {@link SchemaType} is an interface. + * @param isInterface true if the {@link SchemaType} is an interface + * @return updated builder instance + */ + public Builder isInterface(boolean isInterface) { + this.isInterface = isInterface; + return this; + } + + /** + * Set the interface that this {@link SchemaType} implements. + * @param implementingInterface the interface that this {@link SchemaType} implements + * @return updated builder instance + */ + public Builder implementingInterface(String implementingInterface) { + this.implementingInterface = implementingInterface; + return this; + } + + /** + * Add a {@link SchemaFieldDefinition} to the {@link SchemaType}. + * @param fieldDefinition {@link SchemaFieldDefinition} to add + * @return updated builder instance + */ + public Builder addFieldDefinition(SchemaFieldDefinition fieldDefinition) { + this.listSchemaFieldDefinitions.add(fieldDefinition); + return this; + } + + @Override + public SchemaType build() { + Objects.requireNonNull(name, "Name must be specified"); + return new SchemaType(this); + } + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java index 42b94364985..b388a0ec314 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLTest.java @@ -156,7 +156,7 @@ protected static void setupIndex(String indexFileName, Class... clazzes) thro assertThat(indexFile.exists(), CoreMatchers.is(true)); // do a load to check the classes are there - JandexUtils utils = new JandexUtils(); + JandexUtils utils = JandexUtils.create(); utils.loadIndexes(); assertThat(utils.hasIndex(), CoreMatchers.is(true)); int count = 0; diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java index 6afb08849a8..61ccc3db1d5 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/JandexUtilsTest.java @@ -42,7 +42,7 @@ public void resetProperties() { @Test public void testDefaultIndexFile() { - JandexUtils utils = new JandexUtils(); + JandexUtils utils = JandexUtils.create(); assertThat(utils.getIndexFile(), is(JandexUtils.DEFAULT_INDEX_FILE)); assertThat(utils.hasIndex(), is(false)); } @@ -50,7 +50,7 @@ public void testDefaultIndexFile() { @Test public void testCustomIndexFile() { System.setProperty(JandexUtils.PROP_INDEX_FILE, "my-index-file"); - JandexUtils utils = new JandexUtils(); + JandexUtils utils = JandexUtils.create(); assertThat(utils.getIndexFile(), is("my-index-file")); } @@ -63,7 +63,7 @@ public void testLoadingCustomIndexFile() throws IOException, ClassNotFoundExcept "java/lang/String.class", "java/lang/Double.class", "java/lang/Integer.class"); - JandexUtils utils = new JandexUtils(); + JandexUtils utils = JandexUtils.create(); utils.loadIndexes(); assertThat(utils.hasIndex(), is(true)); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java index b6056903242..e29820b006a 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaArgumentTest.java @@ -32,15 +32,29 @@ class SchemaArgumentTest { private static final Class INTEGER = Integer.class; @Test - public void testConstructors() { - SchemaArgument schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); + public void testBuilders() { + SchemaArgument schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("Int") + .mandatory(true) + .defaultValue(null) + .originalType(INTEGER) + .build(); + assertThat(schemaArgument.argumentName(), is("name")); assertThat(schemaArgument.argumentType(), is("Int")); assertThat(schemaArgument.mandatory(), is(true)); assertThat(schemaArgument.defaultValue(), is(nullValue())); assertThat(schemaArgument.originalType().getName(), is(Integer.class.getName())); - schemaArgument = new SchemaArgument("name2", "String", false, "Default", STRING); + schemaArgument = SchemaArgument.builder() + .argumentName("name2") + .argumentType("String") + .mandatory(false) + .defaultValue("Default") + .originalType(STRING) + .build(); + assertThat(schemaArgument.argumentName(), is("name2")); assertThat(schemaArgument.argumentType(), is("String")); assertThat(schemaArgument.mandatory(), is(false)); @@ -59,7 +73,7 @@ public void testConstructors() { assertThat(schemaArgument.isSourceArgument(), is(true)); assertThat(schemaArgument.format(), is(nullValue())); - schemaArgument.format(new String[] { "value-1", "value-2"}); + schemaArgument.format(new String[] { "value-1", "value-2" }); String[] format = schemaArgument.format(); assertThat(format, is(notNullValue())); assertThat(format.length, is(2)); @@ -79,7 +93,13 @@ public void testConstructors() { @Test public void testSchemaArgumentArrayTypes() { - SchemaArgument schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); + SchemaArgument schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("Int") + .mandatory(true) + .originalType(INTEGER) + .build(); + assertThat(schemaArgument.isArrayReturnType(), is(false)); assertThat(schemaArgument.isArrayReturnTypeMandatory(), is(false)); assertThat(schemaArgument.arrayLevels(), is(0)); @@ -87,57 +107,122 @@ public void testSchemaArgumentArrayTypes() { @Test public void testSchemaGenerationWithArrays() { - SchemaArgument schemaArgument = new SchemaArgument("name", "String", false, null, STRING); - schemaArgument.arrayLevels(1); - schemaArgument.arrayReturnTypeMandatory(true); - schemaArgument.arrayReturnType(true); - assertThat(schemaArgument.getSchemaAsString(), is("name: [String!]")); + SchemaArgument schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("String") + .mandatory(false) + .originalType(STRING) + .arrayLevels(1) + .arrayReturnTypeMandatory(true) + .arrayReturnType(true) + .build(); + + assertThat(schemaArgument.getSchemaAsString(), is("name: [String!]")); } @Test public void testSchemaGeneration() { - SchemaArgument schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); + SchemaArgument schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("Int") + .mandatory(true) + .defaultValue(null) + .originalType(INTEGER) + .build(); assertThat(schemaArgument.getSchemaAsString(), is("name: Int!")); - schemaArgument = new SchemaArgument("name", "Int", true, 10, INTEGER); + schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("Int") + .mandatory(true) + .defaultValue(10) + .originalType(INTEGER) + .build(); assertThat(schemaArgument.getSchemaAsString(), is("name: Int! = 10")); - schemaArgument = new SchemaArgument("name", "Int", false, null, INTEGER); + schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("Int") + .mandatory(false) + .defaultValue(null) + .originalType(INTEGER) + .build(); assertThat(schemaArgument.getSchemaAsString(), is("name: Int")); - schemaArgument = new SchemaArgument("name", "Int", false, 10, INTEGER); + schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("Int") + .mandatory(false) + .defaultValue(10) + .originalType(INTEGER) + .build(); assertThat(schemaArgument.getSchemaAsString(), is("name: Int = 10")); - schemaArgument = new SchemaArgument("name", "String", false, "The Default Value", STRING); + schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("String") + .mandatory(false) + .defaultValue("The Default Value") + .originalType(STRING) + .build(); assertThat(schemaArgument.getSchemaAsString(), is("name: String = \"The Default Value\"")); - schemaArgument = new SchemaArgument("name", "String", true, "The Default Value", STRING); + schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("String") + .mandatory(true) + .defaultValue("The Default Value") + .originalType(STRING) + .build(); assertThat(schemaArgument.getSchemaAsString(), is("name: String! = \"The Default Value\"")); - schemaArgument = new SchemaArgument("name", "Int", false, 10, INTEGER); - schemaArgument.description("Description"); + schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("Int") + .mandatory(false) + .defaultValue(10) + .originalType(INTEGER) + .description("Description") + .build(); assertThat(schemaArgument.getSchemaAsString(), is("\"Description\"\nname: Int = 10")); // test array return types - schemaArgument = new SchemaArgument("name", "Int", false, null, INTEGER); - schemaArgument.arrayReturnType(true); - schemaArgument.arrayLevels(1); + schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("Int") + .mandatory(false) + .defaultValue(null) + .originalType(STRING) + .arrayReturnType(true) + .arrayLevels(1) + .build(); assertThat(schemaArgument.getSchemaAsString(), is("name: [Int]")); schemaArgument.arrayReturnTypeMandatory(true); assertThat(schemaArgument.getSchemaAsString(), is("name: [Int!]")); - schemaArgument = new SchemaArgument("name", "Int", true, null, INTEGER); - schemaArgument.arrayReturnType(true); - schemaArgument.arrayLevels(1); - schemaArgument.arrayReturnTypeMandatory(true); + schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("Int") + .mandatory(true) + .defaultValue(null) + .arrayReturnType(true) + .originalType(INTEGER) + .arrayLevels(1) + .arrayReturnTypeMandatory(true) + .build(); assertThat(schemaArgument.getSchemaAsString(), is("name: [Int!]!")); - schemaArgument = new SchemaArgument("name", "String", true, "Hello", STRING); - schemaArgument.arrayReturnType(true); - schemaArgument.arrayLevels(3); - schemaArgument.arrayReturnTypeMandatory(true); + schemaArgument = SchemaArgument.builder() + .argumentName("name") + .argumentType("String") + .mandatory(true) + .defaultValue("Hello") + .arrayReturnType(true) + .originalType(STRING) + .arrayLevels(3) + .arrayReturnTypeMandatory(true) + .build(); assertThat(schemaArgument.getSchemaAsString(), is("name: [[[String!]]]! = \"Hello\"")); - } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java index 8f1d6a9ed2f..809627f07fb 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaDirectiveTest.java @@ -21,6 +21,7 @@ import static graphql.introspection.Introspection.DirectiveLocation.FIELD; import static graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION; import static graphql.introspection.Introspection.DirectiveLocation.QUERY; +import static io.helidon.microprofile.graphql.server.TestHelper.createArgument; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -40,7 +41,7 @@ public void testConstructors() { assertThat(schemaDirective.arguments().size(), is(0)); assertThat(schemaDirective.locations().size(), is(0)); - SchemaArgument arg = new SchemaArgument("name", "String", true, null, STRING); + SchemaArgument arg = createArgument("name", "String", true, null, STRING); schemaDirective.addArgument(arg); assertThat(schemaDirective.arguments().contains(arg), is(true)); @@ -60,7 +61,7 @@ public void testDirectiveWith0Argument1Location() { @Test public void testDirectiveWith1Argument1Location() { - SchemaArgument arg = new SchemaArgument("name", "String", true, null, STRING); + SchemaArgument arg = createArgument("name", "String", true, null, STRING); SchemaDirective schemaDirective = SchemaDirective.builder() .name("directiveName") .addArgument(arg) @@ -71,8 +72,8 @@ public void testDirectiveWith1Argument1Location() { @Test public void testDirectiveWith2Argument1Location() { - SchemaArgument arg1 = new SchemaArgument("name", "String", true, null, STRING); - SchemaArgument arg2 = new SchemaArgument("name1", "Int", false, null, INTEGER); + SchemaArgument arg1 = createArgument("name", "String", true, null, STRING); + SchemaArgument arg2 = createArgument("name1", "Int", false, null, INTEGER); SchemaDirective schemaDirective = SchemaDirective.builder() .name("directiveName") .addArgument(arg1) @@ -85,8 +86,8 @@ public void testDirectiveWith2Argument1Location() { @Test public void testDirectiveWith2Argument2Location() { - SchemaArgument arg1 = new SchemaArgument("name", "String", true, null, STRING); - SchemaArgument arg2 = new SchemaArgument("name1", "Int", false, null,INTEGER); + SchemaArgument arg1 = createArgument("name", "String", true, null, STRING); + SchemaArgument arg2 = createArgument("name1", "Int", false, null,INTEGER); SchemaDirective schemaDirective = SchemaDirective.builder() .name("directiveName") .addArgument(arg1) diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java index b001276397a..e785f1a9d10 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinitionTest.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; +import static io.helidon.microprofile.graphql.server.TestHelper.createArgument; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; @@ -50,12 +51,12 @@ public void testConstructors() { assertThat(schemaFieldDefinition.isArrayReturnType(), is(true)); assertThat(schemaFieldDefinition.arrayLevels(), is(1)); - SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null, STRING); + SchemaArgument schemaArgument = createArgument("filter", "String", false, null, STRING); schemaFieldDefinition.addArgument(schemaArgument); assertThat(schemaFieldDefinition.arguments().size(), is(1)); assertThat(schemaFieldDefinition.arguments().get(0), is(schemaArgument)); - SchemaArgument schemaArgument2 = new SchemaArgument("filter2", "Integer", true, null, INTEGER); + SchemaArgument schemaArgument2 = createArgument("filter2", "Integer", true, null, INTEGER); schemaFieldDefinition.addArgument(schemaArgument2); assertThat(schemaFieldDefinition.arguments().size(), is(2)); assertThat(schemaFieldDefinition.arguments().contains(schemaArgument2), is(true)); @@ -171,7 +172,7 @@ public void testFieldDefinitionWith1Argument() { .arrayReturnType(false) .returnTypeMandatory(true) .arrayLevels(0) - .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .addArgument(createArgument("filter", "String", false, null, STRING)) .build(); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): Person!")); @@ -179,7 +180,7 @@ public void testFieldDefinitionWith1Argument() { @Test public void testFieldDefinitionWith1ArgumentAndDescription() { - SchemaArgument schemaArgument = new SchemaArgument("filter", "String", false, null, STRING); + SchemaArgument schemaArgument = createArgument("filter", "String", false, null, STRING); schemaArgument.description("Optional Filter"); SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") @@ -201,7 +202,7 @@ public void testFieldDefinitionWith1ArgumentAndArrayType() { .arrayReturnType(true) .returnTypeMandatory(true) .arrayLevels(1) - .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .addArgument(createArgument("filter", "String", false, null, STRING)) .build(); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): [Person]!")); @@ -229,7 +230,7 @@ public void testFieldDefinitionWith1MandatoryArgument() { .arrayReturnType(false) .returnTypeMandatory(true) .arrayLevels(0) - .addArgument(new SchemaArgument("filter", "String", true, null, STRING)) + .addArgument(createArgument("filter", "String", true, null, STRING)) .build(); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String!\n): Person!")); @@ -243,7 +244,7 @@ public void testFieldDefinitionWith1MandatoryArgumentAndArrayType() { .arrayReturnType(true) .returnTypeMandatory(true) .arrayLevels(1) - .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .addArgument(createArgument("filter", "String", false, null, STRING)) .build(); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String\n): [Person]!")); @@ -257,8 +258,8 @@ public void testFieldDefinitionWithMultipleArguments() { .arrayReturnType(false) .returnTypeMandatory(true) .arrayLevels(0) - .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) - .addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)) + .addArgument(createArgument("filter", "String", false, null, STRING)) + .addArgument(createArgument("age", "Int", true, null, INTEGER)) .build(); assertThat(schemaFieldDefinition.getSchemaAsString(), is("person(\nfilter: String, \nage: Int!\n): Person!")); @@ -274,9 +275,9 @@ public void testFieldDefinitionWithMultipleArgumentsAndDescription() { .arrayLevels(0) .build(); - SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); + SchemaArgument schemaArgument1 = createArgument("filter", "String", false, null, STRING); schemaArgument1.description("Optional filter"); - SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); + SchemaArgument schemaArgument2 = createArgument("age", "Int", true, null, INTEGER); schemaArgument2.description("Mandatory age"); schemaFieldDefinition.addArgument(schemaArgument1); schemaFieldDefinition.addArgument(schemaArgument2); @@ -295,9 +296,9 @@ public void testFieldDefinitionWithMultipleArgumentsAndBothDescriptions() { .description("Description of field definition") .build(); - SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); + SchemaArgument schemaArgument1 = createArgument("filter", "String", false, null, STRING); schemaArgument1.description("Optional filter"); - SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); + SchemaArgument schemaArgument2 = createArgument("age", "Int", true, null, INTEGER); schemaArgument2.description("Mandatory age"); schemaFieldDefinition.addArgument(schemaArgument1); schemaFieldDefinition.addArgument(schemaArgument2); @@ -315,9 +316,9 @@ public void testFieldDefinitionWithMultipleArgumentsWithArray() { .arrayReturnType(true) .returnTypeMandatory(false) .arrayLevels(1) - .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) - .addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)) - .addArgument(new SchemaArgument("job", "String", false, null, STRING)) + .addArgument(createArgument("filter", "String", false, null, STRING)) + .addArgument(createArgument("age", "Int", true, null, INTEGER)) + .addArgument(createArgument("job", "String", false, null, STRING)) .build(); assertThat(schemaFieldDefinition.getSchemaAsString(), diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java index 181e449087a..5ff724f2933 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaGeneratorTest.java @@ -235,7 +235,7 @@ public void testEnumGeneration() throws IntrospectionException, ClassNotFoundExc @Test public void testGettersPerson() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Person.class, false); + Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Person.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(13)); @@ -257,7 +257,7 @@ public void testGettersPerson() throws IntrospectionException, ClassNotFoundExce @Test public void testMultipleLevels() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Level0.class, false); + Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Level0.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); assertDiscoveredMethod(mapMethods.get("id"), "id", STRING, null, false, false, false); @@ -266,7 +266,7 @@ public void testMultipleLevels() throws IntrospectionException, ClassNotFoundExc @Test public void testTypeWithIdOnMethod() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator + Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(TypeWithIdOnMethod.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); @@ -276,7 +276,7 @@ public void testTypeWithIdOnMethod() throws IntrospectionException, ClassNotFoun @Test public void testTypeWithIdOnField() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator + Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(TypeWithIdOnField.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(2)); @@ -286,7 +286,7 @@ public void testTypeWithIdOnField() throws IntrospectionException, ClassNotFound @Test public void testTypeWithNameAndJsonbProperty() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator + Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(TypeWithNameAndJsonbProperty.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(6)); @@ -300,7 +300,7 @@ public void testTypeWithNameAndJsonbProperty() throws IntrospectionException, Cl @Test public void testInterfaceDiscovery() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator + Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(Vehicle.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(6)); @@ -315,7 +315,7 @@ public void testInterfaceDiscovery() throws IntrospectionException, ClassNotFoun @Test public void testInterfaceImplementorDiscovery1() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Car.class, false); + Map mapMethods = schemaGenerator.retrieveGetterBeanMethods(Car.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); assertDiscoveredMethod(mapMethods.get("plate"), "plate", STRING, null, false, false, false); @@ -330,7 +330,7 @@ public void testInterfaceImplementorDiscovery1() throws IntrospectionException, @Test public void testInterfaceImplementorDiscovery2() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator + Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(Motorbike.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(7)); @@ -346,7 +346,7 @@ public void testInterfaceImplementorDiscovery2() throws IntrospectionException, @Test public void testObjectWithIgnorableFields() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator.retrieveGetterBeanMethods( + Map mapMethods = schemaGenerator.retrieveGetterBeanMethods( ObjectWithIgnorableFieldsAndMethods.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(3)); @@ -395,7 +395,7 @@ public void testGetSimpleName() throws ClassNotFoundException { @Test public void testAllMethods() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator + Map mapMethods = schemaGenerator .retrieveAllAnnotatedBeanMethods(SimpleQueriesAndMutations.class); assertThat(mapMethods, is(notNullValue())); @@ -443,14 +443,14 @@ public void testDefaultDescription() { @Test public void testArrayDiscoveredMethods() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator + Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(8)); assertDiscoveredMethod(mapMethods.get("multiStringArray"), "multiStringArray", STRING, null, true, false, false); } - private void assertDiscoveredMethod(SchemaGenerator.DiscoveredMethod discoveredMethod, + private void assertDiscoveredMethod(SchemaGeneratorHelper.DiscoveredMethod discoveredMethod, String name, String returnType, String collectionType, @@ -468,7 +468,7 @@ private void assertDiscoveredMethod(SchemaGenerator.DiscoveredMethod discoveredM @Test public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassNotFoundException { - Map mapMethods = schemaGenerator + Map mapMethods = schemaGenerator .retrieveGetterBeanMethods(MultiLevelListsAndArrays.class, false); assertThat(mapMethods, is(notNullValue())); assertThat(mapMethods.size(), is(8)); diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java index 70d245af09f..8ab4e2dc7aa 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTest.java @@ -26,6 +26,8 @@ import org.junit.jupiter.api.Test; import static graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION; +import static io.helidon.microprofile.graphql.server.TestHelper.createArgument; +import static io.helidon.microprofile.graphql.server.TestHelper.createSchemaType; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -41,23 +43,23 @@ class SchemaTest extends AbstractGraphQLTest { @Test public void testEmptySchemaAsString() { - Schema schema = new Schema(); + Schema schema = Schema.create(); assertThat(schema.getSchemaAsString(), is("schema {\n}\n\n")); } @Test public void testTopLevelSchemaAsString() { - Schema schema = new Schema(); - schema.addType(new SchemaType("Query", null)); + Schema schema = Schema.create(); + schema.addType(createSchemaType("Query", null)); assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-01.txt"); - schema.addType(new SchemaType("Mutation", null)); + schema.addType(createSchemaType("Mutation", null)); assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-02.txt"); - schema.addType(new SchemaType("Subscription", "")); + schema.addType(createSchemaType("Subscription", "")); assertResultsMatch(schema.getSchemaAsString(), "test-results/schema-test-03.txt"); - SchemaArgument argument = new SchemaArgument("dateFormat", "String", true, null, STRING); + SchemaArgument argument = createArgument("dateFormat", "String", true, null, STRING); SchemaDirective schemaDirective = SchemaDirective.builder() .name("format") @@ -71,7 +73,7 @@ public void testTopLevelSchemaAsString() { @Test public void testScalars() { - Schema schema = new Schema(); + Schema schema = Schema.create(); assertThat(schema.getScalars().size(), is(0)); SchemaScalar schemaScalar1 = new SchemaScalar("Test", Date.class.getName(), ExtendedScalars.Date, null); @@ -87,7 +89,7 @@ public void testScalars() { @Test public void testDirectives() { - Schema schema = new Schema(); + Schema schema = Schema.create(); assertThat(schema.getDirectives().size(), is(0)); SchemaDirective schemaDirective1 = SchemaDirective.builder().name("directive1").build(); @@ -98,25 +100,12 @@ public void testDirectives() { assertThat(schema.getDirectives().contains(schemaDirective2), is(true)); } - @Test - public void testInputTypes() { - Schema schema = new Schema(); - assertThat(schema.getInputTypes().size(), is(0)); - SchemaInputType type1 = new SchemaInputType("name", "valueClass" ); - SchemaInputType type2 = new SchemaInputType("name2", "valueClass2"); - schema.addInputType(type1); - schema.addInputType(type2); - assertThat(schema.getInputTypes().size(), is(2)); - assertThat(schema.getInputTypes().contains(type1), is(true)); - assertThat(schema.getInputTypes().contains(type2), is(true)); - } - @Test public void tesTypes() { - Schema schema = new Schema(); + Schema schema = Schema.create(); assertThat(schema.getInputTypes().size(), is(0)); - SchemaType schemaType1 = new SchemaType("name", "valueClass"); - SchemaType schemaType2 = new SchemaType("name2", "valueClass2"); + SchemaType schemaType1 = createSchemaType("name", "valueClass"); + SchemaType schemaType2 = createSchemaType("name2", "valueClass2"); schema.addType(schemaType1); schema.addType(schemaType2); assertThat(schema.getTypes().size(), is(2)); @@ -128,7 +117,7 @@ public void tesTypes() { @Test public void testEnums() { - Schema schema = new Schema(); + Schema schema = Schema.create(); assertThat(schema.getEnums().size(), is(0)); List listString = new ArrayList<>(); listString.add("NEWHOPE"); @@ -149,7 +138,7 @@ public void testEnums() { @Test @Disabled public void testInvalidRuntimeWiring() { - Schema schema = new Schema(); + Schema schema = Schema.create(); assertThrows(IllegalStateException.class, schema::getRuntimeWiring); } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java index 06cca797f69..2e593a03b99 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/SchemaTypeTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Test; +import static io.helidon.microprofile.graphql.server.TestHelper.createArgument; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; @@ -33,7 +34,11 @@ class SchemaTypeTest { @Test public void testBuilders() { - SchemaType schemaType = new SchemaType("Name", "com.oracle.test.Value"); + SchemaType schemaType = SchemaType.builder() + .name("Name") + .valueClassName("com.oracle.test.Value") + .build(); + assertThat(schemaType.name(), is("Name")); assertThat(schemaType.valueClassName(), is("com.oracle.test.Value")); assertThat(schemaType.fieldDefinitions(), is(notNullValue())); @@ -66,7 +71,7 @@ public void testBuilders() { .arrayReturnType(true) .returnTypeMandatory(true) .arrayLevels(0) - .addArgument(new SchemaArgument("filter", "String", false, null, STRING)) + .addArgument(createArgument("filter", "String", false, null, STRING)) .build(); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -84,8 +89,6 @@ public void testBuilders() { @Test public void testImplementingInterface() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); - SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") .returnType("Person") @@ -94,10 +97,15 @@ public void testImplementingInterface() { .arrayLevels(0) .build(); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); - schemaType.addFieldDefinition(schemaFieldDefinition); - schemaType.implementingInterface("Contact"); + schemaFieldDefinition.addArgument(createArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(createArgument("age", "Int", true, null, INTEGER)); + + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .addFieldDefinition(schemaFieldDefinition) + .implementingInterface("Contact") + .build(); assertThat(schemaType.getSchemaAsString(), is("type Person implements Contact {\n" + "person(\nfilter: String, \nage: Int!\n): Person!\n" + @@ -106,7 +114,10 @@ public void testImplementingInterface() { @Test public void testTypeSchemaOutput() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .build(); SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") @@ -116,7 +127,7 @@ public void testTypeSchemaOutput() { .arrayLevels(0) .build(); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(createArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); assertThat(schemaType.fieldDefinitions().get(0).equals(schemaFieldDefinition), is(true)); @@ -129,7 +140,10 @@ public void testTypeSchemaOutput() { @Test public void testTypeSchemaOutputWithDescription() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .build(); SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") @@ -139,7 +153,7 @@ public void testTypeSchemaOutputWithDescription() { .arrayLevels(0) .build(); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(createArgument("filter", "String", false, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); schemaType.description("Type Description"); @@ -153,7 +167,10 @@ public void testTypeSchemaOutputWithDescription() { @Test public void testTypeStringOutputWith2Arguments() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .build(); SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") @@ -163,8 +180,8 @@ public void testTypeStringOutputWith2Arguments() { .arrayLevels(0) .build(); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); + schemaFieldDefinition.addArgument(createArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(createArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); assertThat(schemaType.getSchemaAsString(), is("type Person {\n" + @@ -174,7 +191,10 @@ public void testTypeStringOutputWith2Arguments() { @Test public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .build(); SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") @@ -184,11 +204,11 @@ public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { .arrayLevels(0) .build(); - SchemaArgument schemaArgument1 = new SchemaArgument("filter", "String", false, null, STRING); + SchemaArgument schemaArgument1 = createArgument("filter", "String", false, null, STRING); schemaArgument1.description("Argument1 Description"); schemaFieldDefinition.addArgument(schemaArgument1); - SchemaArgument schemaArgument2 = new SchemaArgument("age", "Int", true, null, INTEGER); + SchemaArgument schemaArgument2 = createArgument("age", "Int", true, null, INTEGER); schemaArgument2.description("Argument 2 Description"); schemaFieldDefinition.addArgument(schemaArgument2); schemaType.addFieldDefinition(schemaFieldDefinition); @@ -201,7 +221,10 @@ public void testTypeStringOutputWith2ArgumentsWithArgumentDescriptions() { @Test public void testTypeInterfaceStringOutputWith2Arguments() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .build(); SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") @@ -211,8 +234,8 @@ public void testTypeInterfaceStringOutputWith2Arguments() { .arrayLevels(0) .build(); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, 30, INTEGER)); + schemaFieldDefinition.addArgument(createArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition.addArgument(createArgument("age", "Int", true, 30, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); schemaType.isInterface(true); assertThat(schemaType.getGraphQLName(), is("interface")); @@ -224,7 +247,10 @@ public void testTypeInterfaceStringOutputWith2Arguments() { @Test public void testTypeInterfaceStringOutputWith2ArgumentsAndStringDefault() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .build(); SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") @@ -234,8 +260,8 @@ public void testTypeInterfaceStringOutputWith2ArgumentsAndStringDefault() { .arrayLevels(0) .build(); - schemaFieldDefinition.addArgument(new SchemaArgument("filter", "String", false, "hello", STRING)); - schemaFieldDefinition.addArgument(new SchemaArgument("age", "Int", true, 30, INTEGER)); + schemaFieldDefinition.addArgument(createArgument("filter", "String", false, "hello", STRING)); + schemaFieldDefinition.addArgument(createArgument("age", "Int", true, 30, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition); schemaType.isInterface(true); assertThat(schemaType.getGraphQLName(), is("interface")); @@ -248,7 +274,10 @@ public void testTypeInterfaceStringOutputWith2ArgumentsAndStringDefault() { @Test public void testTypeStringOutputWith2Fields() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .build(); SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") @@ -258,7 +287,7 @@ public void testTypeStringOutputWith2Fields() { .arrayLevels(0) .build(); - schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null, STRING)); + schemaFieldDefinition.addArgument(createArgument("personId", "String", true, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); SchemaFieldDefinition schemaFieldDefinition2 = SchemaFieldDefinition.builder() @@ -269,8 +298,8 @@ public void testTypeStringOutputWith2Fields() { .arrayLevels(1) .build(); - schemaFieldDefinition2.addArgument(new SchemaArgument("filter", "String", false, null, STRING)); - schemaFieldDefinition2.addArgument(new SchemaArgument("age", "Int", true, null, INTEGER)); + schemaFieldDefinition2.addArgument(createArgument("filter", "String", false, null, STRING)); + schemaFieldDefinition2.addArgument(createArgument("age", "Int", true, null, INTEGER)); schemaType.addFieldDefinition(schemaFieldDefinition2); assertThat(schemaType.getSchemaAsString(), is("type Person {\n" + @@ -281,7 +310,10 @@ public void testTypeStringOutputWith2Fields() { @Test public void testCreatingInputTypeFromType() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .build(); SchemaFieldDefinition schemaFieldDefinition = SchemaFieldDefinition.builder() .name("person") @@ -290,8 +322,8 @@ public void testCreatingInputTypeFromType() { .returnTypeMandatory(true) .arrayLevels(0) .build(); - - schemaFieldDefinition.addArgument(new SchemaArgument("personId", "String", true, null, STRING)); + + schemaFieldDefinition.addArgument(createArgument("personId", "String", true, null, STRING)); schemaType.addFieldDefinition(schemaFieldDefinition); SchemaInputType inputType = schemaType.createInputType("Input"); @@ -305,9 +337,12 @@ public void testCreatingInputTypeFromType() { @Test public void testToStringInternal() { - SchemaType schemaType = new SchemaType("Person", "com.oracle.Person"); + SchemaType schemaType = SchemaType.builder() + .name("Person") + .valueClassName("com.oracle.Person") + .build(); + assertThat(schemaType.toStringInternal(), is(notNullValue())); assertThat(schemaType.toString(), is(notNullValue())); } - } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/TestHelper.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/TestHelper.java new file mode 100644 index 00000000000..163300f98ed --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/TestHelper.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; + +/** + * Helpers for tests. + */ +public class TestHelper { + + /** + * Helper to create a {@link SchemaArgument}. + * @param argumentName argument name + * @param argumentType argument type + * @param isMandatory indicates if the argument is mandatory + * @param defaultValue default value + * @param originalType original type + * @return a new {@link SchemaArgument} + */ + public static SchemaArgument createArgument(String argumentName, String argumentType, + boolean isMandatory, Object defaultValue, Class originalType) { + return SchemaArgument.builder() + .argumentName(argumentName) + .argumentType(argumentType) + .mandatory(isMandatory) + .defaultValue(defaultValue) + .originalType(originalType) + .build(); + } + + /** + * Helper to create a {@link SchemaType}. + * @param name name of the type + * @param valueClassName value class name + * @return a new {@link SchemaType} + */ + public static SchemaType createSchemaType(String name, String valueClassName) { + return SchemaType.builder() + .name(name) + .valueClassName(valueClassName) + .build(); + } +} diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java index cc1ac439940..ed1bc9e4336 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AbstractGraphQLIT.java @@ -63,7 +63,7 @@ public void teardownTest() { @SuppressWarnings("unchecked") protected void assertMessageValue(String query, String expectedMessage, boolean dataExpected) { - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(DefaultContext.create()); Map mapResults = executionContext.execute(query); if (dataExpected && mapResults.size() != 2) { System.out.println(JsonUtils.convertMapToJson(mapResults)); diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java index 0cc088273ea..a8975e8887e 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java @@ -47,7 +47,7 @@ public class ExceptionHandlingIT extends AbstractGraphQLIT { @Test public void testEmptyErrorPayloads() throws IOException { setupIndex(indexFileName, ExceptionQueries.class); - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(DefaultContext.create()); Map errorMap = executionContext.newErrorPayload(); assertPayload(errorMap); @@ -60,7 +60,7 @@ public void testEmptyErrorPayloads() throws IOException { @Test public void testErrorPayLoadWithMessages() throws IOException { setupIndex(indexFileName, ExceptionQueries.class); - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(DefaultContext.create()); Map errorMap = executionContext.newErrorPayload(); assertPayload(errorMap); @@ -81,7 +81,7 @@ public void testErrorPayLoadWithMessages() throws IOException { @Test public void testErrorPayLoadWithMessagesAndLocations() throws IOException { setupIndex(indexFileName, ExceptionQueries.class); - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(DefaultContext.create()); Map errorMap = executionContext.newErrorPayload(); assertPayload(errorMap); @@ -113,7 +113,7 @@ public void testErrorPayLoadWithMessagesAndLocations() throws IOException { @Test public void testErrorPayLoadWithMessagesLocationsAndExtensions() throws IOException { setupIndex(indexFileName, ExceptionQueries.class); - ExecutionContext executionContext = new ExecutionContext(new DefaultContext()); + ExecutionContext executionContext = new ExecutionContext(DefaultContext.create()); Map errorMap = executionContext.newErrorPayload(); assertPayload(errorMap); From 1dd371d87309653ee94edc0e927b642039b43140 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 28 Oct 2020 09:57:28 +0800 Subject: [PATCH 130/178] formatting --- .../graphql/server/DefaultContext.java | 3 +-- .../graphql/server/SchemaGenerator.java | 5 ++--- .../server/CustomSchemaPreProcessor.java | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java index 338d53be0f1..f65630bc2ac 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DefaultContext.java @@ -20,8 +20,7 @@ * A default implementation of {@link Context} to be supplied to {@link ExecutionContext}. * Any other implementations should extend this. */ -public class DefaultContext - implements Context { +public class DefaultContext implements Context { private ThreadLocal currentThrowable = new ThreadLocal<>(); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index c315e6bd224..752ab840f3a 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -219,15 +219,14 @@ public Schema generateSchema() throws IntrospectionException, ClassNotFoundExcep * @throws IntrospectionException if any errors with introspection * @throws ClassNotFoundException if any classes are not found */ - protected Schema generateSchemaFromClasses(Class... clazzes) - throws IntrospectionException, ClassNotFoundException { + protected Schema generateSchemaFromClasses(Class... clazzes) throws IntrospectionException, ClassNotFoundException { Schema schema = Schema.create(); setUnresolvedTypes.clear(); setAdditionalMethods.clear(); SchemaType rootQueryType = SchemaType.builder().name(schema.getQueryName()).build(); - SchemaType rootMutationType = SchemaType.builder().name(schema.getMutationName()).build(); + SchemaType rootMutationType = SchemaType.builder().name(schema.getMutationName()).build(); // process any specific classes with the Input, Type or Interface annotations for (Class clazz : clazzes) { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java index 7230f217500..80e4634a409 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java @@ -1,5 +1,24 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server; +/** + * Custom {@link SchemaPreProcessor}. + */ @SchemaProcessor public class CustomSchemaPreProcessor implements SchemaPreProcessor { From 0562b0c52d0e751332206d7b9c648aa742078930 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 29 Oct 2020 11:01:47 +0800 Subject: [PATCH 131/178] Remove redudant code --- .../graphql/server/GraphQLCdiExtension.java | 23 ---- .../graphql/server/SchemaGenerator.java | 89 ++++++------ .../graphql/server/SchemaPreProcessor.java | 31 ----- .../graphql/server/SchemaProcessor.java | 46 ------- .../test/queries/SimpleQueriesWithArgs.java | 9 ++ .../graphql/server/test/types/Task.java | 127 ++++++++++++++++++ .../server/AllDefaultsExceptionIT.java | 1 - .../server/BLListAndWLExceptionIT.java | 1 - .../graphql/server/BLOfIOExceptionIT.java | 2 +- .../server/CustomSchemaPreProcessor.java | 31 ----- .../graphql/server/DataFetcherUtilsIT.java | 3 - .../graphql/server/DateTimeIT.java | 2 - .../graphql/server/DateTimeScalarIT.java | 1 - .../server/DefaultCheckedExceptionIT.java | 1 - .../graphql/server/DefaultValuesIT.java | 1 - .../graphql/server/DescriptionIT.java | 1 - .../server/DifferentMessageExceptionIT.java | 1 - .../graphql/server/ExceptionHandlingIT.java | 1 - .../graphql/server/GraphQLEndpointIT.java | 2 - .../graphql/server/IngorableIT.java | 1 - .../server/InvalidSchemaPreProcessor.java | 9 -- .../server/InvalidSchemaPreProcessorIT.java | 31 ----- .../microprofile/graphql/server/NullIT.java | 1 - .../server/PartialResultsExceptionIT.java | 1 - .../graphql/server/PropertyNameIT.java | 1 - .../server/SimpleQueriesWithArgsIT.java | 2 - .../microprofile/graphql/server/SourceIT.java | 1 - .../server/ValidSchemaPreProcessorIT.java | 31 ----- .../server/WLOfCheckedExceptionIT.java | 1 - 29 files changed, 177 insertions(+), 275 deletions(-) delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java delete mode 100644 microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java create mode 100644 microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Task.java delete mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java delete mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessor.java delete mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java delete mode 100644 tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java index 5e967d69240..d2992bcf951 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLCdiExtension.java @@ -39,11 +39,6 @@ public class GraphQLCdiExtension implements Extension { */ private final List> collectedApis = new ArrayList<>(); - /** - * The {@link List} of collected Schema processors. - */ - private final List> collectedProcessors = new ArrayList<>(); - /** * Collect the classes that have the following Microprofile GraphQL annotations. * @@ -56,15 +51,6 @@ void collectApis(@Observes @WithAnnotations({GraphQLApi.class, this.collectedApis.add(processAnnotatedType.getAnnotatedType().getJavaClass()); } - /** - * Collect the classes that have the {@link SchemaProcessor} annotation. - * - * @param processAnnotatedType annotation types to process - */ - void collectProcessors(@Observes @WithAnnotations(SchemaProcessor.class) ProcessAnnotatedType processAnnotatedType) { - this.collectedProcessors.add(processAnnotatedType.getAnnotatedType().getJavaClass()); - } - /** * Returns the collected API's. * @@ -73,13 +59,4 @@ void collectProcessors(@Observes @WithAnnotations(SchemaProcessor.class) Process public Class[] collectedApis() { return collectedApis.toArray(new Class[0]); } - - /** - * Returns the collected Schema Processors. - * - * @return the collected Schema Processors. - */ - public Class[] collectedSchemaProcessors() { - return collectedProcessors.toArray(new Class[0]); - } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 752ab840f3a..37b19a7d803 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -20,7 +20,6 @@ import java.beans.Introspector; import java.beans.MethodDescriptor; import java.beans.PropertyDescriptor; -import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -43,6 +42,8 @@ import javax.enterprise.inject.spi.CDI; import javax.json.bind.annotation.JsonbProperty; +import io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DiscoveredMethod; + import graphql.schema.DataFetcher; import graphql.schema.DataFetcherFactories; import graphql.schema.GraphQLScalarType; @@ -64,8 +65,6 @@ import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_OFFSET_DATE_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_TIME_SCALAR; import static io.helidon.microprofile.graphql.server.CustomScalars.CUSTOM_ZONED_DATE_TIME_SCALAR; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DiscoveredMethod.MUTATION_TYPE; -import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DiscoveredMethod.QUERY_TYPE; import static io.helidon.microprofile.graphql.server.FormattingHelper.DATE; import static io.helidon.microprofile.graphql.server.FormattingHelper.NO_FORMATTING; import static io.helidon.microprofile.graphql.server.FormattingHelper.NUMBER; @@ -77,6 +76,8 @@ import static io.helidon.microprofile.graphql.server.FormattingHelper.isJsonbAnnotationPresent; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATETIME_SCALAR; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DATE_SCALAR; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DiscoveredMethod.MUTATION_TYPE; +import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.DiscoveredMethod.QUERY_TYPE; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATETIME_SCALAR; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_DATE_SCALAR; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.FORMATTED_OFFSET_DATETIME_SCALAR; @@ -114,6 +115,8 @@ import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.stripMethodName; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.validateIDClass; + + /** * Various utilities for generating {@link Schema}s from classes. */ @@ -152,7 +155,7 @@ public class SchemaGenerator { /** * Holds the {@link Set} of additional methods that need to be added to types. */ - private Set setAdditionalMethods = new HashSet<>(); + private Set setAdditionalMethods = new HashSet<>(); /** * A {@link Context} to be passed to execution. @@ -189,24 +192,8 @@ public Schema generateSchema() throws IntrospectionException, ClassNotFoundExcep .getBeanManager() .getExtension(GraphQLCdiExtension.class); Class[] classes = extension.collectedApis(); - Class[] schemaProcessors = extension.collectedSchemaProcessors(); - - Schema schema = generateSchemaFromClasses(classes); - - for (Class schemaProcessor : schemaProcessors) { - if (SchemaPreProcessor.class.isAssignableFrom(schemaProcessor)) { - try { - Constructor constructor = schemaProcessor.getDeclaredConstructor(); - SchemaPreProcessor processor = (SchemaPreProcessor) constructor.newInstance(); - LOGGER.info("Calling SchemaPreProcessor " + processor); - processor.processSchema(schema); - } catch (Exception e) { - LOGGER.warning("Unable to constructor or call SchemaPreProcessor " - + schemaProcessor + ": " + e.getMessage()); - } - } - } - return schema; + + return generateSchemaFromClasses(classes); } /** @@ -313,7 +300,7 @@ protected Schema generateSchemaFromClasses(Class... clazzes) throws Introspec }); // process any additional methods require via the @Source annotation - for (SchemaGeneratorHelper.DiscoveredMethod dm : setAdditionalMethods) { + for (DiscoveredMethod dm : setAdditionalMethods) { // add the discovered method to the type SchemaType type = schema.getTypeByClass(dm.source()); if (type != null) { @@ -494,9 +481,9 @@ private SchemaType generateType(String realReturnType, boolean isInputType) SchemaType type = SchemaType.builder().name(simpleName).valueClassName(realReturnType).build(); type.description(getDescription(Class.forName(realReturnType).getAnnotation(Description.class))); - for (Map.Entry entry : retrieveGetterBeanMethods(Class.forName(realReturnType), isInputType) - .entrySet()) { - SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = entry.getValue(); + for (Map.Entry entry + : retrieveGetterBeanMethods(Class.forName(realReturnType), isInputType).entrySet()) { + DiscoveredMethod discoveredMethod = entry.getValue(); String valueTypeName = discoveredMethod.returnType(); SchemaFieldDefinition fd = newFieldDefinition(discoveredMethod, null); type.addFieldDefinition(fd); @@ -524,8 +511,9 @@ private void processGraphQLApiAnnotations(SchemaType rootQueryType, Class clazz) throws IntrospectionException, ClassNotFoundException { - for (Map.Entry entry : retrieveAllAnnotatedBeanMethods(clazz).entrySet()) { - SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = entry.getValue(); + for (Map.Entry entry + : retrieveAllAnnotatedBeanMethods(clazz).entrySet()) { + DiscoveredMethod discoveredMethod = entry.getValue(); Method method = discoveredMethod.method(); SchemaFieldDefinition fd = null; @@ -780,12 +768,13 @@ private SchemaEnum generateEnum(Class clazz) { /** * Return a new {@link SchemaFieldDefinition} with the given field and class. * - * @param discoveredMethod the {@link SchemaGeneratorHelper.DiscoveredMethod} + * @param discoveredMethod the {@link DiscoveredMethod} * @param optionalName optional name for the field definition * @return a {@link SchemaFieldDefinition} */ @SuppressWarnings("rawtypes") - private SchemaFieldDefinition newFieldDefinition(SchemaGeneratorHelper.DiscoveredMethod discoveredMethod, String optionalName) { + private SchemaFieldDefinition newFieldDefinition(DiscoveredMethod discoveredMethod, + String optionalName) { String valueClassName = discoveredMethod.returnType(); String graphQLType = getGraphQLType(valueClassName); DataFetcher dataFetcher = null; @@ -911,9 +900,9 @@ private void updateLongTypes(Schema schema, String longReturnType, String shortR * @throws IntrospectionException if any errors with introspection * @throws ClassNotFoundException if any classes are not found */ - protected Map retrieveAllAnnotatedBeanMethods(Class clazz) + protected Map retrieveAllAnnotatedBeanMethods(Class clazz) throws IntrospectionException, ClassNotFoundException { - Map mapDiscoveredMethods = new HashMap<>(); + Map mapDiscoveredMethods = new HashMap<>(); for (Method m : getAllMethods(clazz)) { boolean isQuery = m.getAnnotation(Query.class) != null; boolean isMutation = m.getAnnotation(Mutation.class) != null; @@ -923,7 +912,7 @@ protected Map retrieveAllAnnotat + " may not have both a Query and Mutation annotation"); } if (isQuery || isMutation || hasSourceAnnotation) { - SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null, false, true); + DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, null, false, true); discoveredMethod.methodType(isQuery || hasSourceAnnotation ? QUERY_TYPE : MUTATION_TYPE); String name = discoveredMethod.name(); if (mapDiscoveredMethods.containsKey(name)) { @@ -945,10 +934,10 @@ protected Map retrieveAllAnnotat * @return a {@link Map} of the methods and return types * @throws IntrospectionException if there were errors introspecting classes */ - protected Map retrieveGetterBeanMethods(Class clazz, + protected Map retrieveGetterBeanMethods(Class clazz, boolean isInputType) throws IntrospectionException, ClassNotFoundException { - Map mapDiscoveredMethods = new HashMap<>(); + Map mapDiscoveredMethods = new HashMap<>(); for (Method m : getAllMethods(clazz)) { if (m.getName().equals("getClass") || shouldIgnoreMethod(m, isInputType)) { @@ -967,8 +956,8 @@ protected Map retrieveGetterBean // only include if the field should not be ignored if (!shouldIgnoreField(clazz, propertyDescriptor.getName()) && !ignoreWriteMethod) { // this is a getter method, include it here - SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = generateDiscoveredMethod(m, clazz, propertyDescriptor, isInputType, - false); + DiscoveredMethod discoveredMethod = + generateDiscoveredMethod(m, clazz, propertyDescriptor, isInputType, false); mapDiscoveredMethods.put(discoveredMethod.name(), discoveredMethod); } } @@ -992,7 +981,7 @@ protected List getAllMethods(Class clazz) throws IntrospectionExcepti } /** - * Generate a {@link SchemaGeneratorHelper.DiscoveredMethod} from the given arguments. + * Generate a {@link DiscoveredMethod} from the given arguments. * * @param method {@link Method} being introspected * @param clazz {@link Class} being introspected @@ -1000,11 +989,12 @@ protected List getAllMethods(Class clazz) throws IntrospectionExcepti * methods as in the case for a {@link Query} annotation) * @param isInputType indicates if the method is part of an input type * @param isQueryOrMutation indicates if this is for a query or mutation - * @return a {@link SchemaGeneratorHelper.DiscoveredMethod} + * @return a {@link DiscoveredMethod} */ - private SchemaGeneratorHelper.DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, - PropertyDescriptor pd, boolean isInputType, - boolean isQueryOrMutation) throws ClassNotFoundException { + private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, + PropertyDescriptor pd, boolean isInputType, + boolean isQueryOrMutation) + throws ClassNotFoundException { String[] format = new String[0]; String description = null; boolean isReturnTypeMandatory = false; @@ -1029,7 +1019,8 @@ private SchemaGeneratorHelper.DiscoveredMethod generateDiscoveredMethod(Method m } } - isJsonbProperty = (isInputType ? pd.getWriteMethod() : method).getAnnotation(JsonbProperty.class) != null; + Method methodToCheck = isInputType ? pd.getWriteMethod() : method; + isJsonbProperty = methodToCheck != null && methodToCheck.getAnnotation(JsonbProperty.class) != null; ensureValidName(LOGGER, varName); @@ -1145,7 +1136,7 @@ private SchemaGeneratorHelper.DiscoveredMethod generateDiscoveredMethod(Method m format = methodFormat; } - SchemaGeneratorHelper.DiscoveredMethod discoveredMethod = new SchemaGeneratorHelper.DiscoveredMethod(); + DiscoveredMethod discoveredMethod = new DiscoveredMethod(); discoveredMethod.name(varName); discoveredMethod.method(method); discoveredMethod.format(format); @@ -1184,8 +1175,8 @@ private void ensureNonVoidQueryOrMutation(String returnClazzName, Method method, } /** - * Process the {@link ReturnType} and update {@link SchemaGeneratorHelper.DiscoveredMethod} as required. - * @param discoveredMethod {@link SchemaGeneratorHelper.DiscoveredMethod} + * Process the {@link ReturnType} and update {@link DiscoveredMethod} as required. + * @param discoveredMethod {@link DiscoveredMethod} * @param realReturnType {@link ReturnType} with details of the return types * @param returnClazzName return class name * @param isInputType indicates if the method is part of an input type @@ -1194,7 +1185,7 @@ private void ensureNonVoidQueryOrMutation(String returnClazzName, Method method, * * @throws ClassNotFoundException if any class not found */ - private void processReturnType(SchemaGeneratorHelper.DiscoveredMethod discoveredMethod, ReturnType realReturnType, + private void processReturnType(DiscoveredMethod discoveredMethod, ReturnType realReturnType, String returnClazzName, boolean isInputType, String varName, Method method) throws ClassNotFoundException { if (realReturnType.returnClass() != null && !ID.equals(returnClazzName)) { @@ -1229,10 +1220,10 @@ private void processReturnType(SchemaGeneratorHelper.DiscoveredMethod discovered * Process parameters for the given method. * * @param method {@link Method} to process - * @param discoveredMethod {@link SchemaGeneratorHelper.DiscoveredMethod} to update + * @param discoveredMethod {@link DiscoveredMethod} to update * @param annotatedName annotated name or null */ - private void processMethodParameters(Method method, SchemaGeneratorHelper.DiscoveredMethod discoveredMethod, String annotatedName) { + private void processMethodParameters(Method method, DiscoveredMethod discoveredMethod, String annotatedName) { Parameter[] parameters = method.getParameters(); if (parameters != null && parameters.length > 0) { java.lang.reflect.Type[] genericParameterTypes = method.getGenericParameterTypes(); diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java deleted file mode 100644 index 8f4b5323ada..00000000000 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaPreProcessor.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020 Oracle and/or its affiliates. - * - * 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 io.helidon.microprofile.graphql.server; - -/** - * Describes a {@link Schema} pre-processor that will be called prior to - * the GraphQL schema being generated but after the {@link Schema} has been created. - * This allows for modification of the {@link Schema} before it is parsed. - */ -public interface SchemaPreProcessor { - /** - * Pre-process a {@link Schema} before it is turned into a GraphQL Schema. - * - * @param schema {@link Schema} to preprocess - */ - void processSchema(Schema schema); -} diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java deleted file mode 100644 index 10d65dc2dc1..00000000000 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaProcessor.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020 Oracle and/or its affiliates. - * - * 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 io.helidon.microprofile.graphql.server; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Marks a class as potentially containing a {@link SchemaPreProcessor}. - * - *

- * For example: - *

- * {@literal @}SchemaProcessor
- * public class MySchemaProcessor implements SchemaPreProcessor {
- *
- *     {@literal @}Override
- *     public processSchema(Schema schema) {
- *        ....
- *     }
- *
- * }
- * 
- */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface SchemaProcessor { -} diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java index c69611774e0..7b9c1d599c7 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/SimpleQueriesWithArgs.java @@ -16,6 +16,7 @@ package io.helidon.microprofile.graphql.server.test.queries; +import io.helidon.microprofile.graphql.server.test.types.Task; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.json.bind.annotation.JsonbNumberFormat; @@ -41,6 +42,7 @@ import org.eclipse.microprofile.graphql.DateFormat; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Id; +import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Name; import org.eclipse.microprofile.graphql.NumberFormat; import org.eclipse.microprofile.graphql.Query; @@ -380,4 +382,11 @@ public List echoFormattedListOfIntegers(@Name("value") List<@NumberForm public List echoFormattedLocalDate(@Name("value") List<@DateFormat("dd-MM-yyyy") LocalDate> value) { return value; } + + @Mutation + public Task createTask(@Name("task") Task task) { + task = new Task(task.getDescription()); + System.out.println(task); + return task; + } } diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Task.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Task.java new file mode 100644 index 00000000000..3acf08b6f44 --- /dev/null +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/types/Task.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.microprofile.graphql.server.test.types; + +import java.io.Serializable; +import java.util.UUID; + +/** + * A data class representing a single To Do List task. + */ +public class Task implements Serializable { + + /** + * The creation time. + */ + private long createdAt; + + /** + * The completion status. + */ + private boolean completed; + + /** + * The task ID. + */ + private String id; + + /** + * The task description. + */ + private String description; + + /** + * Deserialization constructor. + */ + public Task() { + } + + /** + * Construct Task instance. + * + * @param description task description + */ + public Task(String description) { + this.id = UUID.randomUUID().toString().substring(0, 6); + this.createdAt = System.currentTimeMillis(); + this.description = description; + this.completed = false; + } + + /** + * Get the creation time. + * + * @return the creation time + */ + public long getCreatedAt() { + return createdAt; + } + + /** + * Get the task ID. + * + * @return the task ID + */ + public String getId() { + return id; + } + + /** + * Get the task description. + * + * @return the task description + */ + public String getDescription() { + return description; + } + + /** + * Set the task description. + * + * @param description the task description + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Get the completion status. + * + * @return true if it is completed, false otherwise. + */ + public boolean isCompleted() { + return completed; + } + + /** + * Sets the completion status. + * + * @param completed the completion status + */ + public void setCompleted(boolean completed) { + this.completed = completed; + } + + @Override + public String toString() { + return "Task{" + + "id=" + id + + ", description=" + description + + ", completed=" + completed + + '}'; + } +} diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java index 4db359d3672..24f87d769ad 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/AllDefaultsExceptionIT.java @@ -33,7 +33,6 @@ * Tests for exception handling with all defaults. */ @AddBean(ExceptionQueries.class) -@AddBean(SimpleContact.class) @AddBean(TestDB.class) public class AllDefaultsExceptionIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java index 1d792a6ad7f..670a74bb6ae 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLListAndWLExceptionIT.java @@ -34,7 +34,6 @@ * Tests for deny list and allow list. */ @AddBean(ExceptionQueries.class) -@AddBean(SimpleContact.class) @AddBean(TestDB.class) @AddConfig(key = ConfigKey.EXCEPTION_WHITE_LIST, value = "org.eclipse.microprofile.graphql.tck.apps.superhero.api.WeaknessNotFoundException") diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java index b9c27c9761d..3c2078d6731 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/BLOfIOExceptionIT.java @@ -31,7 +31,7 @@ * Tests for deny list. */ @AddBean(ExceptionQueries.class) -@AddBean(SimpleContact.class) +//@AddBean(SimpleContact.class) @AddBean(TestDB.class) @AddConfig(key = ConfigKey.EXCEPTION_BLACK_LIST, value = "java.io.IOException,java.util.concurrent.TimeoutException") public class BLOfIOExceptionIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java deleted file mode 100644 index 80e4634a409..00000000000 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/CustomSchemaPreProcessor.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020 Oracle and/or its affiliates. - * - * 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 io.helidon.microprofile.graphql.server; - -/** - * Custom {@link SchemaPreProcessor}. - */ -@SchemaProcessor -public class CustomSchemaPreProcessor implements SchemaPreProcessor { - - @Override - public void processSchema(Schema schema) { - // create a new input type from the Person type - SchemaInputType inputType = schema.getTypeByName("Person").createInputType("Input"); - schema.addInputType(inputType); - } -} diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java index ce5e496db1e..2ced4016a50 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DataFetcherUtilsIT.java @@ -46,9 +46,6 @@ * Tests for {@link DataFetcherUtils} class. */ @AddBean(SimpleQueriesWithArgs.class) -@AddBean(SimpleContactWithNumberFormats.class) -@AddBean(ContactRelationship.class) -@AddBean(SimpleContact.class) @AddBean(TestDB.class) class DataFetcherUtilsIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java index 0d3d6e255ca..e46590102fe 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeIT.java @@ -44,8 +44,6 @@ * Tests for date/times. */ @AddBean(SimpleQueriesAndMutations.class) -@AddBean(DateTimePojo.class) -@AddBean(SimpleDateTime.class) @AddBean(TestDB.class) public class DateTimeIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java index e146df853ac..f4369436613 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DateTimeScalarIT.java @@ -36,7 +36,6 @@ * Tests for date/time scalars. */ @AddBean(DateTimeScalarQueries.class) -@AddBean(SimpleDateTimePojo.class) @AddBean(TestDB.class) public class DateTimeScalarIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java index 88e5392b301..b7219f276c1 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultCheckedExceptionIT.java @@ -29,7 +29,6 @@ * Tests for default exceptions. */ @AddBean(ExceptionQueries.class) -@AddBean(SimpleContact.class) @AddBean(TestDB.class) public class DefaultCheckedExceptionIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java index 662df06a2c4..163a2b259c3 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DefaultValuesIT.java @@ -34,7 +34,6 @@ /** * Tests for default values. */ -@AddBean(DefaultValuePOJO.class) @AddBean(DefaultValueQueries.class) @AddBean(OddNamedQueriesAndMutations.class) @AddBean(TestDB.class) diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java index 0daa9cc5416..bfbf3495775 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DescriptionIT.java @@ -34,7 +34,6 @@ /** * Tests for descriptions. */ -@AddBean(DescriptionType.class) @AddBean(DescriptionQueries.class) @AddBean(TestDB.class) public class DescriptionIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java index c80a0ea7fbb..de169f2a4b8 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/DifferentMessageExceptionIT.java @@ -33,7 +33,6 @@ * Tests for different exception messages. */ @AddBean(ExceptionQueries.class) -@AddBean(SimpleContact.class) @AddBean(TestDB.class) @AddConfig(key = "mp.graphql.defaultErrorMessage", value = "new message") public class DifferentMessageExceptionIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java index a8975e8887e..5c29457894c 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ExceptionHandlingIT.java @@ -39,7 +39,6 @@ * Tests for testing exception handing in {@link SchemaGeneratorTest}. */ @AddBean(ExceptionQueries.class) -@AddBean(SimpleContact.class) @AddBean(TestDB.class) @SuppressWarnings("unchecked") public class ExceptionHandlingIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java index f52fbed7cc6..77c2843fa8b 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/GraphQLEndpointIT.java @@ -17,7 +17,6 @@ package io.helidon.microprofile.graphql.server; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.util.Map; import javax.ws.rs.client.Entity; @@ -56,7 +55,6 @@ @AddExtension(JaxRsCdiExtension.class) @AddExtension(ConfigCdiExtension.class) @AddExtension(CdiComponentProvider.class) -@AddBean(Person.class) @AddBean(GraphQLResource.class) @AddBean(GraphQLApplication.class) @AddConfig(key = "server.static.classpath.context", value = "/ui") diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java index b94931279e4..68dab098bc2 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/IngorableIT.java @@ -36,7 +36,6 @@ * Tests for ignorable fields. */ @AddBean(QueriesWithIgnorable.class) -@AddBean(ObjectWithIgnorableFieldsAndMethods.class) @AddBean(TestDB.class) public class IngorableIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessor.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessor.java deleted file mode 100644 index b0b511a7f22..00000000000 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessor.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.helidon.microprofile.graphql.server; - -/** - * A {@link SchemaProcessor} which is invalid because it does not implement - * {@link SchemaPreProcessor}. - */ -@SchemaProcessor -public class InvalidSchemaPreProcessor { -} diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java deleted file mode 100644 index 564efa2f559..00000000000 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/InvalidSchemaPreProcessorIT.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.helidon.microprofile.graphql.server; - -import java.beans.IntrospectionException; -import java.io.IOException; - -import io.helidon.microprofile.graphql.server.test.types.Person; -import io.helidon.microprofile.tests.junit5.AddBean; - -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * Test invalid {@link SchemaPreProcessor}. - */ -@AddBean(Person.class) -@AddBean(InvalidSchemaPreProcessor.class) -public class InvalidSchemaPreProcessorIT extends AbstractGraphQLIT { - - @Test - public void testCustomPreProcessor() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(indexFileName, Person.class); - SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); - Schema schema = schemaGenerator.generateSchema(); - - // should be null as an invalid class was found - assertThat(schema.getInputTypeByName("PersonInput"), is(nullValue())); - } -} diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java index 062b40bf4a7..e37be1a570f 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java @@ -35,7 +35,6 @@ /** * Tests for Nulls. */ -@AddBean(NullPOJO.class) @AddBean(QueriesAndMutationsWithNulls.class) @AddBean(TestDB.class) public class NullIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java index 9374e18e35c..66581b447b4 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PartialResultsExceptionIT.java @@ -34,7 +34,6 @@ * Tests for partial results with exception. */ @AddBean(ExceptionQueries.class) -@AddBean(SimpleContact.class) @AddBean(TestDB.class) public class PartialResultsExceptionIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java index 503f3592f85..0dd51435da8 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/PropertyNameIT.java @@ -34,7 +34,6 @@ * Tests for property naming. */ @AddBean(PropertyNameQueries.class) -@AddBean(TypeWithNameAndJsonbProperty.class) @AddBean(TestDB.class) public class PropertyNameIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java index 65426e099b2..5740626a812 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SimpleQueriesWithArgsIT.java @@ -45,8 +45,6 @@ * Tests for simple queries with args. */ @AddBean(SimpleQueriesWithArgs.class) -@AddBean(Car.class) -@AddBean(AbstractVehicle.class) @AddBean(TestDB.class) public class SimpleQueriesWithArgsIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java index 966fde0fee6..796966ad577 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/SourceIT.java @@ -36,7 +36,6 @@ * Tests for Source annotation. */ @AddBean(SimpleQueriesWithSource.class) -@AddBean(SimpleContact.class) @AddBean(TestDB.class) public class SourceIT extends AbstractGraphQLIT { diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java deleted file mode 100644 index 3aa6d00df2d..00000000000 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/ValidSchemaPreProcessorIT.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.helidon.microprofile.graphql.server; - -import java.beans.IntrospectionException; -import java.io.IOException; - -import io.helidon.microprofile.graphql.server.test.types.Person; -import io.helidon.microprofile.tests.junit5.AddBean; - -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * Test valid {@link SchemaPreProcessor}. - */ -@AddBean(Person.class) -@AddBean(CustomSchemaPreProcessor.class) -public class ValidSchemaPreProcessorIT extends AbstractGraphQLIT { - - @Test - public void testCustomPreProcessor() throws IOException, IntrospectionException, ClassNotFoundException { - setupIndex(indexFileName, Person.class); - SchemaGenerator schemaGenerator = new SchemaGenerator(defaultContext); - Schema schema = schemaGenerator.generateSchema(); - - // SchemaPreProcessor should have been called and generated InputType for Person - assertThat(schema.getInputTypeByName("PersonInput"), is(notNullValue())); - } -} diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java index 46c03e0af24..39af7ac7e85 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/WLOfCheckedExceptionIT.java @@ -32,7 +32,6 @@ * Tests for allow list of checked exceptions. */ @AddBean(ExceptionQueries.class) -@AddBean(SimpleContact.class) @AddBean(TestDB.class) @AddConfig(key = ConfigKey.EXCEPTION_WHITE_LIST, value = "java.lang.IllegalArgumentException") public class WLOfCheckedExceptionIT extends AbstractGraphQLIT { From c1f42d8cfd9024b2b059ba2db6b5d0168457029b Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 30 Oct 2020 19:18:07 +0800 Subject: [PATCH 132/178] Upgrade to microprofile-graphql official 1.0.3 release! --- dependencies/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies/pom.xml b/dependencies/pom.xml index f8fa025658c..ed67732fbc3 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -86,8 +86,8 @@ 2.10 1.4 2.2 - 1.0.3-SNAPSHOT - 1.0.3-SNAPSHOT + 1.0.3 + 1.0.3 1.1.1 2.3.2 1.1.2 From 3e6cf15b0b1fd4b82502ce145640692e8d942e7a Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 2 Nov 2020 08:53:24 +0800 Subject: [PATCH 133/178] Add builders and fix checkstyle --- dependencies/pom.xml | 5 +- .../graphql/server/GraphQLResource.java | 1 - .../graphql/server/SchemaFieldDefinition.java | 2 +- .../graphql/server/SchemaGenerator.java | 172 +++++++---- .../graphql/server/SchemaGeneratorHelper.java | 276 +++++++++++++++++- 5 files changed, 400 insertions(+), 56 deletions(-) diff --git a/dependencies/pom.xml b/dependencies/pom.xml index ed67732fbc3..b907019a800 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -85,9 +85,8 @@ 2.6.2 2.10 1.4 - 2.2 1.0.3 - 1.0.3 + 2.2 1.1.1 2.3.2 1.1.2 @@ -598,7 +597,7 @@ org.eclipse.microprofile.graphql microprofile-graphql-tck - ${version.lib.microprofile-graphql-tck} + ${version.lib.microprofile-graphql} org.eclipse.microprofile.jwt diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java index 01bd475709f..4dadefb45da 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/GraphQLResource.java @@ -114,7 +114,6 @@ public Response processGraphQLQueryPOST(String body) { @PostConstruct public void init() { try { - // TODO: Need to make this injectable context = new ExecutionContext(DefaultContext.create()); schemaPrinter = context.getSchemaPrinter(); } catch (Exception e) { diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java index d3f96c94da7..3dbcf6d9bb5 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaFieldDefinition.java @@ -465,7 +465,7 @@ public Builder name(String name) { } /** - * Set the returnType.. + * Set the returnType. * * @param returnType the returnType * @return updated builder instance diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index 37b19a7d803..b4287c56977 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -993,8 +993,7 @@ protected List getAllMethods(Class clazz) throws IntrospectionExcepti */ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, PropertyDescriptor pd, boolean isInputType, - boolean isQueryOrMutation) - throws ClassNotFoundException { + boolean isQueryOrMutation) throws ClassNotFoundException { String[] format = new String[0]; String description = null; boolean isReturnTypeMandatory = false; @@ -1002,8 +1001,6 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, boolean isJsonbFormat = false; boolean isJsonbProperty; String defaultValue = null; - - // retrieve the method name String varName = stripMethodName(method, !isQueryOrMutation); // check for either Name or JsonbProperty annotations on method or field @@ -1011,8 +1008,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, String annotatedName = getMethodName(isInputType ? pd.getWriteMethod() : method); if (annotatedName != null) { varName = annotatedName; - } else if (pd != null) { - // check the field only if this is a getter + } else if (pd != null) { // check the field only if this is a getter annotatedName = getFieldName(clazz, pd.getName()); if (annotatedName != null) { varName = annotatedName; @@ -1036,16 +1032,13 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, field = clazz.getDeclaredField(pd.getName()); fieldHasIdAnnotation = field != null && field.getAnnotation(Id.class) != null; description = getDescription(field.getAnnotation(Description.class)); - - // default values only make sense for input types - defaultValue = isInputType ? getDefaultValueAnnotationValue(field) : null; + defaultValue = isInputType ? getDefaultValueAnnotationValue(field) : null; // only make sense for input types NonNull nonNullAnnotation = field.getAnnotation(NonNull.class); isArrayReturnTypeMandatory = getAnnotationValue(getFieldAnnotations(field, 0), NonNull.class) != null; if (isInputType) { Method writeMethod = pd.getWriteMethod(); - if (writeMethod != null) { - // retrieve the setter method and check the description + if (writeMethod != null) { // retrieve the setter method and check the description String methodDescription = getDescription(writeMethod.getAnnotation(Description.class)); if (methodDescription != null) { description = methodDescription; @@ -1055,9 +1048,8 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, defaultValue = writeMethodDefaultValue; } - // for an input type the method annotation will override - NonNull methodAnnotation = writeMethod.getAnnotation(NonNull.class); - if (methodAnnotation != null) { + NonNull methodAnnotation = writeMethod.getAnnotation(NonNull.class); // for an input type the + if (methodAnnotation != null) { // method annotation will override nonNullAnnotation = methodAnnotation; } @@ -1098,29 +1090,25 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, } } - // if the return type is annotated as NonNull or it is a primitive then it is mandatory isReturnTypeMandatory = (isPrimitive(returnClazzName) && defaultValue == null) || nonNullAnnotation != null && defaultValue == null; - } catch (NoSuchFieldException e) { - } + } catch (NoSuchFieldException e) { } if (fieldHasIdAnnotation || method.getAnnotation(Id.class) != null) { validateIDClass(returnClazz); returnClazzName = ID; } - // check for format on the property but only override if it is null - if (field != null && isFormatEmpty(format)) { - format = getFormattingAnnotation(field); + if (field != null && isFormatEmpty(format)) { // check for format on the property + format = getFormattingAnnotation(field); // but only override if it is null if (isFormatEmpty(format)) { // check to see format of the inner most class. E.g. List<@DateFormat("DD/MM") String> format = FormattingHelper.getFieldFormat(field, 0); } isJsonbFormat = isJsonbAnnotationPresent(field); } - } else { - // pd is null which means this is for query or mutation + } else { // pd is null which means this is for query or mutation defaultValue = getDefaultValueAnnotationValue(method); isReturnTypeMandatory = isPrimitive(returnClazzName) && defaultValue == null || method.getAnnotation(NonNull.class) != null && defaultValue == null; @@ -1136,14 +1124,9 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, format = methodFormat; } - DiscoveredMethod discoveredMethod = new DiscoveredMethod(); - discoveredMethod.name(varName); - discoveredMethod.method(method); - discoveredMethod.format(format); - discoveredMethod.defaultValue(defaultValue); - discoveredMethod.jsonbFormat(isJsonbFormat); - discoveredMethod.jsonbProperty(isJsonbProperty); - discoveredMethod.propertyName(pd != null ? pd.getName() : null); + DiscoveredMethod discoveredMethod = DiscoveredMethod.builder().name(varName).method(method).format(format) + .defaultValue(defaultValue).jsonbFormat(isJsonbFormat).jsonbProperty(isJsonbProperty) + .propertyName(pd != null ? pd.getName() : null).build(); if (description == null && !isInputType) { description = getDescription(method.getAnnotation(Description.class)); @@ -1314,7 +1297,7 @@ private void processMethodParameters(Method method, DiscoveredMethod discoveredM */ protected ReturnType getReturnType(Class returnClazz, java.lang.reflect.Type genericReturnType, int parameterNumber, Method method) { - ReturnType actualReturnType = new ReturnType(); + ReturnType actualReturnType = ReturnType.create(); RootTypeResult rootTypeResult; String returnClazzName = returnClazz.getName(); boolean isCollection = Collection.class.isAssignableFrom(returnClazz); @@ -1370,6 +1353,7 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp int level = 1; boolean isParameter = parameterNumber != -1; String[] format = NO_FORMATTING; + RootTypeResult.Builder builder = RootTypeResult.builder(); boolean isReturnTypeMandatory; if (genericReturnType instanceof ParameterizedType) { @@ -1393,12 +1377,20 @@ protected RootTypeResult getRootTypeName(java.lang.reflect.Type genericReturnTyp } isReturnTypeMandatory = hasAnnotation || isPrimitive(clazz.getName()); - return new RootTypeResult(((Class) actualTypeArgument).getName(), level, isReturnTypeMandatory, format); + return builder.rootTypeName(((Class) actualTypeArgument).getName()) + .levels(level) + .arrayReturnTypeMandatory(isReturnTypeMandatory) + .format(format) + .build(); } else { Class clazz = genericReturnType.getClass(); isReturnTypeMandatory = clazz.getAnnotation(NonNull.class) != null || isPrimitive(clazz.getName()); - return new RootTypeResult(((Class) genericReturnType).getName(), level, isReturnTypeMandatory, format); + return builder.rootTypeName(((Class) genericReturnType).getName()) + .levels(level) + .arrayReturnTypeMandatory(isReturnTypeMandatory) + .format(format) + .build(); } } @@ -1454,7 +1446,15 @@ public static class ReturnType { /** * Default constructor. */ - public ReturnType() { + private ReturnType() { + } + + /** + * Create a new {@link ReturnType}. + * @return a new {@link ReturnType} + */ + public static ReturnType create() { + return new ReturnType(); } /** @@ -1620,23 +1620,24 @@ public static class RootTypeResult { private final String[] format; /** - * Construct a root type result. + * Construct a {@link RootTypeResult}. * - * @param rootTypeName root type of the {@link Collection} or {@link Map} - * @param isArrayReturnTypeMandatory indicates if the return type is mandatory - * @param levels number of levels in total - * @param format format of the result class + * @param builder the {@link Builder} to construct from */ - public RootTypeResult(String rootTypeName, int levels, boolean isArrayReturnTypeMandatory, String[] format) { - this.rootTypeName = rootTypeName; - this.levels = levels; - this.isArrayReturnTypeMandatory = isArrayReturnTypeMandatory; - if (format == null) { - this.format = null; - } else { - this.format = new String[format.length]; - System.arraycopy(format, 0, this.format, 0, this.format.length); - } + private RootTypeResult(Builder builder) { + this.rootTypeName = builder.rootTypeName; + this.levels = builder.levels; + this.isArrayReturnTypeMandatory = builder.isArrayReturnTypeMandatory; + this.format = builder.format; + } + + /** + * Fluent API builder to create {@link RootTypeResult}. + * + * @return new builder instance + */ + public static Builder builder() { + return new Builder(); } /** @@ -1679,5 +1680,78 @@ public String[] format() { System.arraycopy(format, 0, copy, 0, copy.length); return copy; } + + + /** + * A fluent API {@link io.helidon.common.Builder} to build instances of {@link DiscoveredMethod}. + */ + public static class Builder implements io.helidon.common.Builder { + + private String rootTypeName; + private int levels; + private boolean isArrayReturnTypeMandatory; + private String[] format; + + /** + * Set the root type of the {@link Collection} or {@link Map}. + * + * @param rootTypeName root type of the {@link Collection} or {@link Map} + * @return updated builder instance + */ + public Builder rootTypeName(String rootTypeName) { + this.rootTypeName = rootTypeName; + return this; + } + + /** + * Set the number of array levels if return type is an array. + * + * @param levels the number of array levels if return type is an array + * @return updated builder instance + */ + public Builder levels(int levels) { + this.levels = levels; + return this; + } + + /** + * Set if the value of the array is mandatory. + * + * @param isArrayReturnTypeMandatory If the return type is an array then indicates if the value in the array is + * mandatory + * @return updated builder instance + */ + public Builder arrayReturnTypeMandatory(boolean isArrayReturnTypeMandatory) { + this.isArrayReturnTypeMandatory = isArrayReturnTypeMandatory; + return this; + } + + /** + * Set the format for a number or date. + * + * @param format the format for a number or date + * @return updated builder instance + */ + public Builder format(String[] format) { + if (format == null) { + this.format = null; + } else { + this.format = new String[format.length]; + System.arraycopy(format, 0, this.format, 0, this.format.length); + } + + return this; + } + + /** + * Build the instance from this builder. + * + * @return instance of the built type + */ + @Override + public RootTypeResult build() { + return new RootTypeResult(this); + } + } } } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 8aeeccfc8e1..d994414e014 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -1244,9 +1244,39 @@ public static class DiscoveredMethod { private boolean isJsonbProperty; /** - * Default constructor. + * Construct a {@link DiscoveredMethod}. + * + * @param builder the {@link Builder} to construct from + */ + private DiscoveredMethod(Builder builder) { + this.name = builder.name; + this.returnType = builder.returnType; + this.methodType = builder.methodType; + this.collectionType = builder.collectionType; + this.isArrayReturnType = builder.isArrayReturnType; + this.isMap = builder.isMap; + this.listArguments = builder.listArguments; + this.method = builder.method; + this.arrayLevels = builder.arrayLevels; + this.source = builder.source; + this.propertyName = builder.propertyName; + this.isQueryAnnotated = builder.isQueryAnnotated; + this.format = builder.format; + this.description = builder.description; + this.isArrayReturnTypeMandatory = builder.isReturnTypeMandatory; + this.defaultValue = builder.defaultValue; + this.originalArrayType = builder.originalArrayType; + this.isJsonbFormat = builder.isJsonbFormat; + this.isJsonbProperty = builder.isJsonbProperty; + } + + /** + * Fluent API builder to create {@link DiscoveredMethod}. + * + * @return new builder instance */ - public DiscoveredMethod() { + public static Builder builder() { + return new Builder(); } /** @@ -1684,5 +1714,247 @@ public int hashCode() { isReturnTypeMandatory, defaultValue, isArrayReturnTypeMandatory, isJsonbFormat, isJsonbProperty); } + + /** + * A fluent API {@link io.helidon.common.Builder} to build instances of {@link DiscoveredMethod}. + */ + public static class Builder implements io.helidon.common.Builder { + private String name; + private String returnType; + private int methodType; + private String collectionType; + private boolean isArrayReturnType; + private boolean isMap; + private List listArguments = new ArrayList<>(); + private Method method; + private int arrayLevels = 0; + private String source; + private String propertyName; + private boolean isQueryAnnotated = false; + private String[] format = new String[0]; + private String description; + private boolean isReturnTypeMandatory; + private Object defaultValue; + private Class originalArrayType; + private boolean isJsonbFormat; + private boolean isJsonbProperty; + + /** + * Set the name of the {@link DiscoveredMethod}. + * + * @param name the name of the {@link DiscoveredMethod} + * @return updated builder instance + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Set the returnType. + * + * @param returnType the returnType + * @return updated builder instance + */ + public Builder returnType(String returnType) { + this.returnType = returnType; + return this; + } + + /** + * Set the method type. + * + * @param methodType the method type + * @return updated builder instance + */ + public Builder methodType(int methodType) { + this.methodType = methodType; + return this; + } + + /** + * Set the collection type. + * + * @param collectionType the collection type + * @return updated builder instance + */ + public Builder collectionType(String collectionType) { + this.collectionType = collectionType; + return this; + } + + /** + * Set if the return type is an array type such as a native array([]) or a List, Collection. + * + * @param isArrayReturnType true if the return type is an array type + * @return updated builder instance + */ + public Builder arrayReturnType(boolean isArrayReturnType) { + this.isArrayReturnType = isArrayReturnType; + return this; + } + + /** + * Indicates if the return type is a {@link Map}. + * @param isMap if the return type is a {@link Map} + * @return updated builder instance + */ + public Builder map(boolean isMap) { + this.isMap = isMap; + return this; + } + + /** + * Add an argument to the {@link DiscoveredMethod}. + * + * @param argument the argument to add to the {@link DiscoveredMethod} + * @return updated builder instance + */ + public Builder addArgument(SchemaArgument argument) { + listArguments.add(argument); + return this; + } + + /** + * Set the actual method. + * @param method the actual method + * @return updated builder instance + */ + public Builder method(Method method) { + this.method = method; + return this; + } + + /** + * Set the number of array levels if return type is an array. + * + * @param arrayLevels the number of array levels if return type is an array + * @return updated builder instance + */ + public Builder arrayLevels(int arrayLevels) { + this.arrayLevels = arrayLevels; + return this; + } + + /** + * Set the source on which the method should be added. + * @param source the source on which the method should be added + * @return updated builder instance + */ + public Builder source(String source) { + this.source = source; + return this; + } + + /** + * The property name if this property is a getter. + * @param propertyName property name if this property is a getter + * @return updated builder instance + */ + public Builder propertyName(String propertyName) { + this.propertyName = propertyName; + return this; + } + + /** + * Indicates if the method containing the {@link Source} annotation was also annotated with the {@link Query} + * annotation. + * + * @param isQueryAnnotated if the method containing the {@link Source} annotation was also annotated + * @return updated builder instance + */ + public Builder queryAnnotated(boolean isQueryAnnotated) { + this.isQueryAnnotated = isQueryAnnotated; + return this; + } + + /** + * Set the format for a number or date. + * + * @param format the format for a number or date + * @return updated builder instance + */ + public Builder format(String[] format) { + if (format == null) { + this.format = null; + } else { + this.format = new String[format.length]; + System.arraycopy(format, 0, this.format, 0, this.format.length); + } + + return this; + } + + /** + * Set the description. + * + * @param description the description of the {@link DiscoveredMethod} + * @return updated builder instance + */ + public Builder description(String description) { + this.description = description; + return this; + } + + /** + * Set if the return type is mandatory. + * + * @param isReturnTypeMandatory true if the return type is mandatory. + * @return updated builder instance + */ + public Builder returnTypeMandatory(boolean isReturnTypeMandatory) { + this.isReturnTypeMandatory = isReturnTypeMandatory; + return this; + } + + /** + * Set the default value for this {@link DiscoveredMethod}. + * + * @param defaultValue the default value for this field definition + * @return updated builder instance + */ + public Builder defaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + /** + * Set the original array inner type if it is array type. + * + * @param originalArrayType the original array inner type if it is array type + * @return updated builder instance + */ + public Builder originalArrayType(Class originalArrayType) { + this.originalArrayType = originalArrayType; + return this; + } + + /** + * Set if the format is of type Jsonb. + * + * @param isJsonbFormat if the format is of type Jsonb. + * @return updated builder instance + */ + public Builder jsonbFormat(boolean isJsonbFormat) { + this.isJsonbFormat = isJsonbFormat; + return this; + } + + /** + * Set if the property name is of type Jsonb. + * + * @param isJsonbProperty if the property name is of type Jsonb. + * @return updated builder instance + */ + public Builder jsonbProperty(boolean isJsonbProperty) { + this.isJsonbProperty = isJsonbProperty; + return this; + } + + @Override + public DiscoveredMethod build() { + return new DiscoveredMethod(this); + } + } } } From a83efef61eb9b9cb4504f49542591e35876f48ea Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 2 Nov 2020 10:11:30 +0800 Subject: [PATCH 134/178] code cleanup --- .../graphql/server/CustomScalars.java | 4 ---- .../graphql/server/DataFetcherUtils.java | 1 - .../graphql/server/FormattingHelper.java | 20 ------------------- .../graphql/server/SchemaGenerator.java | 8 +++++--- .../graphql/server/SchemaGeneratorHelper.java | 2 +- 5 files changed, 6 insertions(+), 29 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java index c839d8efd33..f5a1b22f44c 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/CustomScalars.java @@ -190,7 +190,6 @@ public static GraphQLScalarType newTimeScalar(String name) { * @param name the name of the scalar * @return a new custom date scalar */ - @SuppressWarnings("unchecked") public static GraphQLScalarType newDateScalar(String name) { GraphQLScalarType originalScalar = ExtendedScalars.Date; return GraphQLScalarType.newScalar() @@ -205,7 +204,6 @@ public static GraphQLScalarType newDateScalar(String name) { * * @return a new custom BigDecimal scalar */ - @SuppressWarnings("unchecked") private static GraphQLScalarType newCustomBigDecimalScalar() { GraphQLScalarType originalScalar = Scalars.GraphQLBigDecimal; @@ -221,7 +219,6 @@ private static GraphQLScalarType newCustomBigDecimalScalar() { * * @return a new custom Int scalar */ - @SuppressWarnings("unchecked") private static GraphQLScalarType newCustomGraphQLInt() { GraphQLScalarType originalScalar = GraphQLInt; @@ -237,7 +234,6 @@ private static GraphQLScalarType newCustomGraphQLInt() { * * @return a new custom Float scalar */ - @SuppressWarnings("unchecked") private static GraphQLScalarType newCustomGraphQLFloat() { GraphQLScalarType originalScalar = GraphQLFloat; diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 7868720258f..165b0c90787 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -553,7 +553,6 @@ private static Object getOriginalValue(Class originalType, Object key) { if (originalType.equals(Float.class) || originalType.equals(float.class)) { return (Float) key; } else if (originalType.equals(Long.class) || originalType.equals(long.class)) { - // key could be BigInteger or long return Long.valueOf(key.toString()); } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java index 890d53000c6..001e16434ee 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/FormattingHelper.java @@ -95,26 +95,6 @@ public class FormattingHelper { */ static final String[] NO_FORMATTING = new String[] {null, null, null }; - /** - * JsonbDateFormat class name. - */ - private static final String JSONB_DATE_FORMAT = JsonbDateFormat.class.getName(); - - /** - * DateFormat class name. - */ - private static final String DATE_FORMAT = org.eclipse.microprofile.graphql.DateFormat.class.getName(); - - /** - * JsonBNumberFormat class name. - */ - private static final String JSONB_NUMBER_FORMAT = JsonbNumberFormat.class.getName(); - - /** - * NumberFormat class name. - */ - private static final String NUMBER_FORMAT = org.eclipse.microprofile.graphql.NumberFormat.class.getName(); - /** * No-args constructor. */ diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java index b4287c56977..6be441362e9 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGenerator.java @@ -933,9 +933,10 @@ protected Map retrieveAllAnnotatedBeanMethods(Class * @param isInputType indicates if this type is an input type * @return a {@link Map} of the methods and return types * @throws IntrospectionException if there were errors introspecting classes + * @throws ClassNotFoundException if any class is not found */ protected Map retrieveGetterBeanMethods(Class clazz, - boolean isInputType) + boolean isInputType) throws IntrospectionException, ClassNotFoundException { Map mapDiscoveredMethods = new HashMap<>(); @@ -990,6 +991,7 @@ protected List getAllMethods(Class clazz) throws IntrospectionExcepti * @param isInputType indicates if the method is part of an input type * @param isQueryOrMutation indicates if this is for a query or mutation * @return a {@link DiscoveredMethod} + * @throws ClassNotFoundException if any class is not found */ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, PropertyDescriptor pd, boolean isInputType, @@ -1093,7 +1095,7 @@ private DiscoveredMethod generateDiscoveredMethod(Method method, Class clazz, isReturnTypeMandatory = (isPrimitive(returnClazzName) && defaultValue == null) || nonNullAnnotation != null && defaultValue == null; - } catch (NoSuchFieldException e) { } + } catch (NoSuchFieldException ignored) { } if (fieldHasIdAnnotation || method.getAnnotation(Id.class) != null) { validateIDClass(returnClazz); @@ -1166,7 +1168,7 @@ private void ensureNonVoidQueryOrMutation(String returnClazzName, Method method, * @param varName name of the variable * @param method {@link Method} being processed * - * @throws ClassNotFoundException if any class not found + * @throws ClassNotFoundException if any class is not found */ private void processReturnType(DiscoveredMethod discoveredMethod, ReturnType realReturnType, String returnClazzName, boolean isInputType, diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index d994414e014..7e8245af686 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -541,7 +541,7 @@ protected static String getFieldName(Class clazz, String fieldName) { /** * Return the field name after checking both the {@link Name} and {@link JsonbProperty} annotations are present on the {@link - * Method}.

Name will take precedence if both are specified. + * Method}. Name will take precedence if both are specified. * * @param method {@link Method} to check * @return the field name or null if non exist From a12c42365a248c9c69bcfba37143b916f93ff79d Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 2 Nov 2020 13:08:35 +0800 Subject: [PATCH 135/178] Add message about [][] --- .../microprofile/graphql/server/DataFetcherUtils.java | 10 ++++++++++ .../microprofile/graphql/server/SchemaArgument.java | 1 - .../graphql/server/SchemaGeneratorHelper.java | 8 +++++++- .../server/test/queries/ArrayAndListQueries.java | 5 +++++ .../graphql/server/MultiLevelArraysIT.java | 9 +++++++++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java index 165b0c90787..90deb72c509 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/DataFetcherUtils.java @@ -121,6 +121,16 @@ public static DataFetcher newMethodDataFetcher(Schema schema, Class cl if (originalType != null && originalType.isAssignableFrom(Map.class)) { ensureRuntimeException(LOGGER, MAP_MESSAGE); } + + if (argument.isArrayReturnType() && argument.arrayLevels() > 1 + && SchemaGeneratorHelper.isPrimitiveArray(argument.originalType())) { + throw new GraphQLConfigurationException("This implementation does not currently support " + + "multi-level primitive arrays as arguments. Please use " + + "List or Collection of Object equivalent. E.g. " + + "In place of method(int [][] value) use " + + " method(List> value)"); + } + listArgumentValues.add(generateArgumentValue(schema, argument.argumentType(), argument.originalType(), argument.originalArrayType(), diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java index 1b8d51e95e1..201460c5fa9 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaArgument.java @@ -526,5 +526,4 @@ public SchemaArgument build() { return new SchemaArgument(this); } } - } diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java index 7e8245af686..096439a1791 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/SchemaGeneratorHelper.java @@ -426,7 +426,13 @@ protected static boolean isPrimitiveArray(String clazz) { * @return true true of the class name is an array of primitives. */ protected static boolean isPrimitiveArray(Class clazz) { - return PRIMITIVE_ARRAY_MAP.containsKey(clazz.getName()); + String className = clazz.getName(); + for (String key : PRIMITIVE_ARRAY_MAP.keySet()) { + if (className.contains(key)) { + return true; + } + } + return false; } /** diff --git a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java index d49f86e63f4..83903dbf810 100644 --- a/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java +++ b/microprofile/graphql/server/src/test/java/io/helidon/microprofile/graphql/server/test/queries/ArrayAndListQueries.java @@ -116,6 +116,11 @@ public byte[] echoByteArray(@Name("param") byte[] value) { return value; } + @Query + public int[][] echo2LevelIntArray(@Name("param") int [][] value) { + return value; + } + @Query public SimpleContact[] echoSimpleContactArray(@Name("param") SimpleContact[] value) { return value; diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java index fea59b0789e..5ffca68e8e2 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/MultiLevelArraysIT.java @@ -59,6 +59,15 @@ public void testMultipleLevelsOfGenerics() throws IntrospectionException, ClassN generateGraphQLSchema(schema); } + @Test + public void testMultiLevelPrimitiveArrayAsArgument() throws IOException { + setupIndex(indexFileName, ArrayAndListQueries.class, MultiLevelListsAndArrays.class); + ExecutionContext executionContext = new ExecutionContext(defaultContext); + Map mapResults = executionContext.execute("query { echo2LevelIntArray(param: [[1, 2], [3, 4]]) }"); + assertThat(mapResults.size(), is(2)); + assertThat(mapResults.get("errors"), is(notNullValue())); + } + @Test @SuppressWarnings("unchecked") public void testMultiLevelListsAndArraysQueries() throws IOException { From d0737c098d292d1b27a398433ef4dd2586449b52 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 2 Nov 2020 17:03:32 +0800 Subject: [PATCH 136/178] Doco an config --- .../mp/graphql_01_introduction_graphiql.png | Bin 0 -> 137574 bytes docs/mp/graphql/01_mp_graphql.adoc | 206 ++++++++++++++++++ docs/mp/introduction/01_introduction.adoc | 11 +- docs/sitegen.yaml | 8 + .../graphql/server/ExecutionContext.java | 29 ++- .../microprofile/graphql/server/NullIT.java | 3 + 6 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 docs/images/mp/graphql_01_introduction_graphiql.png create mode 100644 docs/mp/graphql/01_mp_graphql.adoc diff --git a/docs/images/mp/graphql_01_introduction_graphiql.png b/docs/images/mp/graphql_01_introduction_graphiql.png new file mode 100644 index 0000000000000000000000000000000000000000..c15917cc27bada627d828314ad6baf38c8021494 GIT binary patch literal 137574 zcmaHz1y~(Rwy3e-!8O>%-7UDgySoQ>cMtBt6WlF01P?C3-66P3a9(Hb+?kV^b6>vR z(CO~#-o0v7)slY^sVFb;7XBSP7#P@FDM?XfFt9hlU|^7%u+YGf)a6SxFfedoD-jVz zDG?EnqLaP3m5mt~7*&Fap&^7%ertle=<6M6|M@e^1v~S6|oo=h4rZ zpLBIMa8NcNReQklmAad(5u-s5bqs?QsS45SS~l0u6hG7a{5Tw6AY^Z-0%*w_-s4KX zf^QY<1m&SYL-#|0bXwXA!20aK>%=|=#6r|r1S}k&+`dEMf$QN1Q%6VJK{Pc0uX~fo zil!29OexqXkCy1cjfVGu5!T^7UZ#hpraFoP&B14~hCC=yo;_?;EVCkuWUBWNB2;SK ztc+R;65WGcg8_ly7;7lAx%L6Q2};qB#Ho&Pf!$=#R1484Bguw_3twQa>p;S?yWo() z4!DrLYq)PB#UdSGnEXtr{(2KIj+W+Vaq;}REZJ`zni zMUaTSlNpGefsKKQgdZLR0`WSTnsY0QivP1Y@IO8hOBWXhZbn9TcXtMNRt9?~3r1!x zE-pqU7Dg5pdf*CrXHPp9BM*8zXVU+<$^YI*)Xdq$$;!dS%H9t2`@TlT_O32`BqYBd z^sj&a^F7Txtp5EZJLiAC7Vv_Mzt1o-GcYm!>)t?9-rq;L6|FqXY&1o!Yyt5A&){cf zV`k?4M}z-5_3uaiPg6B#Gba&yTcD*2|Gx$N&&Gc}`JWB{@l36MKl431$6p`$moxut z$;@Qjsk+_hv#MdSEupAb9+FHfq@BvNr?)ndVn8izsXWn!|OL_)oH~_ zJv|j~Z4(jRfD}bh1V@cWLrGQ?)^0@=Rc^%tPdGhIeHeF6WgQ(KKOQ|E@9B5G8(Vqr zIB~zu^L5mD{V~e{nUL3mRF+H-;%_&i=&npo{>#y$`RX@E)%xEqZ=};%c43b2kYypr zP`qG6koiP5B_#u({$>O1pfLq8rJ57u*Xkp;A|BkDQXAA=qZZF^?pK@fWJN=zYo>L_CQ*N7OPCX<>WQ3*Y$pDDWyWr;B>LV&C$=XOs9=5 z@JLMB)B^czZR7svgYiwx)W4HLmmCB}@G4aQ7VyGIa3Ly_xmkka#5q4QFOC;kTO2ln zR{YNer#!u7>UM@!-yNrnr0vceOrue{*+`|(q=?46o2`7H1BCtG9dsTW;EKUS5hvop zl*(w(F;iV{HQhlw%KrO}VL^bL!J)7d%U4@HWyZ7k@=3i>SMff%Xv6iSr7&d?i$Div zL!tkc@qcask5E+5Z4=S#K$osi3QidQN)O$Cm&Tx%7&VOj-hQneO-Jko`c?^jCH-kX zKhAW$!#7d+9RSNmwA$k0pX0)Uutcigk_7814M*$aw&XhEyyATdQ z_6aul=0m=A(9Y*V#vuX>o>aIH(VTKU-8SGAzD-tQAO15!#HV__8%Ov zj$3_j-ORxxs~x^N2wwoDp3JN9jHaNYV>XZIGTit8FVjA_7Zcx&*VKs((K9+bK{S%^ z-yJIGKq|d>y$YVs#s=!q{Gy)1NLbM;OryHEZ)OkNTMXKIpdjiD=PfsRw;mX6FdfT~TWhE6y=T+6O4jS+@T{nc+Mmq##y}Qji1=m1)zaIQUH2ull*h!a z*hX?|d%Sst8ZQy2d0E5BAq{6F=`UjN$Ox#7sQI-+8&vb@w`z8wL+NIQ4tFOs+Afm^ zhsDOT7Ari`B+M=Aw7MLEFlmx68eV;EcZO9|r-~IQ5q`?KAH@Gk(foM*bnh(V2@7e` zFPD6ekUE&X+(h@7FCIey%mPXL-@GM9ahT~I{j*s_#G~(&THT&yml|xX7GJ+;jA!#v zT1|PhkbS;AUKHVfzE+V+WAOdl7Gsj54B-`#jt35%;<{PV zc8OzTB#rTCXw7&aw$i5}k4my(`_R+ZImU5R27ZRw^D8n0w0SbuGE9+NU_r zPf>?ZelV0od3o0PI(&09-{2}SSD~esGs9TG%9>j`Q>yZaJN!Kg`^J?Bm^k?+N#R3w zkNS{7r~f@wf&!6cK_sWVZ^+~_`E|nH5|x&x(r5`^?u->$)CoA8*HmbIQ*U)SVOaN@ zmU+ikLJMc&$Na>$?Jo-C8|OHl1bg z-Wl8UF;=h6<-m!{VIy8PgQ*|wjcuM!RHuftzxfIQ7*4_Lz-O6s8%_56di&J54tocx)SO$kJjn#C~ zP12stK!@++4j_?KEP8Ftk1v24DwnBJvwP~Bf2&r= zQHm3Q>5kK^f~9xA9VcKqlt`|<@pLgj^n7gx3{>tz9-T&;AZF-+o$*hr)Ykp2mXjn} zHHLwUGcpT^J}>usiL4g#29E+WVLaPT#dQ59{3_0S<2S7BYu~(Os&re}qDobYhhy+K zcOK6=f2oYSg`eyVaJ>53w2f@~UPli6wKrgOpw9i3kJ4-l92WArCli5n5i<}>4Ek)@ zUlE~uD4D$P(_}BXb5V5aOM~T{WNg#3Xa<`t{c|TC9g)ATv=~l*(+){ulgM%0jSKpw|i17Mm^P+VsUFM?zb4fr?^5p2kjQ`UpakB38nTc z=byx0n`{>I3@~X_lPTmf_NgWVq7eoC45vgss%VUp%` zb10S0Vg{A49)Xg>=iP{i%OdKKZIsxFoE?-Hh?|*jf>X3RoRVZQk<)Um8UzR$f7_RV zAZE|^HgJICuS*56Ds4R7M_rB{N*za_l1dI^43RtS41RHJ7UC!Z7N4P{;t8^rI`fzV zBN=&@cb)eWS$tFX1Zgv}D;Ae%)K@(5k-jgW>S z?EY%{K9W{7xL`mr6yW#GMX{u zX5YaATaNZ#lYBg#kE%ohR_i^>uaDQnC}gwBct6HTg+wHfdC$>Jl};BYs)^lZm=-jK zMx~AH3{CGo-Csd$wyF+oU5Yi0qp^R$(8I{PO|9es7RZRH0!PS5JkCaZ9zS8YP?`Ec z*8Mhn7cJ`qkk8%urB<<a#r~TTGI7^s%fpU#k z>P$|10+2i>%K*+_YNBYjUNYd1v+n$iTJ$gV@o9XRzMJOB}(}pPzfF@1O#3#c$|MmBNFlJ zvJO~a@4eeSnyaKW8%Y&Y;Rcoi1&8PQ53|F^POXR^kNOXUwEi^L*3i0MB~ChKo&<>R zU(48!*pf%m7#_JM-oRtUZ;EuPC@gd!FkP!qVH^u8E^+$893)(y-h|F&X1zUHPbyB% zVOMz$i>BoBzI#%)Tdw+gyzVb39*fT)hd1v$7qWcMS+$`y>lj$}!n;#6qIqKYQSm>d)aTEvz5=^AK^g4}_eAX(%1o;7`=<2NOLOB)qdAq^#e zrv$NBk*9gYX-VOIdpf^6x_q(yD!JYPnQVx!1dmBmAjXGpl7*I?z+tyULn;xIgv+`| zMeLO~nsaouz_}z|rqk3Y#x}=evzEbDRsHK&Bgn}?P9cXkWl$+^(E9sAO4>I^Uf&1R zMQFOz+mmIh!08k>SzLTP-*cC`$AQU7BDn^lp;n|*x6Oy(4Y)I%yAEJw$<;2nR`;8i zL~+S4c@c>S`5Yp#rbL;U5rIs?UL~Nl zBZROA6Uu0svOdc9Je&RTN#`)Q<>IG-PNQd$mZrYVYOC64fi@M4BMt{_ji_Kx2i^xK zF^P(J|D!p2Tmip!d>%JhV9L&~CSE`QeSAm4Oo1EXfW!lyETPH`#lK)%Z?mWzb-M2R zE4!mKz(+&h9I=W)&kEfPBus)yt3IkA0Q%Wu{Kf?8D`V%AGD2?6n?Mhr%N?nM#jnML z$V)x!;dpNh;gC?Ghy%9!A^Hiks!^|r;81TH8J{R&;LIFI2eULHmTS~Y4LUpctMBv! zeWCdM8*_;)o;KzY`GcX~CIa)%;RHT^d2@Hb_STy~0>V{8t6MIe!`Q2sKiv}0Anb;_ z!ae+MgE-_Q!dw=)j7EHR+b#4WbFfJ7D|MyyVg13wjii-HRl!#pVQkl*quv%un$`NY z2S32O-Y^6~Ia{8jMHC{mygime= zSTt169rx0FK{>i&GOgO1diOpI;^$*T7Yb)Pr$Kq-i#EqSvwRS zlg`$-&|$Mj`%}HaCgkgb&#XPJRm zf*XEO=_dPieT2!dw89zzTA^;3EebR{lDjkqJ0f0>&jS&ekdhGr{SCpeR-#~}U$Oyv z^6-pTG~0w=9<@qFSEc*;o#O!OyJM6$h}Lgm_ox$#;~S3V4y_?_5soI1?S$_Jko_U! z9g?7ZyJAuY6G+POK7PG1O}N7k)GLroxNay%XuHG0n2(7HH)sCCiLBX&yYnw?Fkt!- z682g?SEi=d;e)sP@%2ihR9`{<$yvB#|E0lZxlY{xd~Zfq&fwQ6BMdUZl$UT0Z%c}= z1ra9Or*Wsy=v*c-^HM{B(W7L+MJ%7Gx)ztyyHX^qQmy4Z_uHxOUvJK0zC9vIPB3;R zh(@9ht!Sy3Ue2VmSkLj6YBwYzv8}XQMNJhc4St@xwM5jAF&JmQVmbO2!efWfg zCfYSTNHjprXIcu+)?W5gYKYq4_DT067IlGt+s3B^60v0UT(-1Lfv1AtqDxxln>|O; z-MKNHsg8UFQ$}dTYCl%1sgl44f)(8kJT}<_MB0=N2Q`V1R7HiauS`Di50&*wVQ;U} z^AS*I&G@Tm*5B@v!XV?P<`2ZmB=ns5S#J+{_CjozXjI2~Wjw1q^T0Ji%FLoa-W<*; z2PQAis*Eh(Bz3H|x+SL4XJ~pX=^Q+}y7Tcbx7$A=s|)Eaf2WoEkk0E^k)v^x*#YTV zd!RBf(gYbc$W@rcZmB52hs&#DXq!Ok&Vb9(oZAcQ@H);_h2GnwGe3T*R^5@vA*ZcX zZ&*~JS*z`b&nJKIkuqg`qtZqmPx441sAm5ZaMoXhFVoHDzwmpws?r<@pzcCqz*)zKLKV%`4kw@2h_7Pkt`x$jRE1JS6{ z8lD1gg1Td2kO`@YQmF`0gU1{|@bVdKN)nAdoa_>A1L)>~s1F-MAp!y5_oS9pl-F-i zn-{;DrAepIYApqIK_#Hsl0sli_Ci7<*0>NPbTqycH$K|=5<(D4 zu@RTiB|VI5&DIAl)h7Oq;GaGx9Jami5bn09ZxMyil`2rKKr_OK94DY^?^4B$35A>qT^5U&S;Odrr*&U@^-ug8wv>s=b~4>KL#e;D!VjEv|f zB3+@Yk-M|$x`NN+f*Nl3ywZM%wnp08X>s18dj!?>+OJ$=_i&O}qS6xp%MPpE0@K1n zAu|5^AIK`v$eh@QwGc)`dw8fnBUPAVV)u^hd z)Pl#L`aBW5Y5j0wdGLWt;-)3{&y}ak72-$OQvC;FKCjy;1?4UGb>G5)WGXcw35g8D z($1G9s$I9^#B83IL{l27^T$nrVPt`)5@e`y@Q6Z>cEV0hod%l^wME|dmnjTaidJYI z(#%8jxu4M@S{C{dCWj*W`jWC{t!HL;9d(a&@?0o*6Obk=_tJdLe%gO^xyO<2?V0}8 z@PRYA!CIZ>Y}+)|#Et*_bjXqShTG%Z;eMy`z13hy9E$!c^x?974dAECHEHFGvoE&V zh6C=tct z_`_U#gTAp}(RZ0tnMb7Ddz)g(su7>(1_dtIo+2XKWw#$@!o!(-Lhn=F<<^D?iaHpE z8rGb9yK)gHlSvutK_n#1!716%YqK@9J1{THG*26a817KYq^iCvQT&B~%^)S<3iCxN zX->IWlM7R*X-MCdI-W5u_xa7`qbQO=^9FiDGz2vU9jx3O9MRIA`WB1d)0zLH5xpiT zm*JzOSwXqInDIBGy_2V96Hm{=#w0vmw{Lz|()!;#PcBEY^@9nHL0JuP{7~UQKVJnI zSf`Y!(Qy*}5U@5dc|wgWITmw}%fu#OCuZ;@>6QlTp?P_Ky!K7!SFh-#SDvpz1335@ z5FLAv-wS(-=aS4SDu1*CUoIMnc0hyTozN40wHk)HDa+5_6vmT^l5f;>@g*rzGIXNa zwr4DOg8xw#i%#?ND>_8NG{|_LqI_zA4_PuC+&pYvwj<>C2Q&K>9Q3EJsJL$v z9$%gxmxyo(C*9B1#2TwZtcUe^8lOg8Lx=C;$e`RKx>0tGdW}eGeA&nU42OaO4Mn=N z67?G}1=`|AerPzq~X#e+#?;YvH#v|3B!gF9+JhtZQ|I-60nSJ}5H z2Iu2_N2|&9T0{a~L3>J#rMWOy+9%NiepRe0ao8NT1MI~%>!es3u|gmYK(pMT)5^aF zayVQ5s@6F=4ha+YNv&K#b#iyu2r4aDu_8 zuNzzD3=58v$2Y!tym&^(C~IV~x02k8)+vNdpGAt^|4jA#fIO+CQY;$}>D@H6z?^@5 z3yYZ^gNHyNbUx5f=b5Ci)3QjEl_rh?%_<7(uUp&Naj;2EjAP=yOwmK@N;7u6U+Z5_ zWK(acbmO)qN+jjZ&Xp;3ey-Pe>@Jgx6CzSTM#HuqA+XTl_FTkfwWO_p4@0z-)lx^h z`T4VMtB;s^)m^Xh`b=a=-q9%cAt~-A*h@VHV;%e?=MjJ58lSq+Z7(;3lHZ#2j|g1a zFos-iHEBJc^^V}FW+Oe7*$K3531KppC01MQ>c`US9QVFxOqE3g-pQW)Vk81aecn>F z^2TJ}$C5`patnp+YOHaOz0?uQSQ4 zGAhtAN4}a(tKzt#tit%c`Nt?Y#4I{QL}pJijVE4Z6|Ow*q1zjQEKLQacyCL{dmA1? zi;5o<{JAAC;P{iZ1Fu{RDdnnj;lU9f_l@fz9Wf@G(pfET%?Q8)F(*II4K$vXpKuSq z5z~^z2xsnMfZ9VgVYULoR$iE|R)IK?j+^g#n&Uz8Js;leEnlD9U2LbiCI=4CC#HX9 zY}O|&dNnWQvAqi8J;EL*CK=0Qw=R+~M(bxb=^q4we!h{7C6MM-=e;p-NBn}8VFW{f z(cJJp`jtv^Z`oyXqUfMH7xB*^Y~U}^uBY4?w|nj9Q@6d>#lb?7QDCLBZ)MS zh;Ac~E2<%&OJ&&cF1A?-tbIiUoCPlo!PrF1M<&mg<#fyvS9u>iN*BmIR~nnRb1_vr`EJ| z>vEaS%*1_SZ^+pobj>cBfKOQcsaAZMPFBNN%T}|qTc0`n@v{YSpx8|r<_))~C~S32 z_WL3mR9WEwOgggoa~?RKV}K!zR${B4qW-uK6{6Krt_lA=RH1 zj;)spI*@##@Hk_q;>!*!xAG4;9a1aO<87tJ$6E?NYvXd824FC%YmQC0XvCp<<_|oP znDobL2x&38OfG*|%R5q!1tZ|DPvLGTKWdM^4s+%@3^$s(iOSKIw4bTzkRSt8;@l)INg+;?eA4TBF5+TM{C5juBTZ| zn;+7Mr8p4is`Q;F>`+5Fjq)D$jC48*7Ci1GsY`d^7W)XKQ0)tQGtPvg`odblb#$J; z&*rerXoapi++PJU0GcF`#1JH2S2YhNA#fG(AldF=u%04XIKo=@WO4`+`_24!_~i^+6i>HqLpyXt+sOWv5bczu%|*~YGhhgRQxJT*l#sq0jrQz;i#nImLm~B^x)9- zxc5#rh!0j&Ir^%)^JPk@^86DAHJL7HQT2E#O2-9mk3TmGZuA7Ua!&}{&D6uyHjRMZ zTs(~AUEm&YSIV|+Im&<6v)Wng^<(K)Koy?#&+_~jEpqZ$G)y5HpStTVHJR$(<(&e!_Ndq#m$s8v>+iV@=A zUTZ(Ztl7&>--LN(ULUay`RBQe~6Z&tr^lnBOF_R;plk-P;8@)fKTqvz|8%V8BwH=*e zHC+!=g~Qrs=+npyheKj#gc``=dN?ylat}miUDb5`XUjFx*sOd=&gHyc=qF>ja{PDW z_vE#btCb5s#_EUJU!HIF*2+*uf6;4md(brrV$)2BCg4$55RQOYn9r9$CaC@J5GKX? zdbf>CU=__`xY+LLEVCYb#J)9bF&vG$-NQ`*_w@33c0(4k5FRoRX80QD;{mJV@29&N z&QTE;Jb~Ni0(&$!PdnU__#@TTyK2S=_YWujoDBTRUpYT`-xkU^H1V~Z-~T1IYi*;p z4o~ytkw_Xg`^yEp-HIS1Ye{jV``J=uzL|j1OQ)nx(*vv_E^C3tRooq{cf<>?=V5e# zLYlhzbBnrmy$vlct6B1_Ki#bBx!YEslvEbgm+$Xp5;uQJ_q(j6zH|1=tO(t^+G89h z+10Y9uhWL-((3l%f@NY$=(3)Fs;a~;l7EzYPF>tDz6D13^Kmbd{aL-2K#(nB6fMnX z)R%9P2dbT~57tro$b)z}?E@`!@=B@!7(oTlZ*jlaBFO49vX|=MVh+J8SR^%4ed!dw zuk&(#yw&qc@25=}YNX4~c9s!pKs%^xFdj}m>pLM_6-cWylRgai2A5QHFd<#0%CGdJ z1mVm30`lddtva(Yy0>`j^Zc_iSQ9rUxM=Yg_n%Z-wOCd95vMATELfaoEbl91+^I_` z&W5rni$9)wm*%}7l@6@CQ;{BG9B!R^yLa6xt__=m8{55}b)<>kSeGl_gwx>r7ha-t ze2#jl{ZPqq!f|t3%JhVl>!KKkm8Pte9;a%Wt>AL`nw-$kfL%*{wtjg{om=DO6F*U^ zF+~=6oex`?ym-AXeYe`+oHkRsthwQw#iatxZer?2a1UnT2^ z)kNiW9(x|R_hD89$vE|!X8Z-u^#URcBj)DdAOt96d%61epmQ1<;UK6;q8($p@fLZZ z&4%C1TlaNN%oIo^$^Lqi(?t=8N{4?=g!!}wqmDDT-SGSb1h?{9PF&4mA(6K*4($~j z+U;bCTFCy$tkp;x9xvpo{TQ9-2t(EhmVbRzJ+m%_GF+hTa>9Uf`xu3Fe|OSowW@5z z5jo3CMpOngHdfH=MdLi1Tkg7Om+L;2fzlp8jMZc}Erc={+TsO~boq0He5qGc z&RD%l_${@1eME)sO))<-udbeyQU%lyR&IS#D!uRVl_IvnO0oLZZVf7b(iY$Agv1j& zYCDe1n08EoLT*k6(i?=ik0~_6xn+#5ha3rIYP3Tc4M(oHytcTS^hHwB3O)qLcr~$> zt7R>9iBD>KV-L1vh`0~IIqj7rdwgcy?YODjRKe>OfJ-`!K1iJJx2o;bJ3NB(n3e6E zTq_5w_;5YV(Mt45w5qJ`3$M#v#3>9(MVpJAy$*Sw%`$wqd0lghv$=;V3X-BJ6IG+- znq-~n5ZO919(g8*{UoDEQ(`OV@%oh5mj9%{T?i45h(AkzKB zJcujQNASq6!_7S(0LrLNWQ=U7KnmUVIe=>1``4+}Ba}7LIj*?(+~X5ZqifD@6r|#Y zxh6If%7d8ekdh?md1T-h@l(9*NXVQuR9-V`KLPVJ(^?1Dw#6_Zkje07*f#Gi&h3<#i045kcN9T?QpR4EiEJ2Hf1!M)4+;tX!dL*OgY~R zGh4tx(BR;_bHW7#FF)}9MdQC`zn{qt^{-%)Z--k<;;QI62ND{kWumcr%N= zgoT;(IF`XD;Z3>yN#JFJ$74)?b)$760VVn+m0C}jml`&_&1qNq^XyRZFw(|#`d8`m zn9ryD1+|**n<4LRD|wtGR@o(U=+ZAA&LivE{9gmTd^T#yRIoMT7#}|nt-D&QO9J4> zPS1B4h&b$>Hxzp6=(A!mXllNnws~xzeR=#Z^1Prxp=c#OeYs;gjWX41&%4D$ooVf! zPFxj_#H{DU>z%IweIE%V;OzN>lKh`>dG~p&Dnz*%tGgtw^gI3J2>F{CTb#X9--@b! z$_?Lv@`e-bAcbgB1tSSIS`H{t)=+lX4z=7^_j{DnOM7f!haxeyo+%-22Hz~jK)d@5 z9W0Y!09mfnn%+xxW9*2eYyjSwPsIZ)q_nw89Uf|1f#vL@em{g2nLlv8KvF|`Xc3Hl zgj6r{$l{$I>7d=fkCzT5_k!WARTS`7btZgZQIMA6`lTw=2|ubi9RzXtl{>yS*{{vH zm4aJAqML0?NwKzYvoXqw!&JguIWNl8M0#9YhQez{8)>YF5hS;ii0)t<9ymyxELihU z-D1+~%THiJDs?J{);UR8*_EmkUfCdj$2p!#fY5?9W+Hr}+2+-PW-0)0 zYMoOWE7ey)G==?hjJY1Are@YxtOP+}_BNp=7~nMs>57 zslkuyy;b)O?pSEH!ehtvVh$IsIbed?w14meIIfB7-D#UkJuLvZSV#%$6;9t^fZE0v zje4hsM)4pyp375AaU}h$FFz8Buy?~8yk+Q3$u}`9DENkPVt;868hmIn_KOk;h9(yg1RrR^A&{y+aN<(qS8|BV{d#P~m7C-8xaF z-O6;yJh5MVN=f;Fj-ANhA)^4BB8MAUo#b(t@PuqTU4JK7BE%!6`Qe^vcg4*7&{66Swfa4eoyQ7>--3TV@t%j6 zUxNvBz-H7(MCCv|gA}*qgErr2FQ+(I>hKM()1>-rzH^-}IWfKM3&fSZczv5#8W{?G zKIB>1lPVBxPZi2|Ih9}xt5A}OntMAvp;RPSM%}{o-04XfMPlFI!FB^^;Js-(EqeUX zPd(eJKPbe2J=jl_*tIhps2^DF(yD!UgHiW)%P_O{y!uiB9#5DQKve+{bO~4GdVg|v zW@y@JYdwiZ`F)#c^v2?6ho?Ypr=1AX(Rlr-f*Z8EY$&mh_*`{aJl^uBtDV?n#?Fe@ ziz>FlGjVv7RUU;GXtI4jrGJBw5;HM``lMAp3>70#A?dEZRexmy;{%Fe6%lVSq#6Mf zbAt$w41=zrX^nq~u#!HI#@Sg%r`(N4v;As5Rzyc?>}Bc_MFw@fB;?2 zQ-Z+4KeVQc9iq{g$?q4jFpi8U^xkOtbVHct$KPJ^6Y<94xuQQ(Uj?!P7zusfl4h;R zP%HrtxlvDFB8ys?pzHBgD1y_~PuKfq8(e@@wTp-E-luwjg)Uscr^4y3Ve!28R~`({ zrGBkO`3VqX|4I1@?vtRL0=Q2!B9h+T%z@>`UR22v;2w=MSkIG-eN0ZIQX(n7sX$n1 zb`)XIYb~L;k)eBofc3+jcJtei=zl31plcia;06iZ!;E*c*6Rt~El#^mol0~6z}WBr z0@^kpBt`Ddv_F>ckEgrZcIBjA zKJORV)3p{-WJ3OUQ8;wzQf2ZKJrOcb0ND6+b@rBa9&X@oG~N&BS3iL)PYg8JBd8t# z_>E2IHoBvUz@Sd-i^0=)JTJ2rveWJSBDM}pTpuH^W8Sfs<$QuCam?$B>>C-8d7N8o zb60r83N`Y<=l(CTfx&r%1T^3v%JAI1(eZ}f8_!-Q9WWvV#+p$k7+-Mj?fty*sT?L| zbco~zoz_>&O-=xg@=D}59tcsrgM*WDZYXX509*BYe*DW`CQEL`N+xcEHjOP>jXV6*sLwYU0Ww$%}Sovo%73?;Rx zg@^uAmK!4I9+ebP!JsGcMNvV-{v!Y_(?;~W*a|I>PTvx7PZ*#CNcuNWmguPP@+9GZ z_xAr1$vB{PZsHO5|2ptDJ1FqLMM!Tb6GZ;~et-3m{<&`f@b^5VeLnw(ME-FV@U5tz z!-J9riT`t8|5d&LtiZ_2Mm6UDi%kA{{vZ1x5Wqzu-BLR!{~slg?7{_R>2X-*|43&3 zsW!skzo+sIGAG!-dHa8fZzCG)yP-wMCxyRPsQ+pDB>?REhN1%+7u5ex3=ZM}#x<=0|_&tXKq@dPEOlm59{nD(N`rK!r zQiIEJD`ch9-(aghh8AE=69*GWml^RxybanthylPcxi1o3CIl8$mw#%t&3*U!U^>W= zOgdQ-AjlavdqSN{Uw{}DxqQ}pG(InvwBfHPa8THQ*n>yX4F&#Q?4StPEFO0uZr1~b zHusZT=a5qa9|k>vGb#Y4xJ=r!{7p2QF4bFIKHOLX?qIvVcr=dEQjx33JAhqV`_`HGZkDE;YDv`TdcpDDZ-EN;qBqXz zVN@&qd_;kP;Ak>mg2fErXcI`o*{tVe^TeVkevekIujWHQ0>x}+Fu_i2ztu$%8H?Lx zKOQT{dai;RpcPXzs`ZCDUtefDUmnT{HGnCC*Mp5i45B*<(2EUM{sp|h@9}f0z*B?U zN#XP1-6(SZK;G2GtKBhrgr8&a47tCOxt#Z8T*=WiN_D#v&n$NVtDKt_h$LcVLRI)* z4pG)wO;dPY?nL%|#FY760i;qaKdX)i)LkU-dR|Hbd|5pvB-8>xehvac+XZF|0FhNK zRmu=f&%o#*W5qw3C`g<2EsX5XuanBS?vBd*0dGj!O_{a*p# zkUK!KY)PKj@99pPO5DHBVuAsXL?VEGSzllWodO7}8Nh2PcldY)E+8u11F4S;p$?z> zouvkA2x}NdMn-D2vSdJR5y>H5+uaq~ku)eBq5nrBXv*+lx~>HgZ(q0#fU5f%4wR>NQj zFxPg08iqN>!JNuY`Y=oIm{W~f?*%px}#zv0@WxBw&0u&C} zZ_{hlnO*|i=#k`>{x>fv0N{KxStl8afQ@x`D-0&C%BVbBrd9|<+`S0Yv4E*??x@io z#di`(bk26U=>U6Ja6xQ`r9aTmIP094gR>}Cz`sh&U|Jv0UDs>o#w0Q+5&+7ZCSmEb zdF_wAv;<q0-`~MMA9*3oy1u8N~zFfAeLMC7MNL=PQfNCQAFEZPt0p|K5;o071HDd zp`Q5M9IDw41xPOZros1(E0!rl;ldHH<>$MTc5338;Ik8({XXb3fC!)nazpSEOg@g@7=wMh%)A{~a7E=Q`R>FJ5_lJQFB3ksw7W zh*=el#$k486QAjajJ|zdJi=nNd~8V75zKV$E`O}A@-fm>TYL?m14RxA~WSun&C}M zHt#kv+q{3-NhMLl_;oM4pDZ;LmFV~U!iIyOO=Run2I}Wpv%WR~iJth9$-LnKBL9jL zBB#A^dXH{yw0P(?l)t46Z;XVIq-(8dPhS_|13FIW)y8SO)F?zoZ7D zNQ0!dL=zRX1fvNazp_~`2~K$w-m)et3B)EX4=;UQjUe{BJU;nt!yhj-$QWA1vso$2 zpx!}{8uhTUI^x-28`I#ylZl@9P~znDq6BT8MMX}>BT+Liao`H*iVpP{V5g#>WF?@m zFBdBinb{{8D9Bpq%y*tdr7?UEFv4Hg7L-q>N~nVFG!cEsh%lL+1Nv?kCQ zb4#mf60GBNvkqLaGt102Jb9uDPGB;fLC4AG}$BWk$a5H!A%2KHplG(tgm z??D9T4`v8`Mz3=|UFn9w63FbDsQX)MeXSeq}gzm`_5sF%v1LmlBI z*6O%j<%#Sk0Jq!2th&D<49ORP?Q0UBJ67#=)ri3UGyyP8?a8o#UcpJuZF;_1GOy_ zq!O_+&t7-uUru`g{U{=J+a!pVIsti2WbV;0*P3o-hXHthjL~fFIHGgg;QolUC53^t+E{$9yf+_k zDs?EIYSV$Ton2(mB~vh^`GIH#vq@M->TsxyVY$7VfAi~j?of0(t50Nnjhy8| z&6gZOB(=|rf(7_&mhpDPX2Z!-KybN|D4AYIeXiP|+SfZl>x=LkdQAX&lmcv%&m%gW zM#kKh$l`3`L?7m>DsKp6vG(ML0mEjlDwR%)MBwQ>7?_IOz7UDU5$Y|AtCnd>saI>! z1BGIfSeQSs;IZf|sqO8_eVWggpnE45lwkM=t0*k#UuuIuaj@oD3P}}B#9qDmxOq(I zZ~=nvF(>aSYN*sJwaEcb6)R+V`&8DjZv$Y__80L^Zs4$4`AhU#T_z1sBBpHTD$>8@ zrTP{tWT)z{# zM_z3Wg|xXEMleeGr1xII7_c-G2T1S-vC!)_E3We)mS{!^h@(6Jc3EjBlSY*;H4sEf z1{T-D0ga7;U`NA$yRv|z+4Z`+)#v+byFUdeWpp=4EL-eP|EGNmB1844tbkiNcquPI_4yD%}%%L`n5lA==EE{Huha z58Xxrn>oG#XS5Ib1KP0CaN-x1F})381*M6IuO+O<6b=a;l(LP)+A!&mJVML`u=?m4@nhQV}5O>k474}(e8(3I_ySR4>A^j__8c@yXmlm704m_wMU=VZQ0 ziVC2NvK56z~(g4n@a#M1S7+=dtx- zf;(ZjY@%%qJNWhGo4wAQ4XN@Rx-ipHFhdCCK+JqCSJ(}jS(Y4`h2EOm>g0m$2-h+a zEUkqti+~N+Fk=#Op)hw}?F=P7{0Kb2^NA(qFj+y-sg0cnh+&uImz%K^ zM}sy$IfUle(*-^acYJ34yQ2UuIsy=MrWQLflu5hf@2VU3mmid5*SPL$D4vLc1Yy@e zX+bKG8$0l67dnP^j~I-&4r8+M2_fd#<^>rY&ke0$FbOkVI_Uq1wbeTE()}BEHt>fm zSkU>zw%=3+6x1eWb+^Xu6VD0<4z=WN#e_;MXmU6aw1# z(0raE^kJ>N1;}T}E~^RNg?83S4b7TFpZ5!0-rmk}VBpXy8$LjzyDs2w#Nrapc5&+i z$~9x#HpQv;qZ{uNanBN&2a8BR*lLh#&>aE~O!PWgG_o+$X-6Pl1t&1kn$*$DMAQRT z{ru#2&4K7#ip|M*m$idy580jSNWXp)dX%52ayFSD64LZ5`%5E3Cv&^PIIpUI3)n?UHK}NxdW@!ub#M5c9aQQyeH);xMMHHZLB!C?7gwU4(W$2S@ z2ZiM$H=?e*+`KiWKfF%^LFuptH9&@_i>l(`!On!gfm|Rpy5twN#^Xt#)2iElj$J#P zEw>m7aap>6a>8S4K;GXiUXE@$#{ zwiu0PpflN6kOHZq!J-;7Q6R8R7Jcd)BdfrsQWQieG0*)*P9kXZHlE2y_*ob?BDJ3r zI}a2h3fnq21UoSIXfuy$8@(eL64C`LG{=qGJvkEy@;qDP(}NN|0HVEg-yx?tWk`|K zpN{Y@vm(3Cw?bFLn3e74tFkq7iJUc$_I0x$$s#T!BR1IKOd90RcWbe%jnIw0#lTu0 z@#nR35zU<}RFO)*gGYfX~Z0@&HdZXdKZ|%UKY|o?A%U-A5f{TBH@@prSLkA6+KD}j468Jeq${0?f*yEUq;0hW%G693Admu&a;6iqPe)6Y+8ypBA1t!lTz*f z5w8^Z@p-)i)h9ex)My{TbLxHtQ@g|yEmg{$i}=kWlKed#7pFdMQiupjQ=1$;nxDB) zK;gucN|-E-Vgay>Y@2u26AahzNA%qDslXFot8O*)8FNzCm@J?s(Y%?!^%OLm39pZ`tmh?&fgRgM-{!tAjxMmNQ>i< z3TL4u&$8rSduv}8LOMVvm2PnfEv*NCS>NmnZ?VVG#>(vDFva1S2^?Qc#DpxEL46bqy$jER{I zq1cuJB-C2bsx&*iJToGOPS)exfmD?gZs+n<(*UWE%;b1$px0*xAe9U_u%2{)X|LVV z549sJ=LeG|)tZP9Q;)z>9ea7MP}S5^4@G0z}I z6V?hwWs*M9u9~haKRe{g2?P)fst`MR&HB=k8MVlN^d#%N=p3GhwY{PE17*1@f9tLG z43_z_u>Z}AA&Ty_fH)kd%^gB^>%0uGI%ag+J&GyQ2jWVK6&`r>AK$hN3In_tfB_SR z0?_m)T4A4P+~n;dzFv7v^x2gy4)diMr=P0jR<3f_C5<>9tv=CEizR+{}FM9w)l&CLJkmGGK2%FE*##QBZeS~k=>3H zaPI(eKBy(sur5KFjFW$1>;4O7o-f=CjZx!|1U7J zuMfs~vP>_qZgw6ZM}Ee!WRhfnG3o6I=FxxLL;mZdxFq|C_pU>ZW=Dn;Daj3bLoN?z zXk4#$BY@Zw{KtG0R*|7s3^x{mBG_IqbLPy8RRF0d#AD4+nh zR*i{bHz;%ygVWAv%mfHO_=|f{0cSo|2rtYr03`i|jU|sVRA$SyX^1?p(F675x{SI1 z?*jZUTK|tPvQvS#tT!*DR@mZv_6^8H;-P@9(CyF}&*Y^65;SJy2LLW6Q9O!J^6q?N z2M}Dweina!)MKCVkHz}0RKfo=1)V`Y1C-HA)yCmi=2A)YR`x|cAh-ORSj&a#gQ9g{ zJxBpduJ%hhJ4grcdCPa=o5=jfO$oR)@SmZ1s9}i!ZO@W0P{WA>CG`$d01&zZ)6GCj zO3d6t2$lHZFN7C2<9aM$8S?*nZ$eaWqe8p>HakE(g%kjGE^~$g4lux~ zD>t6b_ZbLexw&Kd|8q-Jpa?IJgS^iGB_A0;H2y^|qiQr7ZL$R{#qe0o-u|=Ev9o-C6-#DoKo!kjAreJ+N7;2kBy&Bru{W+8UkjjG z0ITxpz zk-xw4XMP9O>iqD@yuD!&*zGY`mi|p57jWNw*M;81_yQ^m`I|IJ6$&znHY)#{bC2f>6;+i9hovsKSWPpfXq={fi-ZOG}Or)&1TBlDBWEV{%>`#!nhfJ}T0f286>lc@2)IPYRu zj((g<>@ZCY*|$w{@)4y(K)rc%NuyGl$V{Y~KqMA1vomEnN(I4VNdf?$Lys%iw@4@1 ze8WWCH&NrMEN0DIH~J};M+^%2b}*s1yv_AwUvx?CH>)>x-?^GHpl zl=qX5;$a5ViDtaAu~iv%Uyb%0%BQdOk!J1^6M<{{F~-kr5HwjD!65Ipkz=gTssxjv zS|W=nwc;(88Jp8LGBjyS1L2Tw0L+i=IJ<+&Y6y4XbcPGnj=}k~!|Xq%74$;&8D>gn zB>p?2~* z#~ONGI!G0i?2mWsI9~V0Ia+PIida~mbsf&=_W2_t*ogRC_Djk?(|=)cnE&GGq4n+_ zG4YzcdX|8qk&ykVPMyo)_%|3k5(WuIBr!LOV_?mL`#U%$h=)x4e)lPR^jFs!lG*}= zR>$M?Sa<@>>-3)6(BvBA5)dFJ=5Tm7zE^pv(s=N`Z97dnAi8rrxvBFhlmueZ;(oIp z9=+svicdV?44iM|ZUYGw3dP?D!n$CIlm7iFiI$2mn$9W*oZj@D_C^WS#v{YmxHSat zZ_n4n9d+9N@?W@L-5MPZ?~2E{-A`MkGr6>b-V@N@U+%^jNybSmz0VqIZ+qN6s%ReB zpWq+VCU@{$InvaAxye|%yj}Aik>ihuo6V~IeuKHPeU>|r=ZB%1#p`a(xq80wIo~!H zcCurQ-R@#j^x)o6H^X`T4~ZmTh1woZ=uwz06)2@wuMWUk9pzI^Lfg_~-#E!op^$kH z1;j55{I5KM#44nM(<0ZVCmO7IptTAZzOK3>mP9yIkDO?dT5w8@fSc!vuttM56@UWA z=~76GDf_;8>4*d>zHA|8%XtCUmo!ypSE zq}jpeHTv!?naWb-@KTy~6XgYf*nsu$+v6$eSxKhupsu%7eAd8Y$kT(53W!o^$*}*4 zmdT(uI$O9sPAg3F^$d21%sAWEVQW`rq1v^iR$0i(5Bh>!{DxMc&P`iAw|4`~8@PGwbPE_r5nbtl0Y*&2v)T*{b9HhE!_sw$;i zL%9=^eR5Y|vvR{GI3mIh$@wh)LWY7r9VdzDo#=4TnyWk?Mt z(OJ!W&6?plOqD#B_mC`Z?Kqk#`79d#VKP0DR$HIyaUkpHx*d=f4+9*Q(L@gULy%O~ zs_wU*AV=fbW6nvP&^Za9$4rPCQBM3JpF4c6?@wltILf6#(YS*&a^OmUF{q5)!#TZE2sZ^diWJDq4j>@_fvH;kgk9 z@*@1k!C4GZSW8$8_u4>wR?e*;^y#1WI-P-S%dNGr3_y@ZF7x|#$z3X&B9_;=(tca<%NzRk*5J(W`zp&%R-0 zyN7Gs6n4{8u#tr%hSU}XodeMs%>}3CO)2x{qp!jE3Lr4vd2-FNFTk&K9Z9Cj9Zltw zY?x`3*KR7bWC0cwgOz5(A94~S{MC?1K6}`KOsae-?74f+YSTD8)+2=8k>)y!FM^M7 z>J6t1#reG!T9`$;1H1ii%bRvaRKHZcA|0%j)mRsbL8z&}HAd$72Ro^vco?5F%>SHN>$*vFTw~N14Wv?U|9eYWpQvpw_JSxSwgzpndjS@CdCarH^Km zB(;{fmfw77%<_h?cP#A@K*pj099kl-AL8J7)Bsd8!_6r838GS@1ZGjKzh{0AGWQKs zc5yQ3X14-0?3{w*Mb1KE(Gy3Nfd%?&YpRxQcV4l1b?-LwW5bYJY#%Jd!b0BrDS%2L zyA=#E!Yfv)N>AtXl=Y_g13sttTq*VC{QC>IB!!%5${5HtitoAuR2vpe7MRn|t&T^D zmTRZW!h6xpC3W_DvcW4&h3(gWK-X(i2Y>~w+I)t3#1J*pn*Ym^eLo%X0IXp;zyhoW zuEOOJ%wEW>12ldr%{)Nv%{dpOX#$Fn1VA@9{MXnxId(`?%5i$UiE1vOAn}sUg~!0x z(MWjSeVdlMP2B*H*u#KY@)IlOuV=vv0?c2L(E9nm*0BOsjHmv2_kK(EW&-`-&qgk@ z%RXxLS*KtduV??Kwpverv>>A#ohm|6QIh^pe9ew=-Ns+X#7_DRvOqaHE5yh~DiT60 zg*6uIV!Ji$!r=he^HbsGdHB(5J+@^o>@ z)%2WHn&WXgZp7k?K>L}2^Z6m*YHKIq8^R6ewzILT&-LD6U8*KPz0KF=0o~paWcb@p z7Aosy4u!lQ`fUe4rScJpjtqgVOmK-rI+;V6%h{STg}H!zOg-_fzJe}75t?62Bq4wK zqj7-VafwbDGnSon2Dj;b^RF~eBZ>dfCQv|f4dby};QcPrk3;-Gs|S$N6&iJ6Tu6KC z=P?~S6FuUSrN-Qzyc?+xst47TIW~!ZaVVP&A>aK_RPKOvAhho{>NjNwiPNk9UTIIR zc$A74Bfw{Jsdqfzq&XZ;OVX^5AFi~ys|KnvV1&#TWO3T>#sgvwjuN~gkB}`Oq#~A7 zH0*9^Jm$0bHMS)Qq|wI_3Jm;G5%+g?x|WMRrSfzz91xNuQ|hv?;JP8%&CRQ@jUkOUqR}csU*}-OIlnEScz7y`ST4IB!$w)e4(>4$E`-p$C z$>0Ujzs}($0R;=x^bHf_%K!ivlP5k1hhUel0o^N1QQ56Jf?>bm_q+Fh0gf!;v!w<( zZ+4)gACV(;3fKl0ue>eJX!H%i*G^t1k?$6X1?Ule(G1&~av%Knjm0XBm0)k$(yLEp z6#&Y=qCZ|rI3sD+Ao1N}V4n96+%hhjHBn^?56eF_RRV9|1q7aCWIn-F*)3eI(b4I- zk1yXd|MF%4V74%0eic0!pIUdY*YgkVS-`>+6)1Zi%}YX_2_hO)eA6L+YYolcrjs`a`^t&n_wtOFR+LG#3F*!B%x7zTND z+sZc@_p;l8pSZ%Y>So4{%6T?H$Op8J-No&ih45@k6lbFg_6olp$U{qdZxBBYn<2qI zIbAY{hJ6@Pb&4P4TawG-nR&$YxQ^Mu^O3Lu(N5%uCKbwM9$W(2s#w9{U>v^TO8XxR zt)Ue+RTpdC4A<@O-zaNk8)t1IIJRP`mA@@ioiM1^8l|MVScM}KF@5X6qEDtH4||+l zfa)eQ4s(`21IJ^7{Gbs^TZ&1$J^3)@lrn2PbU$h@e<>sV)8%ca_SYv0x_w!R1y$TZ!z^-ANVdqDFC#@CnPWW`wk zu7@_6U+P=pm*38}_>9v=nu*EZ$M0SYx|y#hfUqa2_S^(9ltb!~5J~30&F0)KjW#l7 zgVlVcB+PR{{1UZZ)v6|{s<_qsQWQcrquGioZ;Lr$$YGgwMd+xCpR1!W$fwr(m4pes zG6bt!p)OK7g=baAKG9PzxM&Yh<4L@N+|_S#UyBu&P^GIrRM%ANZ72|sw^CzcyOuB?C?PqnQC>)~Nx6;<-yZW4S$whX zS)>E)LR&zFv_Y>6gWteLAX%gmL$j@Is)$m?*+P2?evjMvo@XYVL|bF$^NDvmq)}UY z;7t3Dgh4fn{ElRD4IYqsCrVPenZTG0?%Nh}EEGKtIS2GKjv^$z;JC8|UFSsij$8~N}F$QXev*w}ItRFq)?Bf*wz?!qx1>e69WC+((3Vxt1Wk&eJOw;O{FA=OcRR?ifmTaBG9nMs@b-nv zH0o$|-;Y0-nDvhT%@M9u=c-0Ki2|cw@NG4gPh|1GJ}KT~Wz4z)R?Io+32!nr%U@?j z;b4Sv?R3RbI5uB$j;(MscBgXPj`B1SqI1!cQcxvor=tphP4dD%33jorG}T0bVsc-y zmjS#uACR1-Uc^%=UXNj>V^(&`P>FCp-V*qw$?x-H^y(Z@zUf3>L%sV+l49%2-pKu! zzR7Kni!nICe{_a3rCS?n2q3zmy7s#@fT-D8_gR3`8N>QXCWA{^H*HpqZ(Z%qa*IO$ z;ramL+64*wqPN@zZLfu`{QK@ne7M8#VPI^!XK($NTkFZ}iROcv34dZE#U}!M^r6PY z=*~5pfrzo>Lw?2ac0-$~uYe}T4N&18Pwb%rKMysCbzEb#ep;!#<+T`Ysp-8%cX-X6*$jcwH3`LX3NU(gzE6!v2a;2m-)z(4%*lBMQ4AER0!@aOKQ5D%Utc4gk2*X-aIa4ZCa0x@Y$H%tHY z<*KdVr)kyFe9s+HRJvGXQKP3$iXJJK&GAY}@Lt#@r~Q^pLs}ZBd3?m8s_7`R!_7mM z;HZM*l+(-y40q=JLzeI0E}p^kH2iokW-b1$!OJ!m-JIJt2oDf%UMYNUCwiLQf zyx-Hayk)`qOQZD(*HvN5tL4B#BV|*ODf>~+JwvABjVLeuwU!9&^<4!17=iOswg8?W z_QF?|B?V6)q?p&l>nA~gf$Ipym6oJV0?%VNlU|^R9u-pp@&p_wF6D?SZNs~pBJ#87 z4`|eAWq<;??jtET(IkRkrJ#-AwqAmiiv(y8dYq&a%Uy(V*7z~HeGNvE#MMxe-$_=Xr18ZX)-fSoir0_Fl^?(8?ViAogG z$!Hpd(;vwQAn=iT@YwO4Mn5)Vv|OUk)IZTz&$BjD3fMFBy19e!_@skd>IY&yc(Wnae$Py z^;tleCz~G%Aw&vuk&{SD3;*1DTgWEVj}IOv)HiUh50G&3BqTWy?$!PL^xy=OB6}dkAKFzqnojfgnp>$djKuznEf# zc1PURN-grWoX4{Dw$jMEPSNM{%4hktNJo<&81mh%zw2p;Lcm=_*b`!Wblh5be`u=h z8JBt0zQGs57t$Wrjyr1H?fb$&u=3PMJ|o;i1r_E)`V0Qcf4>~#!86XfoHij8jKJ)H z#DffQoCNnGXIJpjs_z3MiYh6BI!`{b--G44wO`w5tL?QraM_x1kP6Kk$5?8La|?)Afx z(<>LauV0kcJ(p*Gjq~Wrz@ph zgFnFLY!bxQL~cbL#hR}U_7K)NSFEwJu?**Bj1SXxn3h`oI0fogv#?ohC3B^$xnl1n zn|6o|_O7_+?=o6Zc0_I#Tl7|Vxy}VODYY3|2U&P_q${jV76CU+;0SrrHA|UB19OtD zR~_fWJlAYkTe~=quKkQL2Cw5NbexjW8Fbd*?b<5?`1Qg_u|wZ*YNtE1AklQ4W&h%Z zO>Nm8s)}koI=g3dM->)7W8>YuCpfuSW+`&@?nFGzS_XWGV;wxUJ})xy5xJBvi+%0S z`=#2m{TV5$tL)W!y{4ZV=uE$lBW$j5iP7$UKj*(4`s?O}8)2>W~I(7!Q(!*%^ z%zpyO`i8%i!YFc@x+tuCO(_2>F`7a@Be=FLY(p0?@fFk;B;rZ7YvaZk>qYdB?89Qq8nDgO2us>;&=1 zW4*a8Hd+ z$VO<=`EYvZXnSN{Z(lT34f<+)@X|{{AKup1_Kk@e<5ziqMyl?XZhoTC?;~tooiIcjB;= z9p5Qv1FQIg^!kSJ(PKGJc@wUmJb$)!)MA@pR&@t+h!7*%layxaLBVatIb*~iunt4D z*UB7*x5H+)Q6_v@mheYFdbkN^XrHH$%)HK*&{|cKy&^bopghEvoE-S$U1zy4DeHB< zy3S9utWJ)WpEgDu&M(dNXO_$PG+je-gK{VQTLW!azWm3xq5|mOp+Efh3nIeXegm2$ zhW9cA4KU?LP~M$lg&79ylKyBfi(i|6>)*~TfMJiOtJ-+0)zfL7K;y8cKrjfTUthD; zR;_(Q3=~yFt$wNZ)o!|m?e5nA;YG;qVbowG)@~nU`+^=_M3`forNJ z50b2xRFc;T?z82^PX%gC!~BjXi0-Z_2svCLS*bTUPSd)5Rm*#2hOnY;63;pGRHVnV zdxRlE4%feFOUu@Hor1se^V#YS0+415YUTh&qx6!Qa(!Q~W2-&Y^FQ?>VUo;4ssSc;S$gvR|DCD`kf{TjR zsWFc*rB$X@R$-SLr%~bR&hrl~$<3R|)imt^0dVo`bC6|D^0n)0@EQA4cix=n3NpWH z@^`g^ow;0h?Avu>BjoUHywMDML7WlZ5istEaVLLI{ zEv&b`pY#QOTENl%cor!@iSHsRwWj)@V)u15UEaqhRO)^88)lIJlsRIJsvNr^JqdQ( zlg>#`lIKbTLmN3$EDF;MN0njon~tEk zsRukgVQ9$2am3@qcXyJUo0H%C@B{eXgVAk<@&yX&tm8VaJ|FnuZlf|EWvH9JOOVmdXq7S zV~lye$3=FfK2guicPH~4@;BsaM!+4-i@d{wbw4Bz{Sz#3DKEQR69Qp#6eI`@fRJ4B z>`QcXM>XfJH0~fG5;-xHMxl3W4lY*Sr7W8!z$P}Gjl0do7OMR=u%g3n93l%`&ypn6 zx2^Qr$q)`E4joCmRa-TVOj(zG>_Q}rK@fk~<5=;#mf0i3l&t;OUZt;~)Esz_d;6~s zl#?ekan6&Gl2-A1UY4~^iSGMQfm74i@;u6c&rwe&-iG}u&d&#^mtS%}8_KfFc#|&t z1mV8IG%20$p*zcP(zxW6)6i$1t|r-U<{jlWDiz0K@Roc|7Tv%K91%RnRNiE71VhLD z5=vOx73bW?z1_?V?z>vc;A`HlfSYHOa z_-Ped+kW;vLiE5xkpG!aWp&L8en7QD!6rx3xl`*~MAcV!iw}=yT(R5^@FwQcMo&Vt zUc$6fwdHC|5@D@x#$GLaMnxNK{L2!|Mkh z`y-FpDsW#U$)o!u)2*#1r<%6e6JwzTh5uY117)&+ zEP5w46Mq7Me`jSO;R+M38NC^aPg*=<7Z6b*DLbeq@djSC)RbiG92Y@-M}yv2@y+1Z z5Y|L+jS>NAm-cVMBsMbqa*|5RK$hW=k?`k1q!x@Jib(%AdX}{fO zLP)Clf^U$*xHWB`4KUC=`hV}o$O?T&!9pf8fhr|km6`WR==;!>L)12UzGm~meGgI! zoj67QpS8O7>j8w8px(H~a#2p42^AMHqE612q~~t?TV4qNt!^AW4b6N}Bv*{ghm{xK zht3LCtb#i}IB=d2?r;(fXysQlg_U4H^G+J0*(BLg@SlP|ydl@Cjb^!Y+*UHQ;p7C7 zUds~rT|#MmNx3wVR|Tcg8tV-yp==c9`#|s85CuH+le=yCS@|9Pw>ApdD}t?>qH zXi3EHrD%$+^)Ddp|JM!uPg4ZEbe2bSf-LrHzQ7rw^Z6}=1V^^=2?LWXEp0qD!_JY3DlEVE2kCFBbdzh5SdjL*(Gu4g+Nt{Srf$dcWSi6BGJ@io@9X5Rgv` zk98mIZ5bQ6md`KMI)lzoJ`T=@+vi`nobC526kSNO~ zsGo2BTdCN9XoMm_XdSK(p#{!Xys4_%8HPEQYZv!SYn}qcIu?VC=; z0OQ!^5#F^UU87zjNE7>z#FksrTOaIa>;LPS{NXLy-R{zkNmIjuh63PbE^gp-emS)8 z{xo^-bdUIY(mQAAbhESRVHnyQvFOo2JCtAM$|j5W@dVx(O%9WPIl!-a-!G)R?6FM^8tygHDp(8r z!rK{Ef2g6}vJtmE<~}Y$c$U%;827XW@$KvJLD;le^9B6b zVWOl*L@xZX1T+@iHJcvf{yu8NyRBjAeq(hQ|KmTJ3q4gKz3^M_gIT&&ki`q^#!o7y zYd`x9@70W%J&W7Sq!iEAk6v;pc+v2uToAkzYi$Nb!IAYx9cSQ=uI5ah8VfP=cTlf; zlrVee?Q*FHikNYOG)O<+%QgpqYpGb>Iqh7#sQ zK<3uF{Ks;m_{`~|7K%`znk!-aM6|^7(o{cL9~3lv?tw$EJlK|7s3Ex~2HyoDJQ$Z4 zsx2;?HhHPF4IEG;9?2hI&(?6PAkwU9<>SwQjbhg)w=w@G2J$6&Hy1}_=Dw%$#)BjPR*0YoQg9`Vr@U2vD= z`~{B$K0pB!X3gn4U{s#L%GmmCqx*^7?c;P0`!< z*s_D_ylA>Zy`6$7%Qb_H&qR@raffinO{K!nd=5$4dHofIxiEcp^w(FF zwyQn5pQBFG`&GN6CS@pYY}S*L$hfIz)K^Ui3@PO+m)d?Wr~PYb!H@h{$>VTDUY3hl zb2I$|-S=|ND`|EkjJBI2^`Bl3ZYxc*mj(})nnpx3EgJHRYjY0Q`_ru1QTZ>6nqGay zEuR`p-P{9QX4|z}bPcX@f9}7$z!9a~BVnbAMS$xMULft>^AGduWl#i+>+;|+vG$So zm2GMw(RDB9d%enn^@!W7X z3HnjdkEf3{DLgwLr#fB*>~GX%q;sca5&8&ju~l`hxWC$VBkeAjX0j8*4!=L)n?w%?ogMUYf1er>gd%%6*#SZ zZsi9MZmeJ4eyDJDxve@n*05|I=3256RpD96IkF@Njl5WEZ7j=mad$H5Gi^0douMrw z$%#$((7iNZ>a(X~8-aT+!Czx?g4cq6Cj{=BIRiCy9jhAf+k1BeOG`!Nd3oO;E4JRX z$2j^_Y?)PboT7J*v_88mrhI;1p-12Gz*VF70%gBc#-285{QlznxuNOgre##opNKVs zE5r2V<<>hT?kL?zg9T*H_WBgQ@(#v1Zji{jZ3G#guUpHrPJGjyMu5ouNph;dO8jaU zUvcbjSAgFRu5ny$TMFD=?N~O>bg>I*I(ObN?w5`5iS6QcgcRBR z>y7>A?~H=q^zOBS_fniq+N~v36?!6eJ6@lC?)}BJyk4JS?H7!blKEGiYQ{pI{Z=ov zM(M63wwkiW*mYe`$Fy{7jd_!xqxdi48*r~E@H2UX#G(R1R=`Uxtr2^RcCq(&TVSD~ zbzNS&hsAdGlww642>g!xs>z@GG=-(!bbk2*zU5c>BReZSZwV_y`s2l+TJxz1kDG-( zS;USEq0vkaJA$vr#o{Mp<=76)dyb8Np7vhgXw_RBElj0gZ;vNtrn#uSdv4bZ@UJ9Z z2k0hvHktq-$I0Z)#9O#y&I4}A&Pv|68izKPDh zMJM*7?BMB&orvCfJZ_7+AJIK!*$?|(&jkMYb!UXY1mODW5|OQe;W2-ec6FrnzODR# zhq+$FCW~(_nfvUnt!-s>s`v&0WvmC4-<e%5EZV2; zr|0&Tv186Ne63F&4+~mSlixb!_QvbTidv`^`r6!ayht67X6i&3=hPAsAu95(+fBJh zagS%O6`o3E8m#a1@hJ2UHlg17JzS2s73Nf9ODwUKK@I73GpJQ|E<+0W3%czm%RMdZ zm#n-mpFRX1QKNTm)QS}rgy@|%faVrsyRuwGS|mzTOoxie9ch#}mOa>R?LkvV?@pd8 zxzeR?<__5RGWjuFSulC$u4`Pn8k^vIUx=O|v&F=EP73N0r(W#?M%}DgeRZVKD z9N#nT$C3%Nj+k?CmES+k40+3|-9P{0^LPIK?6oyk&~`m3wOam5)A@VklBs`|z85X( z0(v*>H>sqTU3|(fawg5^X`ymsIlUAT4VGso$c~v-xGevnxR3HtJ13OS_KR7Qq|DTF zb2u~kM(7dkNcE>#2JT1^3ma-q+gaxKpGE=>8~m7*<^yg?XCiVf&SkeFx?KQgWqkGg z@r$8ujlO4crS)DFKV+;*yNgvg`su-f4^^R6XL4M-kIB_p^q+;K29UJJIx0{0Y zqumG{{ZM}xb!@_nXc}}p|J0ntc-%|kr>66)et;K%VZ!MP%Z~0s{QrF{i|U=vi)v~DPAJ?w#M#gilNJf9M?f! zXWqd&-|LgT{i0bRZX)M#>Mz2pe`EnjxI$)B$Ha*|jcPu0LxS+Fzc_qc1#~0!)AHi3 z84EGKu1qxSwg&BnE>M3$YpRri6f?^^ujhvp!${8lbo+5m-wWrPG*R_EZf>^US^u`( ziWL4O7IDZ{!~J>$VR${YI zsbE|4Xjy)!+IgEcQf0eP+EAF^oo{b9#;U5u4rk9BzTo|G#b40lY9#|PSH`5Q>JT?D z@Z?!qLOYoZcQ4okW^!;lo@TDM`EwuT!UFmTGj^Ka$146K+ zbNZ%;{2R(F4}<=ngx2FcxiOxZ4uLi6o!;KaTUe?GH>6`%g#a% z*ep9we~{u>Yy-o*{;*grPCuPGchqoQ?_Vq_M1BuMx_|ps9<3{w$g8pG>2d^xi>Zgr zWE9krfK`ncnCT%uB%J-wNx- z{c2~kLgADkk@kMLRIO?Fv&3F9_e=bl>}DD$G0n;95~a5M#6B*9d*SLTJZnW|Rm>8O z$iqgA<5-(EE3)})wWU;tec45G@!-0-sKxbW!FILZRQSZiJ_;4jAk*Di=m%HQ^{H7F z|HGZ$k!}*ozE~vQ$^Ex>4wlHrU%)V9llx21SOoiuH4u(sULfP%v+M}gEwg|l(Lz`o zxC%sWUtb;85AYpy|XWyix zH96+Aiwvj3V&DXQ>5)$18q*|ipI*LvcW~l%&wuo7J(M!@+^OQJ>%$cnDy@zj2Z?t-IqeiM zY8Vg`&CU;$v@OtTVG;vk{2M3xQT7zwI`f1SX4f$FCiDEZIUp^@{xDFLiW% zgNtIO-r{6s-;ZnR;$`f`jL&At2ndnu&C1oPx(Owdt^sW%sB%>FT-P3W8;kb$UmXvX z-pOk>SZnui?f|IM)u-|-N0A#()^ z`D=+{^FFvw+1A}QxhNSPmqS?Mu>}#|2LR0<#6%Lcs!1k`Z{_Ga|5c)$CCmC0kj*(A z8~tT@^Dw>ls})Fv={fh`pO|^RZ}Lv|<%P#)=5#=KSutl*^*|`qF4%d+I^x*6{lIy5 z?bVf7v|!ciu?)7OT~%vlZuHrx9+&BfYU=hO%~A_~Fp&<-%)atxz`Syf&0ww~a!uRs zm2)nbz^SO|mccnC4b(LA^V74XFR|^xs?9U2N#A9{?B1CbVbV_>(@fVO*MXWFl-Q$+ zmc`O%>x`zY$d;2RMqYOZ_KuEwH~dz$D!)^LC$sgh_b-pH#-~ktI$zBl+DY&OEMCP= z8PIBHC&W_#(%j-qAC1Fd5R)qWu#)RbbZ31yEaWKFWGpR(`|PRaS$|=<0j#{M`+92n zVzZR7`wIIsx22?!LLwZT(qP)YrH+P57d_hP`^xHB(X!xh79;264_+FYT zU5F?%G_^_>>1VN@m4(N{p(V)h`hyA{n?lKtFjD3o9qXFd7_mGg?IgCL!J2SdAc5f= zn!l*+($c||ySKuUws*}jzB|LH%sbSM$5ZZizB0JPdL*eN4d@|fz<#(GoBiJs4j=A3 z2EWsEAkq*#i*QRUWCkacC#)x2qyP#SaQj5+dXid)1q#taUwjW`n?;BFDHz{4;0~)x z0rxV!{^u`BGfW^mqye036JBC}Ib7SeFbawKcW0IlX*2~?Axo+0Y8G#g zM#8D0hi=P68rj98rk81X(+%QPo2hNs2-MFr`nJ5_g~JP|LG>|kZ60G{Pkw8}piK)g zGf>U$us8+?yvKB~*~!V&M&Zvpg2_;b+MMkBUa|J<4UCO>>hS1xeWt~-G{Rt+}sP8nt!i?mfE!AqD$1Yhnj7>wAO`%5augdx))5gB$+H{LlosvEn)rP$tW%};x2c(ypB@@7? zC-qH+xjUs>k0qrWk7ep!5|SF_b3L-CZ143WkQN>`!x`Mh>$&rvYD3P}fdn$# zbPL0eVM|aUqk{gh>LrFAPt^x_elX17DnzUun^ouLMkMt`2XpMqK zjQUkdE8)wb36X1e3cuUFHd)hd<*LJmg@K) zmWfkScDkBXq3tj*(yJqh_*aj9s4R(BwB4kVO8|4w(@9PHa^@nC2!YM79);UxT|f#R z3HPV6)kq>8UBPtMS1^xZnZfJci5hi4a~?T_#HeN&yq%0K7bmu0 z#wqzO!B^@~wf`Yp8Nhkv;p?>Etr1=HtD4gQ^#~VOo-lgY0dbzrU_!6W>NJ6gJNX#pEi-FXq}{u>1YNx zN7m5chNV^{h1$MaqQD@o_PAkK_CXuQ96G?*Hh&l}f^fYnuKiiRaUmGpyV?wdK6Lo` zA7J4>2@?JrCnANeEb5mD3?r?q`5euPOG{5uiQ`x%R>jz7^tu^-u zhBST4pRG8M@q+mN2?_xrRK3j&0u^P0;48pmsQoja)*9^`8-E+WL13GOo>)Fmj|?xd zB{~uwnme)W^3(H93JTf<9F2+5kgAlle-Mf^5W8B21g}Nds4%IyNwOKc(CCOZhr)xc zJA#pzi%%G?)7{7y+V@JYzry^TqPuzfcl22~U+;)W?D}p!49VJ%)S%@s;Z9KR%v2wV z>{^aB$oApD{y%+A2~=NTqAjI-O`P) zknW9igOoIgbV~^mqBKZ%H;71gcT2~2vrqibIs3ft`^_-pxW>U}-OpXuudb{Wc)hXr zf;bDV)2$(nve5fWt(Q68c(YFy$SG4PCmlomWp5c2%t$!evR=%2u;sF^gA2br)2pIX z>@b@e+X7fK&$x*5q&(E8`u~CP{p&5$2uq5>ugQx5Aygw>Q?KzYIPLkFZ7vTL*>DP2iMu=FL1cJmQ>NybERO^p~ii!%-4B`8^3E zV6dN7MRe>aW8I#DGh;B%*@M1~z^_$+1g-;tVv(P@OHg{x8v$jC4=92_JgqCjTW(4P zL#2i6HfHph|6m~6L5>EUFI<77-R~qPIASn{@`dI@WN%LZ@tw%^7PW$^#%6>tAFXNk z+93gx6^-!qt`OmVWWD!RcBr}?OWaP*^1t4<|8;mCr~SDee6=Nyy9CS81FCqaG<(oz zXkz|nrZvX2+;Yw-5NKe+??@W8V$vak^btkhpTayoZ# zBkBA<&kh$Ceek>%a-`7|U^VRef0modq}9|qQ}|<$@TM4dgiKep#&S>@3&>Bu{{F_i zxM%PH&*Xb5qa(xH%V;P`CFL`%x@h6^6#l!j zpRAPoScI;3KwJa;_DAnoF2S!{de57m-8lp%HNbH+K`KpjwHv+J(mg|*LjX%~B9OeY z6Gh8vtAK3D;+rSZagxhc=w8l(g_gX1XEQ^hHMU(V-=`@PS@4frDjqFt(F)`Ges4xp zkzvikvu4}FP?r@e=s#BfCkE`Pl{)vi`mP(>p~c2$$st5A<%^hlFW0G=@$=q(tN}NW zJv!t)oTRq8&Zm3?73Hs0>!G$GeQikOHkZ=j4aToEYjZ~^e2=r!l9Mg1 zmi2e7YFPtiSx;T~)I5{wz`tJpTMs_Gs}G;bu+|>P|J!IUQGK$b(tCcU2bK^j8q9Ze zHWo7;{eO(~f1W4*Ty$cXBc+1SOacbelJF&3g`^Y^mzVP9*SAzgjrj-kMy;Y0V84qVM4?ZQ zF~|I0H~K#Z+<%_LKm7{bpvEr4^X!Mr5J>SE0+2wA#`wwCwN#L>t@~{WShQ0|mBGi9|MR&UsA2gj1EasXCIElWH+aRc;MlquLhs%{V%(u zrGT%p*eeXgl%;{QG8Gk{X^n+kN%ns^p#J{c`2Oj&;V%wqSJ6e+Kz$E-?tb6|B`_}t zui?v+PwYo=%dG)NP#Pdo(unPcIRGv&B3J)0?EidjFIZUqa&$L;lb^L3%KTjmubaK= zZWvZiK@r)lnEv9&?e%VHzF~{6MO!<9#r9a?7BQ`qeh6sXW$6Z3TzMp`=N;M3@3w6qO)h&Us9ZJ{6x?D+O#pBi9VWax4FT zM&rrjTTEurF%h}htHjfb`JN%*Dz9(pbprCqQ$b$3j?-0f&BB<{Y6XJkD)YaI*#Gjd ztz@CC2BKhJpU_Kq3;NVP##Bx4z^65uGeKIuzxmbwyuMrG-}ut0NC>pbA(qiq_ALoc z>EjM@Ptn(3HWM-I-Y5Ph#GsSnf3Ft)xz9c>(gq?}dBj5# z{QMoJ|9uERb&3N1sb#>&zc9mpc>*IsNwK~q{i1fw|9h9@5~r{v>5aV_5q+>}zB||b z){`Ad_6Aevs8!Kr?1dnazkTynxA*08=mr3OIlcD3Pht+;`90iZ{fQxi+pC>?qP%^< z5$V@2@zcPOJOmtILskVD8*}sbRSb?w^!9F6i)W{F0RQT-GYpfcKfSwHOaq*|T{8-r!1T zaWGd`xCX!oVCF@^X5oT#C;1Z1 zpDuqHB|w~-LwbpcZ#UBH0a=Y@^QPd&^=p8N$V(y0{Nnc+Rg)pjGjn)t|04>k@$;;%aZ-#u>0%SOz2Q*=P8RE0 z(hCCXpN(7(fF(b6U<$J2^-qGhlp8%{xv?qyz9pCI6wS7p4DYM;E?7PDj8Ac)Y@JOyeg^g`aO?6*9-01^Q|A$xZj;| zHbEzi@3@Bb0hII(Z>avVG-F^M_p3#(LtzbY3gBUXAbUi(5qIEajEUK_q@ZfH^Yy=k zjyrKT0P2l_)b^~pFtwPYpyD{{h4!qKLQr0 zQEv@k6fA(>3b7sNKK!1uNI!sYFhpz#1l=_0B0lxs;b&_dKNqTI2W*dKOZuC)6$Zg6 zm%aC1jpRd5(t^hASB{9}gOGJMlwCCTMk7vWHm@N=?(Sjsxe{?@z3 z)8R@feyq9{5OnYp-151B%Fa4i?izG{_)bt)`-hkV77YJpGPqo)GR7CAB!1I+4OeDd;6#tD`De2N&OI7(6)vTH#5uV zlwGGHw<+Y4ew|ZZ+gGACTR7JM&@y{wr;q=waob}ysqUsxOmcG1XW49#l)+Dmz3=gv z_hQ%Qa@i$_v}?c@86E9*pu`Tf&aaK-`z-`2S;^UTABqv+aV06jieFQD3e^ncw0*ia zcfnIK98OMW#OJmL)unIrIzt2Nqw!IRNI){(esJn!eP55hJ>)Ta!L9g?Ou;ooC$XM?yZlzvdcmE+xIPIJIwxDD? zaI40l@DbL(A*F($``}iUXr)Jv^IKnR^UHbnDM$FOX=|_u>LgJB_8r?I?F{GP!=n~v z-|$-Ngb5^bG7TuYD$qilBxc3^g6v;`?9&>w@Ndk`X<%7aj-)u)0gV2QI=>>{%t{QU zq$0TMvHloxPqs(ABahEKoAi!=G?S{kT|8jls6PDVxP3>Z+KZfPnMC*r9UjX}9UQj% zPu_HfVOxOa8i1-hN*NT+eN$MsZ$^2%Rc2z1SA}816MCvlY|YK&)JWpKa44M2@y*^%S7|; zPLx(!Nj_0y3-f?)YR$Dtt=QPB9I4Jtj@i4fx}B^wq_CHT?d9p;LtS4LkrKLEZGRE~ z;{>xT;`(k0G03e43x~W7HCNzD5Q_%Q%Ab2JITs_OhNh%niK^Xyr28S9fF>IfL&+;`KEWQ(w zv4o669vi|WNcDY5oBEbOdlQ~UbisToT+ z_SqPeX}oq7Z;whO-{6M0XSs!UZg&e}^bK|r5(Go?&WL5_8cg8KkCD_h>G=L&hg^(_ z()OjDKb=^}8PCZoI9N!zf5}9&_=H8iYh6Oq>vI;4FfGhN+TnyReR8%8ve>oK3E-PD ze+5!c=E5*4K6H)jmrCLe9tYNn&>S>CCc&?X++Cy)rc?)!$M!ARxAv_KBe@6{o`-}i zdPcJ?XE4vM3}O3T)-W`Ab~i|7P>z2#^f0{IIL5HVDFFBJ>Vx>LZui9V{vgO=Hb4@l zQKYx|wvZU<16<1z^cPrL*xnZZwIC8Rri}S+&H*P1Z!Lt_6~h5(gBoS{-CyN{Q(0%% zpy{QnMxDIK^c-j_A*dR-X8N;G51Bw83M08zh5~^TKrZf%Bq@jsJ$rF6=e+M6MudXJ z4@dd_P$QEXeVq7~4tqu7gRxTQ6806i%*9E4Hy(wfB0}Shg6c^GL+@w1?26zl}V+rjTd@(=&4ID?~PsxOKPba6!l84oaF0^Nr{DpxzcET zlla*6t8NX~C1`JX-XGaK2Qiy`sVTs$&FSi7fPE2DyFX3}%)#edfezY|x_LfgIqDY3 zg*FQsvSPhtM+oja3;4X!fqHLTUQ_@4WDIu`#=A+_YdFJwNRyi{n_vggI4gt_S3W2A z1c+AAI@3DdagB_X<%v){^?FFr%H0ld|0o4;89DCCLl+$Phm<<}hSzZIU??zCb3bK_ zc8{WPiIg;5{Z!a|O?T%};3*1W5w?;+KFqkha^3wQG6&bp4xEI7#8OXBO3M0!-8aj7$SQrRtH>E^*0N>(QojQTfy6&xPYbaQ zCAQ-U@(H1Og8xmtiinjQeOR_$uZjJIYmr>J{Kw;s53maIl}ZnntI>R=E%)`Vzn&0( zNMwiIrVjX3vPZz)d|#H|B@NSw2{+I-J4fb_1w$(;YDKf%rxvDz_$3t?9`DMOGBhlY z@`AXEy7nCe77u6CwZ{l9=r-28!k5{yR~BCJiyvYy5T3ugUFm-or@v%-E2!`my{IdI znCoSjVU%x#S+7?iJx=#EfKwU#Q*9eiU)quX#>!>dsO4QQkfrKHQJI z){-of)el`~yzq>MOwzt@D&JSp%V;pZ+l=`gHk(vl6o?)&xF!PaUfxmBE?M14cc`+Z zl-2Q#JR-BW$sD^7gFk5&xjpKbs7OY^F8^w9?b>`3f~#^}JW`f=b6+;6cda=0%t-iI z0SQ4HaxPvF4eT_XR?m6J1^v%+hU35}^@0E^RicC7b<)X&_ObXIZ_yNJ$Lp$aK2NE| z4+F>!9m3c+6ALtvicu!Nm9EM;@)tX~h;Y=dGZe>$UZsP5XQ!d{f+EdcD~@Z_zS{1m z0Di(ns6FnXh|jx5xcg?F1N|6#ai-;GS*k7L!i!H^I=AVTQ*-lusO$t1boz&W7`la* z{tvIasLzOalw->u`6bboSZ9$=#^Fz%SqYX? zpF%%D2#Lj)e>Npc)qHwh*@e-bA<)|ISuS&M(qSUf_tIVBpiZie#sB4GtpFBN^c=2D zUa_NLuX<{3OxZM9$Bv#X(O5FC?3B%-##jd|sYH_7@EXz`yw>TZKw`zI$jk8AXk>r# zf;6Gfvjnc#(3tr+!~A`<6(rIwrU_a-D{5bq0l~-BdTeMKtLR4?uLN<5X%92XRQ9U; zeF`+cKT)nF4JGT>6k7aW@w%#HG~7o7t{jAUIr_+syNlC}bKP0cGLj!Q&1$v%eAq{o zmzjBMyhD({e(}d-6=Ts4X({rObs^;%uDnz2LP+SLWlYd-LviEZng0b!se1`1uaq`S zI8jmYR#&>UKU86kWgFf+Ii7J{+GnC+(l_8<5 z*C!%J?6!(46=t+8M3s1{VKJlLMlh!xqff{hqI9bcjdA7UW7Dy#?OKq2`07I zDDQpwM-uJUR(((9bpKPm4Nz2n(r_{Ho@C+;bh&|6OwnQ}jWK^{@X+!1=n;;tfFDE& zl5CQ0;V{%Z^FvZc!gvhP=^c2FFVTxGFDFWfU`kl%x z3;8VuiNc?H2AREcfEVPIh*!#{!I|)@HE*TgJ{=Kl$zV6(SiZ8(Ok|M4N*cnYS42O9 zVX+D!Er$)-gJAO!o4$-L_Y})*JnkkbT%R82PRkdEs2%uR^zqf9QAg3vf1-XH?!#q>M&|DHp4HOPaXwMzJ&6y1I5bDu{Ns5!WP~Nh-up z)NesXQ>Y!WZIa6tv!a9cd}U`YtJ!M^{>j^SxMqx6D~-62A$4uLOT}T@j86j5@>oq3 zpt>l~$)>QyGl9fbW2z~%bOs@tF|IN0tI0NoX}0USlj`S2Cv=ZMqq4 z?GW{C!y?9o&QzUZzZxIp^B%t#d0L6x^iqEf;(QH7X)F5<$e(KKHEcQaL;#Jh6VRqiX!eDv!Fd}^k98DpEkh87B)a+6@6;Rojf z1N0ASv_2O^)dlLB9^Li1%iZgKFD}Sd{rY+g$->!3*0*-hv|BF|A4=CB+CQD_2umBe zOHx=57(L~vl`m#z8&yOcJ84DIH~3d<8k_MK?jml=I!H-s`V&4q2eDq1wy3WBOCPtND1tpy zPnosbg6n%it#Ib&p3^ojePp%s9;a}Y9+lx`Pw;X&+r*HA$Mz?G?HkjwL_^+LE0F9G z7zJV6YRg5?szlyLFkGpN`y#N}goaaC3T|!-^b6c=@!qwQW@!VkVKm-PqCu$L6S)XK zOP26e8hO6{u8n9&d2aN(@6bhstCf>?QZKhY$8SkNWd%?CmqH*Rsy%)j@hni)?#H9K zmpc4Ncv*Ne125d#zUP_qhu=Ja+b^35yzneF@sh)d?diZ29`wa}GhSre@uT#382+$~bGJ3{22do`}bDWKYn8Z2cS0I(Jt*2`0izYSBHn}KHS zNhKo-3ZY^9HXYotE|878d|s!y{Z`uiCOpo%w29aJSf>4w&Z6vPD2{pfU=cy(A0|yH zQR++GVnmOZYZgS-*HvYcF$fSfaRMvecNd|T+(4ur84qeOG?Nr z$D;`cj2XE>yrJjlax19yKiDeUf-iG%rC{qk^Sm-y?GlKy=W}&8V_J-8lZwUbWqx_8 zdR&n0!|dwJC>qOZX7sEh9i%oFG+N$v1T>)P*d2~UpQXAMkjvH>thjt$pvdSf4i!E8 zVttLxOIoB^9tU(dwwj~+nc>pk9`0t?jCvz!_E~AZHbwFXt#6PwP=vp+Z=^p{QnM9i z7}8%h0Jz14ewej=a3swB`};)qE<;i2Jd#5c0dX8|qX38V__OJqmt<$u-YOl}vF+Wy z+6q~})bcPH6O(3kCyV()?JKubm~#-d{sG{>hxoHDJfP^-c(fE>k&K-_Jo{F{iEM(d zdDVNH=#~1Vmqy6!h<`KPG^)h4%G3S4VWOT8j`I9&*)KuYzUf!VT)=Jcq$g<7%Q}7C zBYOYi>H2ZYZ(uAljnud#M`=%dPE3BcO8!u0tfLk-45^E0B^u*oL+7eDO@wNCpsyaZ zJos84xq@mgW!A1?=pO6$WXt`pr#tgb1FUX?u3hWeDfrCVP`Ia*nbO)?g7r6w#&hjN z*I5s@1;%i${gV8>p|8UE7CcPpj@~?zHcFPDpigR3H$miSm}w0Gc^DtRTL1w{Q6h7a z?!7qUdI!_pLFz@otbQ_pgamQ{>ypNkQYwU;;>WJInKmI zw&|QZWl0mh8^4vdv8}btZZ}R}LFU{3k~gp#NyI0^qgdB1Fw;BTe8RJb>c*3i`KAA_ zoNr)F*>wW?_OkX3er0ncm8mD9R3r0+Vsg7V1V3y- z%NeLbB4T!tzfmwMjF1TrHFx3e@v@?9t6@(CWA`!=H!`o30e0Z@pt| z5bi_#y=AbYezl^`Hx=XDVk-c0w`PVvSz{uxKVYK4^EGp9dBXYFJsyhVJ5uzD)wKlj zE}@$i^?tZqxy?}YKr>*@ca2F!N#jBzCA|$WPPK1Ra$^ z5m>2=%S!Mm(aktkF?yHu6aX5&Z!ur%-I15O=S+RYQTSM5&i+bG=qnA}RgZrGePXKT z(uP0?9k6ul>e*2^C}e;I+@hBd@dp(ZUp1-YGRb|4*lhn6eMwA@U2# zcdx*$E%?aHZh(B>kxQm&R!pLd>XL$`tp6UL;&KH0` z>twYek-Los@gJAXCxBd#-m zaFXDXoxp0b+2`vs6`|vxd9yT2BN)!XdrYA2I*4jD$P7Fx7Sy2B$x#IdLxxVx_XE8^d#j}QGPs>s8M4$@({3&qmU(+@ zPgAEC0cdB*UmolhAmq#90C1BH9}S-PpZzuDm+oD zYmZq9j6#9>s$M6Qe+GhSuwcW9=ZVgl$v6g$P(X;Xj@D>;(nXk^f`2;0Ehxk8u zZD;W6+{ZvuBUb;XX2lKEE`!o!y26c&NCASVv;ofkU-QeE0nt2Qb?a#IOMcW*jX?qrG0`&9>MKryKUDS1F! zt3oq}0K`@#_ud#v#L%-UbCD2c?Gi;rMWsxkX@SxUTphvsVO%`d0#uw3T^w4FYUvCI zgBU&%njS6Ku+XpQ7WbSB1y{SEarIHSY%4n^x|!~*o<=e{JesO=p-4+I$2C-0wptF4 zk@NEb6W^^_I`%&OVq?nOO0IVa0gYhl5Yb1YuA?6zt_}U!kxw-U3axC2ZFAljEEkk* zXpkV3&M;526aY3QgQtf77{$jr$_21EuKEZ^xCzvbaJ@5i&|Od7Ap%%FI`Gg;cA5!=QO^AeX45B^Uv zTCUh=dF3DVj}pQe4=>o*L^fWvW$rEvQB^=6I?#Y!Wbc^G z=V3Kkw3^_dL4deXK|6ryUv~SyU(gy@9{IERRJks?r6owOoXy*$>-g}De=ObidK*z@hYD&CYY#4R zgFsfo8a`M7f@(FoxrbkL)WZD$3P9b0HI{%UVm9OE|I6z5T?E1@#A-kR#Dzv2IOg5Wg7-!6fuqAljM`8$KO% zn7!VeZ>Z4^;Cy@bhcn<*b$Or%iln}Pr6wQ@6Lh?fG9PMim=}6B zpJGJEQKF8gh4GuLO7|3BSD49>z_rL-5}4%2@U}-^yUf0o9h)%0Vm5*QCUo2eUk&l2 zG9u90tj+fTY$~e(9T8D*iX+2>~FNbQg@sbvWT z1aER4*1 z_pHk6wIU(wcI?fUIk9~r3)pn~%7A0l9NT*~7=1EIFuf;1Q`Ce3G{xz_QgSYwWB*x& z6g0uIwKnl5eYF|qJI+~jm?vzy512h`I7hjL4kX~K*)NKK$i+9uUj*2r-Ko;WE5{{y z0o~xDY|vps1R;s}XemlI%W;_GgS?=0yF=oUn#?tMk$WNgLOD>lP5Y)bXIgSGQdu=# z?|iFn*`F#mrLjBfXjI6JkX=NruJCfz%)lfS7+;yIv{Vt)>GT?u!mxc`9VgY^th1$+ z;F>Arj`6R0w!h0go*?55@SvmbP)Om6T3Sk~kTly+SBI?Mwi(Q>x>(V}hH!NVzW7OAI!DkWFqid4Dufwr=Ah|Pba(jZq3MN*|CgYkN(Sd@5X32065C!?m4%jzllhxkXXFXp4s z?CN3qN$J!Hu}7CVW<9FOs!~`ic(I_>xy&`!r)oaadsT83(Yva7A3V{K7{0YgyG#B6 z25|~JMs`ue!x&NIz&A5!0d}>Atr?-n%hS_%%noM(Q=i$uG^dFyqYk2bt46=~7Bvcj zkPuG3AK2I?I}`B&1k7k0M3V|mbT5B=n{cVFbpVtN@XThk`*8v=8psj3u*>qgc{UKi zn5qC0*Hsh6kJWZU5A{@QGEZZmF};dxVSZyuBz!tP6hxH#$F3MJ)eF=PVXb0#52^Q4 zQ84ImDLP|mYQv+%WyaiQYedo4E!2@EAS;pXO0u-=FPjRMLQ5bP34!hzRr$(&lNp!B zuKf++vWso}xjcu0QuKIr=}?J3orG$aBFUbMEEKSTq`r8sRr)W6J` zA^EK&Xc6$Q@~Qm(zbe%+t@Nrs4D9xTC(vZ2`dXa!TiC`_B&pd8OY$ z;Ka<)s_vfOqE!RvVD~+qU0sOoFhvj79Y?KJKD~^(cJv{ERbwI@h`Y`kDB_L08`{<* zx9xLtelCtpmL1DJvxD@V6;xp@kZ$W1>c)N%2*#sd2tvWN5gnlNS*0`C_k? zevqr3;!2iVy72F!sK4ELkRJmD6xz`l%Bwp237C8JrL@kJxZm0WxY`L=0tE^d z{CMOBeXdJ$qo@bfA~Eb^(!nR#%to?Eh#lx!#6+NN(~CqQc*p^VN0e)PP!Ch8KRXTH z2sUB|l#3dYlRjCf(SuVm*C9WMAI6we9U*Pn;(Nzpg~|`B^Q1h2mM+i(x$4m$LaHpd z|6IYsXx()?FZuOYpuneRR56R=2n{Aw5b6XBW61E6B=HwgY-^?hus>k6G6da~bsEIq zQ-_kdeYsj0U}uqIkfNF>L2sl)>?LtZsBe`=^H=SB^9x_r5LvAsm6*Bn^)oiRr}xfd zxfv31?_HmDGUjsU!b3+b7Ccrl8a&gX*mSOP0eQ^k#)Misq&Z00L+_=2@!Lk!*KXxR z>|rxY7M*?J+hLObgV`u7nB11sk(K51O8v+eZ7b)or1{N+Xj0Qc6%O3+w}dq4=G1I5 zVfN)SDgkM^%c2r*DY;T%I3sA;VW-&%lDE)8pmZl>13}Vp7YS8*1FkGy$-YeqvsxhG zvS=s|c%-Z7J^9uQ=z3nvp;r1u{NyW%Hr8P1t$SI(!-esbx2*4q^?9Lg(aiSER!=I? zvi`flXlr}O0krLB0au~8;ptHa)@)yHt}B_=OWB?;&B-2H3$wb|Fl|b+vG*8vsSE*S z_z~XMcA}0XN$zt#+sIbtH-*L4$7kOKxSOJ>hRrP;=cDLU7N=bXA%Pu zQIUfcTH87S<7q;yB$%B=h|+sUQFP zi@3oj!HHg9e?40JF#C2@dq^3?l%+HjUP{lM@cwyi_fw*bn95;+f&4ztXKY}h>AXf~ zq*VX>5<>ye^p}$U4`m7_3a{IJ>6IYT@E)vXB0SNrAcAZL&kTV|t7a~mIxx?sYTG8r zJ*5u`zJ^krOb(Y1$*B}{zC3S=22ea(Jd#AS80K*^MJ~OS6MDPS*b%uL5;hg;fVdvV zC}KGSK3B@M8KVF!c27efji(Aq=mC|Y8S=s$+BXfyS8X&wTjCxrxJ`A#nkx>>gZD~1 zoQfB>oAKja3E;1XZmaV0Q)w(uK z-DFY*9~W1l$lPW(japW6!6q}FvB&Jy%8RWyM1FH_PF#b@9t=Wb_PCT2Dr#vX+g+tw zc#qdy(@4EUW9C6*=e>CuOK_Oh5-qD<5ObK2d~M1#`F$$-9qTb4v*JZg(diebZSKF& zuv4dx(z_3auUSkw`x%o}O;Ea*FxuJ`K&YEJ)5S_ujE%29MZoiSLMGdSf@P9%_3+H_ z`N(UGu(499*oX<2(r0LEIWgHM=(|eEMDM&4{ke=sWzK@c0$PpU?aOz)$1g_Ux-he1 z-&`RJaPEB#pQ90Urq}isZ-6DqnA@RX4;mz~(T!qODHsznhZB2=eue_)KfIL15#x|U zlJuI&cfOWgD>1zR?twuMnS>T9NzJy+s@0I@*XKuoFPdSg9)15p%@y-y!mQz@8XJVh zrx1?u^IWvR%!?ZWbjpd-Iee}GjOAu53Dq6|;~EyZ*X0)@$r0I!8?Te?2VM|n-z{6F znlF_cv59L^j@KD|ToaboP96;Qa4YTD(L8y#73X}isqZ@b{GuX};&DSz$g$TXr{33` zu~mI*cN6a$=R{{^FF_Vx5+OV(2e@2uDm%LNYlCo&e3yGByb^t?*K{3bCb64#i$ku) zs9Lp+B;GaKJ=o=R$^q61njUXv|B(e?H~!<5w!V3BWO>lWYUp$Gq1`R*7Lm)KO>ws zGTLNuP^bCPz!I(k@9;5y*smOjI%pWdkjt)Ms_*pih$}3a+H)x$5_|h%lRwGGgtbpt zo5J#H*&tx3ylm^uVsjG-&C#Qa)s3{9Mq7<&5FF<{Hulb7sx1WLlP=Mr=Sh~p!m_EdY^S(!2LHB8-U;H zjRS`-*plqC(U1FUP+Hp05)alSR2%i?O)KL2(3(BZ)Xqpwl==y@UEs;JT#iiRh32kD z?{$0vj+B;kBSt*0Z$CBnBcfjgmUtK3#pNm=6OQm*rI<#Q3uGJOBx{@$kDR5w8h1VD zkuUv=`rK$ln!-;iE%4;wXcY^Ah1EI6j0vS^lfs~EA$ZW70vQF4m^s&!$)PtK8f;wH z|K!<;KTIvMRzR&r%o#CD!^!;2;hqucVlnix?Cf>YH?ElO{_r-o34KV!-c(d?>qm^X zu!t-@#6)p89X?Th!V8>7POpTL;=sZ3c){yXhbrCw`Py?DZab#V`VS6qd^9Zg8q>G^ z_-CCvPb27A^2&@}-sV=4^azKpwd*6?(-Tht9b%^lK>&0I5jLw#f>#VN!Ww1HS(QK* zhLVXgew79_xwaO<2CZh(s~U}?guWf1vpA?YsdZV&jK{L$Y7G?Xypz=va(Oa2DU>x> z^{PmpUODBDBz8kG!e1t`Ub)KUy@4(TnU+o51Ks#J#5iGYL0M~hC{l{!4KdHN6dg_b z1Z~5H_JeX2J{EvL_d`Y+fuzu-z8fXac*wbg+j2p|<(c`?@C~9IQ@dbqP}r^B;vx>+ z4pFeK@o#&z>m&4{oNJ<9hie36{(g)fFBe&G?bNm{XBXneJ)!rFGHO2dP@y_MsV?se zn#*!ajpqQXYf>~@&5u0yao10(-5z}yC6{es8KE#TR}bua8EfO1x0vn2-PUvQ$D+?k zGHLB1e)FO?`Q-u^C56GCb?blXAu5B^RP~**=*nK|c zWY>e8y#Ho)&5aNnW(m~U@EY|;%1iUhIf7WBtCnw>E$!9Q$tJub4D;umaD+Bh9OJfs zZ&-`BMoyz(rsZt}hkfGE!jU0!0G4`~Ru~PuewW0@=af=q<5EN}+#X$ib_vLwVyV+Y z;cYx5J(TD<)3Tu)kHupyB@MSij@R!U^A65lOC&FE9tnxZ5-EXQd*QX#h=+BE5)uw# zg=(0!ol|MJeP0R2vm>Mv44jlU(b{)#BopDHrpQKN?2c&fc-RSIw??p-Igu$q{;kw) zzzPX5)rhMGB<8yWLg%tJB@Z>o zMq4l7m+x(otsB?*sE2nHb6nTFm|wXjLYDAW4vf%s<249k*la?_Y2*3Ygssxv_d|)< zDx<-1E&VL#+JKUlw(+4i2n$!b3dL8^ZrLqJuF)mT^B-_{uK9Bi=qGq*M7iCxUeQ;F^+wF2r zNryxDd+L!d>_;eHxl!VWu&ve0@`lH{lHO+9C?W^NnyA&wHA%qVgrhFNAX_}#eK{NR zB>2rnBS#OEo>_g6vJO`%f!a{e;RGr+2;5VNNiG9;d<%(y9GHphps;slRjb9fcp%Wm zw2DykLAOOTiFfCmKhIS1Li=`F%XOUYB>lU|(F&5>%>EOcBEHolm2HW&RqofRpAPAm zbtnu|TL(422%g0DY0J^42_!ymkDB(abUPQoVs=+sH2j-^rLbxo+G5wlKbIDy^-m|8 z8!ddb$7)ZpZsX#_EynhhA1TFs&Sx`sa3*o`*DC@hoO1Z5moz8j53 z21g_3{+Qvh9*QeXf?Csg6C+{_QaT1AR>fh;Ad8BsF^~MD%%zEXU}(0xyP#Cg8H*l^ zs0zyp|Nea|mJwU=)<)K-PZXWdlgu^f)222Qc6ynfDQ-i$$?R#eP|nWxvR->eUA?q; z2jdIL1@fFnqGzLa7FS$8 zyota`=Ebc-DG-Vlu=u$E4EIci{fSE6?tz0}yx;HW@Ydo?-c6_@&IyR+bCpZXHXgO3 zRWiLMIyb(k?6=Wfv$=Vf(KPJrADf^3|A?r+Lz9AhuwKuI_e!I>GSQH{D#uUQ^Zw_Dx1x^W z=JEji(ncSB)^h+RbAq4}J^;1L>88?PlX)MB!=~8lPG<15iw;Io^#-wX*21Ok{;T zTmw*?*NBLw(XX`mddDpqwL!UNfMe-pbv*)2>ZKOj?sJBpAWI_+qyixdk9qz5jr`@0 zGF-|s6*iYPb^GZG^L$VpZhQ*Ftms_do32y>z@~GoHxot}OV|kTfa;80jY?wi+#EF6 z?|2%gKboHOfCYxvs&|v45ah={nrhm~EM3X1O=Ww)3rz#oB%_8Z;JdIWX}Q1NKiG|I zmtH#isls>b#MmI=HDK<^Ef?WSr?^E;5s}7YO+Y|%;%hu!XqHlJ7pF|u17w-;&*xu2 z>&d;3+BSeY%f)`T?tgl1duGUMLFjB6`5#ybCMxMOT;g6Mlqh0&`}S8|1u zERPkGQ!pclWL1ldRe*nHva>pM9fUw+{GL8PO_DBYBJFZCm{BEtS zu&}D-f4w_i7+;Jwhs2^+J6iR}k9I}ih%eG_m;iJgq_%FhLmSUzY<#4BP4Q>NN80}h)+W=oLMg!3vLcEor7vs{qw?P*ya}c}P4|}}UUhI5Q`e-4VD*r50ZEL$TURdott`Lezsy@=L%w=US z?z!6ERebtrylF;q+{b9z7|yU}O|z=0T0I>W9O2-kmJmJqI}Y)t z3hV5Di01*0`RB-Mol5W%Mu2Jfu24fsPl@yzxK=AAHbB5pj06-x@*}D1xLVRt+X<06 zgM8VPH2K4rN0@A{LYUE*&-Hcz>krLuTC56BB!wg;y8FNaw|&gIdW63E-t-Ezlm)#7 zUXG;JV(rq*A&vIV^~ixp0Etd7K|VinhAodQpx4?E&|nYTa+&o)cGckms)5IEqLgEe z1C~>Xzt433+BK;&cq#CcBGnSAzl>gQ4i2q+3NRY;2hZk5rrBJB9@JI1@DM1lyXhid zOSnWo<>`-`gA_w*PHcdWty&K%70>IsQBN-J-fP1m4*26YU(ou4#L(3@%?A>A9^-6^ zHHs#FVj~+m0PjFMci}s+&OpyX^*jlE_siX9RAd=a6x)xs4Y@b{O;c$>$fwjSjxQ|6 z7H&P`qgsX?52-Fb&wx%Jox)b?n|}+!Sv^5&`~xn{cfstBUEpX!#dDCDkD2JlXV$;OhMpf9+uizXJtsRRF}yZJ9JngPR!)dI4sg_q7^n< z+gL6Tj2;-)nWI%y)};?0L;_2>L)foaH~eDtL&8ARCLq-wroC`qeEZ66cw>&3x|s_5 zUQYZa0=}9U0={N?}_luy|B^itw@<}sc) z5VK6i4T*e{FZDM%iLO)6hRalbF{a)h9=g9#v7Q-u?Ug0)aChLI!P!UtArmNm=?^H- zJyq5&PgZKMYq#w|Q$b$aolY|$0qn0ObsHa{PMwDRGRRT(^fymIUYAFH9F~fm4KQ@u zU4gB-_PBHR#sil6D5N9W!MFL|q0zz*vv?LyRMsKb{1VBZ^iZao%j697PuD}KN*1BV zd+dWf%)q0c6%XTB=hJY`dsdwdkQ&*euTmT;=6R&{&3l(37ZR@Z$05lgp4)^;dMX}K zPQwn%{s;!ykI5U~-k&?MWE>kIW_wK@=yPUIR!WC;H6(LRH2ZjIXSVoy*CFqy2>(tv zEwh{x1y;%fh;6RTLri|I&(3c6=0;DdXyzX}Gu4hQfe2XA-q1M1hwI6g9%P5XSzioV zN~?U{jtF1WdK?K=`IFzZ>LL@_<2(2~++Vb~XLj8C!aJuThrRpDPivhF1+xi9{8!2` zN<_*Tj#T2(=ZlU#q&kA8LiE@XNB!yirupCK-z*0yi&01{N#}NU;He?mo1!}sj7YM> z%dqmv3(Q1b)z7`$QmL=U_GvmA;KDuB3erc186wFM#?A7~j=T$7I%BSlxCb&n zKKzPLOWU0wP7WJH?)CN-F|_zfi$&tCyNvT|>^J|qgIx_~9gh}`Y1%1Mv2{`JH#lk#AVXd;3DcEhC{y ze*7!wWAHgsroMOtU<=}@4WJ1iM|H}v79YQ_icTcX?2P>F@L|gAHwDFMTRf(5 z%4E9d??tpg57f}v@XuZu_9-E-g$Rw0fjg)M`k+;o0A1K+ce;|cL7@r2P^ms3Ns#uMAE+@H0i2|)Juh+TF z<_|#-hFW9BC6<-!Qdnd#zXF}g&CXC`)z8wvyo1;w&2jH9O6`ZM^IhefdFGtAX4Z}! z`zW^@8^Nvm2l92xYJOG{f)`t(G2okI#IiFY7bVC^BF-uS^K&4)eW+l_CAG+Q;ah5` zz%pl`M`oe3e9Uw($!{ENccJmOYf?ILi~J#BBm zQhUKtk#wdRekDJr&;%}@E`P9dzQS(2g?CchIj3SM9q5TDynlwn>hrqM>TY*!p)v+Ku&~OZw!m-$ z@j+pK*P6*%K0FywrqGK_J`;FnV@ZWhmA;HJ9EwB`^qnF>rd}1j8G}|ofA=&liG&>bT!C^bWO4h%@c@NLdL_nvd#d(O8O%Qeh;9Ol{k*?a%v7wXAsbm~M8 zQw3M0S}}(TFZMkY?u6l88?Sc!@H5f&-CKAe5cxnqyxJqr>X6_Gp0qn@5!_^p1YRv| zSJAOBe+-GsleWqvbTm%;oD!q3dXduK43j)_9qw!M%L!$@ozZmkESE`xNw@qQhD*X; z*nXWEHt%LrVSB~Nic1m7LUlxXZMFOHZ}LytQKYKFCC#wyCpN*rTvsf6Jo2My&{(pn zI4&1%u{_*uS;cF@cS_iPs+aB7(iQ+RJIu95R9>z2U*6UMPi9>Xi(%QcHtwHEK z-ACmguS8K%k2+6Iwi>pI{F&x1&pHDi-%g9SsVP~6L;PoFw>B`FJ@dz}>S8@M4o>1& z!oP>d8~(u9`XM!Y4yBb7h1W{o!imU|d>IdjJ837pdxD-``=I~y)wW8x;7Y zMr7C8GR2OBkv#ce$%AVD}wy=siu=~r$IGZ z-lJV4>1O;rSEq%{5s)=dho!Ce$dH=d?I)mMuZ~!-VeW0C zka8Vp+woke$;JN7iS`mdodIVNXRD?cXYi8^$1KVA3~!&j1AbFD+`4U>nGo^5&x)Qj zkc>0l`;)>KdZ#DG;|lJ(rGX#dZ+5)TGNHwmroW^tt=BKTbh=f1sv*2=__mBTHeJH( zg(knNiI;8itn}k{#4V}8%dnx5c+SAzw*$W>=AW$Ep+rjyQfcuKEL8Iiv5y_f?D-~^ zoZK4jK|Yv}dHScMDxr*qq@$mc7`H=TpWt1_ctdqVok3RP27X zLqh%0$!?+T#LcVfJ9nc0IJDE1aY9-u?5mEHK9gYZ_t zPyd`3O^Z&c4v%+6bvLcf3asYsaR%TkZpZ0~3i;2;L=glQT7>qaLzD`%W!XvXhkYZH*|N*^y!0}YyQzLZ)vBQcPY4^KC&t!%IIPS#)by7kTJjDK z-`NfzW7sHEQF?q(b4(e^q@=dcQ4G=~47T7CS6{lh|hAWzbEBudzvN;;H)AAsbb8A@!yniPW%ZbIeCkyq&s z)#;ZoqoMZ=Mm|03rT+S4LA$A7v<8-^G*EvX!#_`HW}=`RbA$sjBiLbU$I|mC7CRy? zb1=F;tjLetAKxu}F~FDXdp>Gn(9+mY`c{Qe%;99zQ6T9dsd-w!bRXPnJbC0S@7$tr ztA11)R~D@!bN)p!&}QDpV7HsoU?&heN%w2Ta?jVqUV@sh)PJ`rJh95M)n2>tIa~hF z$JFb&)u1jgR=VAd<;|+?qml}|F~wluG`6b1O(?(IYJL>7l4gytG5Hp=ye*8HU{~ftJd-?+Lv1(Io%8~wX$yfWJ z`vp1Br@b=Om?dSnaV5RzFoQ|8!r!F0+&~6+f$J~VE>QB-v?radC68bppU=Qa9DB;? zr?VU?>-NiXUJTXopC8DK!@WA|j<;#1e$C!qKaOwmvIVm4SrS`QhXh?>P>X50v15wZkezT>DW8{;E5SR@Gw!srx+1vu6n{Ygn!(Xz7`a!l zEax`a?!L%%N`ewUuY6!JWtNy}%uF z{%jMSb`O5-p_GyDddEIQ(@rlz{pY7dE0*PyQLv`_3ou)xFM|Blkw`NE2Y+_D%g{yC=_LN!(MBx;g$wai#km{OTHJ)aJ~2j zB~?}3=k02g5sR~af#F%g=U$(^&C%JxlzOY+b4hb9xFt&JDbqIUK>ILH$I9Bmy4tgTa}nofzgEY*EzVWE z3j8|O`sHdA%KIJfim@lmYZMwgZrA+GomD;B(K1^vxVOF+MTx6F+BtsS(;-@fT1)y7 zV=sr>?}d{~6)fU7wnAeFnpci`z$e81$ERpAVYA6v4UyELm+!tAE}^g?ZVp{dr-7yO%3%|WI|G4eb&2X@?jDnS160_EnzkxtEC zLpAuMYkkq$@Tj-si?2&8Ck|Gq;~8F~jQnXY?cJHOvANzATm79B{bD=M6tNE3LLR{_ zTbjmZr4%8P%LC&vxlFoyKFLS7t$FCZpg$u9C7OeCe2ZS7|4H zwf*`&+)~lyG|56p?$}*DxzJ7D0E|z%j{-{o1@so>y`8?|0J~ zGgh@yZAcwjwHP%xS?93RciEwv_i~~BHvGN|tij_bEwD^ZVrOF>brto~v`T-y4$Uon zqkX@S2Kt+c$P!VHNqMJq?X3REVN6=#v(4I(#XxMUwQ99Pz+)F$v8qXzcQ#7ZgUV^b zVh|4X&LkadaAh4PuSMPw53-0CXs(EmF{5ffF1)FvkLX_eqFUn+Eg|JNyf#u*zo-Ej zOhGwJ*Xn6EsJl@syu#hQTK}Tbz}Sx7>a?FKhSm&~+~}lSJ7rh-j>!3l0((cNWv#NI zmBDU&or56WCJ#S-0Job#+E27P{h1^5w^&9gWQhIB;D@k6AiTHZxm1Hgz%U51$*60~ zR^Lm_G4UDBo%0X<(xy+BM1=hgol%Ycbr;pyeIQFIlP!nK`Iz2VwCvnye-h+_Vu-Ad1m8RNR02ue&?1uxFBRM2zZt2)GvzbR26 zKDv}!55O-^-z>>5s_hQ?0&tEazd^=IT87nu-Gf1ur%|esN^)iASLVOpqDAotG#$r8 zzr;oUW?1cGG2SEvG*}aOg{KFb(lAemwh|4QoN>6kq1}9cyW!lK>Npf`dE)= zxucA@GQO2bBAr;!sIwVM%6vOj3@h%Ikxa`-z`oRp6+*#2;8E#;;E^ROkm>rI5*=>~ z4Be%6o{%Q9LG?p8>mA1L>Nsp7Q@veo+xGx>FjF8ZtdF#dbr(j5477~OiROF^dbly2U#VG1t#2oC<_C1FnfUhsN2Zj(3U}BJ> zMf57cTG4rb`F-yH#NoeJ3k?ZzkL&MdE2)$lz9%8d^v8zi+W1ZuN5?AR@>$^Mk%9el z!Ukw;mVkB|3J4KK#SueL@Jb+VY7xRIRU#{GNQx zq%&XSW#A`hH{yXQsdd$7fx{b=`lbSx=(7Mrr1~iJyODlnuL@`XZbSr<3x_q%A4d;E zSahRT_>R-$hF8opkOBQoaqy21bs*!+Dtx6G^Y_F4M`GJtqxisVy9Om<_fO&1?>B7` z{jzzZdwcksiEdps)41dKV#Q+rl;h z(FqQ;nf$nwCmMXK0QQI*Vsvt_<+YjPPyJ3gLwp#x%XnpHB!Ri_V^@B6qxYA7LZzEI zT(>jw+*vsU0=R&s<{$5Ha^f?M_|V}zj=5PHLBXLP5D@S#A&)7>haoGI8#peG7JHqk z1YQ8vfG%KMiF1GC`sM{Y=pubgvKq{J#23qClCM)7!@%9wJ-+n(9XyM~yJwD3V`RCn zApmHvSsF!m|10VH>&SDm1bqV`VIc~%MP2qmGnKAYc8ru6yWWKDVOL6-!3_s}MKM#M zbasRFix3cOsmLOuItL)?_9nzyz5?{8#sd!OrpE!N#HIoR^pLu=%iksii%J9D-A9CK zrlc*<$#pp;{bDnna7V*zwaQ_9u3yMtPPt&l$@U5c@Q*Cv?yEyiUph60nGbJ`BqcV- ztX%ME3Z!*KUFKi_Y0&CVBVs>VB;_I7LPtU*N~{_aXh3qmHTppcPmjIz&O#R)l<%Aq z^$e!Vtz+v%T!b)1Qb9a8LC{;4+=J3VGwL-sqUO)Q@Oq_w3j28zGZ z=7GYV33~^!dT=TF<>I^lciFx-yMYTm0xh{VS65f^*Q!9UGgGTTcaYI%g(csxri>z* zEE1?}T7kVG{Q0Ag_wY%=BOz`m@s!JswP|`FMbpjqhww|8vf>vNGcRssxHGu6_o{O zl*_Zx$p--c!DzoMu~k2hj1`TZ4U)3lxiRVpdq!gfKpCaKzEsa*lw?d#KR2y?%{Mzb z?Ox6bM1%Ti>AQQjWt7Q7Mh!k5qhac>Nzk&SGk#BFo`6M=w!BaY zzjFK@2#Gq%1VUf7*pCO}k*JL4!Dum=r9hU^jmJC$ru{kC{$*+VWqg=ncPHuXQ)o5> z>SFA_cd=ttsS%_W&t(r(*MSi*Pn|sgYPzDBBs}8PD9`!H&M^g1ho7I{(62+&c3v#b zh~Q0n&=8vr0>qr4olq$gYgfG5;u-Sk;eG^3RGNascNIlS&AUnnu|vC;FiQpuAf zg0;6bAE1$n4goqqVo;=!uZXOZ)?9Robc7a3f=)9@t zE)PF0i7#(nMK-6*WQu_Hd$QE96byH@8rT(0%dK`&m>+}bj`hy*LQjl4R?l7y)JLDh z_!^gOh3K~O6o0HkY?Hq6eFAu;Q6A`WFgU`EUiDy{bo%9z-6aWUXh_rq_c81w^O;+k zokYu%plg{WwR(50vehfwiTs*lbqt0h0}gk}B+Pv$Vl_i}Cp#Ea-V)Bjx%%2+NKC9V$a zZAqMhPk=k?3#&HL7vT*07v5oWuelBD$e@C_utVt_8Ezdeta?=3tGLjah$@yj981tD z=VI^AoeOPNX28$E?5WAf6s#hEO1*d~4%wR>vNcl9`sOeiIRjFQ9J=}jGOkv7GnQAJ zBs8A_r6)tDgnu&RFpMe3dOIaG6RL^`Q)ND44RkDg!_-H2G8n;PEo6JS&jWPh%w zZu+KfIW9Cz%&_ifhwd`n;w60lPC2~85<-KYwG>8(RiuK{7c|SP=WT=K#vQ<~s%yg- zH_b|h+hQ9jr7>y~$|i!t2V1?{BKuu{h~9?J*~L_N{uRc6*vW;msQ3 z+Zcnd@JFjZfF?*=^kejTMMM}4{vtL==PNv9KnAtGiWW4~2*<)SW~ci7ud1ix8AZ9p zuxLLgK5#_yr{|xm%=y0CUA)9Fy=744;4%6nVK=-r@w;Rc?X+;`^-&=+->=fS858vN zN4|v94UL;v-5kr>-=9iw2mXGsm^jQpAh!I37$#v(IzOvzi$q@={GaFXkP2U(JXoW$ z?Yi!%?u9n!Zp=f02OHhm1tSxlH6vVki4-JwgfuhqqV>@FQOB?QE`)hcesEmsJR7!YW1D32_HGGNG*mUiiM@EvC2l5ttVXj|Gkzbm&&Mk-i|)n|#Z( zcFMf(_kvnDAl;f9c14zR84D0qHC)@7zs#Jg`{mc696yK$aDx^|0uO^Jlv?MW-*pTR z(mlj;t!9*e+@39gu5Q~5jaZI(@!E@KDh}Gy%1_a@OPjrLVHE{Lx=}!je7S7*!OODk zA)^MX%Jo~g_vCM^$%|sl#r3j=K4DvEvSb1SWCdxg@QqD*L>K`wa{?e_XAJib~}qig>{71XWcfwFM&&Gi=fWAF;jHZa^A01;L~Po zIetS2URt=stY^@w<|d+vv6* zwo6f|*3ciHzTye~^u76(SoJAPSH7u)ChBm}Bx{M%CVZT`nZK7h37wA?bQ-r( zP7}M-9{GIX;o&?&>?+gR@~{z)@u6j!8*d>CAq$N>0a4s%zO9JJD#x)_p7E?G15wpe zaU5ouub2utV2&q2NDTC-^I$3!&-v^qh zv~(s}DOtR!13#KNznPT*nanRFs-#pyMx*yuW zcn=8Jr*GYkjICP#!LF}RC^6`f{<;3pA3R4G@4MsJO{c*1w{1}Y1D|GV`l_F><7(*K z<-^XZ9GX7KfwR?xeVkAF3Ryi)5D^uScjs3V3a!tpb+RslAI)3acZoK6d@C$ntFhh@ zxDrf%Fe-z^#5zjUGqF%$s9jPB6ur%V6HGr-=V8$4e)3Zoy{`LJdQl>YjH&S&98KHB7h0O*(IjEjTJUDNq@ZexuK* zVp8H(=kMNDDeQcT(ymf%dUB-rjx%tb1Ae->RkPursb`V+HM2+ya=s%1cSPc2$II={ z?KD2xB{fO>eCOfYxwbTsH4RJ-2*z8^;#^Mt;04d9L`z zl|B-7{~4G#>=yqgqvyO5@tznTZWo55?3e=z#tf)`naaely8DIL&$lD4AjLoc!>w$S-`($>Wz9Bp)QZr4&?E zQdKrhZQR;LZ+qktO}ZchJt0@PC&2#j6w}&B@WwwT6hUU>)Ac=Ex$8d;@1DV}SLTh6&jKQzFcA~Y{WzJ=HDYyl z>xVY)K5VyVz?W8!66{(ZgQAD{B%>~z967${^=u&wMA0@mn9v97M5TBxqEqC1dVcS= z;mZ-92U5&>f8>QrsJG8J>wQ^MaR>MScKui%lBWqGYtr@WWS^=Jc1pMnmh_-3{CYKA z)ieI<9o|_YgJ(j6QnwkZK0`hjXN^<^678lOppZxwG#g}X-}JF(>uzsJrEKJ&gE z2Na`x(|y=_8v69)raYHSD12wzO>X;r;vpKg+x3&&B{xbQatl%`-B*E?U;Pl^)LtcJ zhdNY|Cq-0O`Fx5%=J@}a(>rHV=+cUM`tRia&>JfU4Y&=X9+RsImI-QYAl$bl#T9nSu{?f`9DNoO5s-la|5rcGG8+?s#5z+j` zU=2=5n*|f=9OFtBu6ai!= znyGS@sU4`bOYp+^DYRI)NOj793qq-?F3o|ZW$mvi&2aQ=ncl(XCnfnPU^_e%^7f?^ zW4?NyZ(*2JMVmJ!bp90IR@Nlfr$IVidT%~@tTH01_VhAE%&y(trI4Fd>aS&@7;v>= z>aD33;$$qRmd8%I9U&5W~f(oRe$2TZIpFfA6{5LsPP3JSa39RpvC7A(4!h5Z&bs~_MQ>)lx>&dlgv@yrITZ?Cin+bRZTrCTKA|p z&?)}J7>&H^KlT9CH1qTPa~4s;*jDq?*8rN9K`NCfiQ5$6m>J zR?(hUX>HrC4xv%^?wB6WU#Wxiypp-HSLCThEIRFY>errFXxAS z19U^Ao(n;AMWxJe8AFwJ@nU1K#mh~<5fwFd^2VN<`KSEY{u3mX1e-rNx;+f{UZwA4^wjFi+%1)XMIv)X`!Os zx@P3co%Ewu{BP!}g6*)Y7hmy#l5MB_DOCCA6z4=v1+QKfd1$C$t5ovt9Ak^rl+1?W z&rk9v)x|M4#qJJ?bu3TUzl!I~=i z>zcA9xN-ITWYdKHl7rOXeO- zFAHlCH(=0`ISKb#w97Zlk6E1XE=Enh_a(8v>s(@xb#OKd7lLYFc9_nijrMv>^d8Qn zmoEfjIZcmS=Mjd!DvVs2HvX%sfi^wC0#>|EjAl}S``0DNPDj#xmPt8oYaC*mYn;%k z0T#pWgsT|O5Bas(pcH9tV%~>w`DrB~UF=XVf>$y`64Ry#oW8Z0ehtQkH0o0W(yFRg z!w!SP9M_>Y%i<9@S2>6g`wS8lZ83N}Tw3-oh>!mk%UjYQ^Zs*=_A zOpBu@q#efz6jP++40jQCjacz9a)~LSy<^djH9jdQ^^}?;kU(tWZue`iVL%X>jJ1Oi zN(M0b|56uuLuVZ3V1BcWlzLlqZ>oGj)e@R3NI=SZlZlXIxCD>-WE;6SPqegZR?ZrZ z5f?B+0_PyRX!`qbcnu^&S5*ue#d_n`wpn;+yB@DjV|Cy5r}zDY`Ty1eU~GQ~Aaq|9%uGleg!6Yaw!n$r$}l$i7Q7;%^& z^SvtwOh8PPg+Eoc*3 z+D>~-`(wf;<<&PVdP5$W98;-By0m-H4MQ8~AM!|!R8O_K<}4#n0u!)d*{J#t)fwPiMHaagtqQs#3We&fHVRv zI6EIOd4R_%cA$FPt2z@t)+KdA?l&t}Wv7K*3h!h8)ugU!_MXQKU}>I4QFMmOIHBWV zP2jIIirC13I+f=1-s;*uLJ{ei_1AQY(;i0(JDXKF+@@M<2UqbK-5D*c%17WDe{MNP zXwi$54}{6sT=DLhs_jBPuTdv`w^fWCNjl96tojt}RGvgaNP^oNuia>NW&J4j+^%oy ztf=SR`S4wofN4G3vsmiVDsx{?31NCp;bGKv0FHDyatykcP=AkkWTj@X)L zq`F*G?s#r|ifZ&xF-Y5=k@B=Tqst$zNo&{}F=(v**dxwcRL-C{UD3CD-pAKaIhGGy z)?qSefa}ykTb>Gq5^H`*wcm6)4PNARvfYy)EKQd_cK|8iWrkco<^^e6-r`fdaE`l+i4dR!`^+?B736v-f(8z%j&^!#W*=fb3VZqmpsi@SE{Jx zP6K`uM$k4sum2RE(YAi4m&CkX1viMZGuB_yoGiUH6hS4UyH&HysFN9Z<$gMQxA+9G z7g>HQ4H#zXW%4S)4?^*7GmhZLhsT-Dk`MmI_@&+vz$4bR&zaFV)+RcT6&d4to;&hfYbPzM)&R}AaE>4a#CqA?*ZK*dFXOaoXcvP zwtSzW_%Uo>kdHj#%g0%L*@fKc*5#!J>3O|)j<2(`&zgj$o*}o?*Ixr~Ru7K4u=Y`t zAxNiKJTEJ5(n64Of=>;}_HR7nDNnSo&cnjU!+lcqH66bolZsSps@#~qEwn_)kf4q5 zTc*sXwq&T2N`e*3w#3nqGzZzAr$c6UpCRt23)eGNs?v%~M4rmEI@5j`xg#@qpa34y z&k#i|8N@T>zWbta0b;aB&%Sa!oz_H}GWMcBfA26*^WXiqWs0EI880<;)9pK4nYtP0 z%237Fu%pxSp|3ZxMM-!}%rqQnZGzbYt?o;Bxai{7sFnH<)y;Wz=(jf>nT&CG5mmAX zQH_;4?HC=mXnZn=j!CZhm1c6rpLQZ51FsX=bV0#hF{6QGd?<7M`VSJw)`TA1Y5X}# z*gQ!q-vs4oRMF2slJ}UflRK4Q;aY5#b1OP}OBIeZQWeAL3tW=$bpD~RE@PW?ohV4e z4IOy$hI{>6r^o)oChpBp0*XSFVxf#W!5IWV}(C zsrqcPX*U#Nyxr3*;RR27fCkt^htn*HAZKRWgF#htYH0Bn9(P}(bgXy6B-{P9y;QP?G&aTQmngZqW;p*3r#w5R3B@@AbdObp8ZDI_sV^B1CZ#Q(WUEPFe_p7 z?@!gSk-f4N@h7+HY=qA1Kf8U%j-<{KnG|lF7_ZGr(3)HY*+a@liGI;#aki-IvpUs9 z@zI;^mTVHnLnjZu`dXLOo?<^wJg)>qjZlvJzxEW2SecXMI-hCOa9PGxlrk2#wi z1$50VNx6I9tg@08z1x$OPmSlYSUbt>V55hY)YL&2&V|CbrnKA7dI9hxGV)`;_J0An z_#AQC2RmYzIAiR4jz#jdOP<)AQ1`>7dUW*8ON$N80z|6snVj`ZvnkOxl`EtfR@qOV zjtdqqJ|L(xy1t~B5tA_&pKMpBU%0PbsSQEhJs0t`*kS}=5tog5=WKvPbWKCVZoJ-< zu2}CG^EvHqjNZbN-Bw?BUgs}RKM9+hEoPsBP(36p8Y~G@Y_dMX3yM2Up5A`M5!|&7 z*9Tb6x%?qGzC{RV`ad^oFJA)+r+k)o^2%=E@7e|G(UuqN)}#2$MQ`Hzw>@$zb za6XK6NGdGrC&7WuKad_2}tD-IFfdD&UM<)L#)M((%&7mxOS$53=jnURs?klefs_Y8g3Rkss z#@?QvTRXn0ez(bAS$w`by_wdBDp(#kZ{tv`Iqgynm|KcHs%ui@e;~3-B|50YPh9i8 z{;|tgg!8PHJ+(bMz4h4y*Qkvt9$cAZ?wpT+gJ7g*JgCrZqj}$!3U}WbLF|}iUYTZS zUN_ok>pRlxb`rxh<(SB4>!W}CT8qBU{PfGz`#dcISy#tYk&&;%j`sKI4@xVu&Nx%* z(Xh(;xni@`3Rax)+s?-)m&SSYEdL2jlJ0?{W4e~rsw$%Q`6W= zqy3*6d6a`|Nd}O;Y8?prAk<)zyQq}v>}0$(>x|#Gc+?WU!W3whEOp{9;qohm_iWXq z&TsY?X7-NHnNP+26=a`u;D%`$6vA&HGs{~yXMJu*RMfOuAwDpMFH2|!U=Hy6_9N*o zW1E}@)H2_ic+xzM0!r%Ajh;8X(Q5pds`y;`hV+N$m7ycmyUIacXkr6NG@rr2cyWO- znldlZsQhURzzK1Mi=9>vI|=oE9WHc2Q8hqzvCjU?_s%=%jD(q=m{{~o6G_4@U(Z8C za5xxk1#oQR6q)Deo%RA?8+k$<*S2mzGo7dS{VH|Q29sElquB%dJ2kH-m5aGyP!`Gp{3<6Xq*sAs=09eSroX*l&!N}jq8~?eTKVLYTQiR_Nx+%LAMJvR^ z!NHM@xAF05yaTX9905YOz$4Khl?*s@Soi*pI(zet{+-95j!&+d9gn z*|{QX$s+NHZ>Fql>rHy?EIhryD;fo!?ZF)N`zs;a*eC8vIT#G;d_+LuNC1TY5A;)D5&*DN;Epjlda3V5@V zb)&=qZr_J9l9Q<=MyfGiXqi>1GTmp1@pWS!NV%`5*$;o!;(>gnl>7olm>#2)K*h_(cpUk2WmsL^H3XAAi?a3M%pHF>v-Do*KOSO;u+2YzK!Bo>AuaX zX~({Ilz5H@C%Ubc0Mg*O@||TA4iHw=*drk2U}O^k0FkuoQDFehfC8uuvvF!3-I;p0 zts8Krzo4G+5fQ*0#?7{x&Kspb2NKNZXOXo06k8CnLob4IeU!EnoU z05ETm;6hB};fC&4N+f12zqnS7VByhkpQ6LlRtX1es-0uPQ3bc2!2zJhe`s;}5VSJa zTLYLKOk6rB(Kp)&ZF+$BYr4+8#hnMgnNrDF_yep3B#>08Q8i1tQoq5P&K?rWWFue6hrVXk&;Pwg4kB^$a%R60#h z`ER>MXW_@V1CqFAuiPGRr+2nwRvNm>Z&r2_)QX3JtZYP1#2i;$luIC~KA9YVWEtPG zzzM~t?YJPo`{A1mHu!pR=(nx_P-XZ+ZY}Vj<#-PqfZM!(J?C(ki{77o7%4y(vl*!t z$&8MjB~zNPsjn;sRQf|R6_i3VnhAPyo-#Vjt$vUi#_>>~aWTnY!t|JbLI!_&6IEH2 zT}9-9h+v5>QDkJ_YwI}z;xz6EpZ$&1dgLs_))6o$SCwF)iJ1z&96c6*VDPFoiedto zcjQvf;Pd|q!2XIg>!-w74xu6Bk>#9Gex1D1ODn{CGlzvzX~-OQso9(|n{?m>t`uy> zJ#-~;|$EhXql$Uskbbd&_TFR^iGSSqyubs zds(j6=GYm4T2*cW@;w8LyoH!adZWGzvk)^RocuKKuiyQ@UeDZQbv6!fajM6JLZ<4+ zP1{a>(67%PRt5w@^?;`^$Np^LYbX?m3e~FNz{oxW#T8N8FC**2h15Ny6mwKg)wUtumU$#Ova4{Hb~Z=+(N zbV;bywa#e(ic0KDchb=5Gfxj;7YOhUUX^6&_<721>g#Lx<-de5DXVRtjR$^>f*zxp zmTOFU+JSQn!;_LZjxxDJE7x{t&qs*Tv&T$5|1}qX<#Ek8P>3#Sr{=KLyT(Gh@oo|R zwvJ8oZQA5G=F}z2;H!PXv{1`e)+{y(kA10~z3~AU-Y;D8$u6r>pW$W!) zp+zY$WanOhrLCeF%~imf;Km3mc@J9nh`OEa+p5<1^U&M#$|{oPl5fc-{b{?9R3?1h z0ekr-<3GP>%#+v}{NrRcdf>c~4Avzn5Y;fl8#z*?MjLpUu(9KL_Q(E70-ZOl-d%9g|9sAYdG5%n^BOZwxHxvJhxrC8Ar9p8>Y-qAW>W7og#V_F>#ERn%gG5eAptSb){L zETuEg^0Y@GOWHsE+fzbILJ|{P$L`l=zYxH78`7O{$8k<{hq>Pb#2V^lSxKQ0TfakF zl+j_^0~Vg)^M0h6-rwR>KJS|eGB!vS=mHI`zxhXWrRItg@-U?(r*%d{bEQ>m?zc|*=3d@*gNj0ZIH&h)_nnI%H;@Gs zLUb!s$R4Cbk;fir#@N_fE8>6hP?vr7Q6f^FMPz94sYiMot>5@;hjQ->@PVm|nA|q* zZ{JSZXKYAa*tP8%;CRClkS8Pws>J^%n)}bM&U~cbCbV`8Z7Pw@Efg25m$Vh9rQTFN zG(Sb@WB_WD57vGqa7X;%f2X5=LC$kt#@Qj{yZo7}>=*9#=R_UIs@PZDf4q7dcY$`} zsma7<`OpKz$mV6m{TUJlK=QU z!`sR^G62b)(K12v&an$$^xvPNBd#a^xLUz?=aavPnHPJKQ6PYyfPvEmGg70-d}=&8 zz@<~PL9CoEI{3{E^dKdIM5XMl%*A5h^2x4lKu#6=8Hxi@*++7_U2aqI`rpfb&%#QYNOJoNez`y2aehPb?Hvs`ORY{$cLxf= zJ~C40wN+p~Sz;Qzt5Rf8Ra9tD2f&w(>3iau@Bakw{LL5pjLX_VsN%KLzLjy$_U(Pg zpJQN0OyKkGL`(zqeL06t1wa()84zz3Vr>2AK=Sw6%>r&2>(zO(C?Z<%+uq8#qOO0O z$Atj#JyK*O;R}pW0B6cnfoC3BX`h^9VM+Oy1@>2_#P1&$8T{b3;NkadIZvQDLG$MH z)bApti$sCP)gJ|rsUY<41NjEEV%xR&hSH`AZ6Nv1&Bgk%m^8i$hTONJy*sK5R-Pz8Xwd^q(QXUE@1HKf^Vfid z{0opjifVb^8GZdq&-X%=vkvHK7J^CwZ8B9QL?ch75)c@4L1{uY-+Xl_H%1Q>D^yX% zM1Oye-`M|6{6Sj(*Z|$)kvvtV^}!F13m7iq@Xe_P6hSkGYUYImBoaLE83Htl3Au@H zZaqfE8HDK;A}cRM=k_Pu2K0a@Eo&kcP^QPhwHp`pgdEk6O3N;+Ftz>zz@SzcyWrLV zhMGXWHEiY-TtG*18T#9UbFcN0zN?@-GVC*V-Jsez+a;i@{l)LO?FGlkOpol2Q|&5y z)ftc#rL~kiKivf|6|qM%J^W4Xqdx3r0&}OJ=u_bGq4Q4143NvLTQ3IketBF`C2*fT zyU}Gl62=MzB6>?LLOvcIcStc{GDWn{a6&%BfXiy^*^CJl!W}27OP>dOKR|5kM0Hd>X62_O*wX;EoK*L!)b`nIT)@SY@TUy8UmUILp2t9COykn5^ z`3Zuiq`lg^yxSM0eA#W@s);%Iw()XniBghck7GtvH9c1x5}8xyo#0%_);%4tHS}l_ zHUgMd`6Sy@gn^%ogs znD3w#G-kW{9O4J)LjBjN1ryW=E(j~vyI$Hfcy8*~fq0IALQ;?YE`S-Dle>z>r%Ot1<*yDBIxO@?{2DyRvQT$w3> zpoB*QOSWa1i${NiL22gIRj>%AzzOe7KI8f^((x(pWrk&Mm1%&-_b=V~a@neJ;KgMx zAd`?7Lpe`XxAe}Q&}kIV$*eXX{^qYB1><|a`Rl~UYtr)9Wq|FcUw(j3c}&=5?%lm` z2kthiQq>fAjs>iq(P4$BqM<&t(WatmWw$;FaDGAkdMU$@%f?}Aj|4}*1UcJ zx~I>V6uo#*HUPcQvqRYK=eLa5;x_&&Ku+2@Sr5l_r~M`^`K_xpUKogo8UV>!U){FS z|Hs)|hgG$1-{a~*r4-nJq9ENVDuS>Dq#Fe3Zk3kYq?EKMjg+V$Djm`w2$F&z-6<^% zQu3S2bMF0|d#{}D^Zfh^d4RRoTJJmGImZ}tOvpHHcs}tg4vI5J7LT~LgfX-wtv(pEDlXlVavzS`1bd`%w;Je!S*~07aV_3* z{oEhhq@fm9lNySa0}`w&njkCCidt~^nl67GoXBV5+g&1lAFYMa-fOD@C@Z=zX(7i$ zXD*Mt#!hkx)Pq_iD44aQs^>7IMJx&@15E>q$zE`t;7%pUaPB-EnYe-hpto#tkuLH0 z35pY3H0KDjh#tGUoNo4Exkng8*yh4{>2TqI5&dQ9Slz3Sb-_f_;C{Z$0=(-5Kq?o9s z{}Rk1U7Td0DxciHHH5c)q4u)g>nH+sy>bF_0uJ}q{*|8*nZ)zQ4CAld6EMb8ycb%v zJ~Q4Tu{cufoH8gzD1LV+rFzs~XMFcXc%?>MFgbx4Gux14-F(|%y!nl+2kws2v4wa! z9#}f_qS$7ZH3?f+PgOirxVb99KhQ>Ip7|cSlsE1A$P)!k-LE7QnF{?F`$HXHgq?u* z@@|l$peKF_Gy;9}MUs(H&uVCi8Q!rKGGaII+MlFK!VaGusleFNeZOWE%-e|!(@LV- z#;b^vwYWU}v69-;vM%wz0)#Wz4Z%v4aBRr#{M(5UBfQFiQwccF%=1eT3#{ z()BA_dqn1|{F^8E_U;MMQd3mz4KJ73(`JfS&_tnpPAnVs@Y=9!88GU;5Hudzu?uXx zaD6R6yk#X%u4i>#yj5(-IP(itmC(y5-#|HmvCkh*?4A(~B+#a{Mq?YIg!$YOglwu7 zc)2Kc51L{&^0u|U(Gcu&i`~bP`gB6CF9<(efA>2AO&UjUHIVzvtbDMK?;8rt54BlL z4WP_NrsQT3+b22-zGBPja6+$?Zmm$xXqCal`b*~MT1>8}KuHw>4a7~}3ztNnDCVV9 zqjs_vntbLfdA(KNc#iUIM-E!U$PzQL1Yk-6;InK`^aMsSN#LgbKSL`ysyq%o5TFW&!S68B8%Az(a8HzI} zVZh5{MZ~*Bd=5*|-A|5^BvZ4T@#T9z(Lictvsx^gL<>U0oAKNdbH?n6 zkGaPhj<;C)JzB9^nWWtpKAvM*8%dqMwt|fpQcf|GibEi_zv-*aNx38UFb-@|%Lr;Bt< zV2fb-QXmnXk$CP%xw8$^lTS{J8Z{xrAMFWONoy{8lhK?H*Te6)$X!o9@)0{^4xdX3 z-}{DYBkuf1LakM$4cZ$ttPtuRyLH4m+5{%N`bgMMm{WUhYhq)ZYsE*wylM&CWZa&> zZ7-eGcb%c|7_Av%Qy*`hoM(IZqhfq(N2Bwn>Q*k_%!M8I4e#?#{gQEkD^4#2MdN04 z2!ki}8(xpzbk>)Yj?rs8i#M`bYwuo~tg>Rc6B4hL_=bYoNRkH6q>UZTBUonn}F8()%N*GTc zW0@}HvKdd797yA`PQ-Lh(WK%m5zW{B<{UMC?#2*bz2_4}?&xE(#(ECoVlWIsJ#Jox5 z^M_5hTl$+j?ziUxvV=F%IOJ&IFpxWwN1mhFEFRdP(yJH|b5Aq=)iweC zzAw%i>N>5;%S7hnh4`oiswOA-2vBEQQkAbUt-7 zE78>*18k+t$(lzyI4@T`>#jzVC1JZ8hO5Ws`gyq|QP4c;9lVD(;t}YBojiwSj$Vw@ z3F%Aw=*yS>)AxMxd0&}6_SiFqAh>?IjWB=QGDN9yas@4oTr#N zbHf^!^l=FA>NpL9h2-QU;wvT|_p;7BlQMbLzT&dMtrO2o5QFVXAv#2_cRBJUoc+?0 zA8JLT?c3_yHA343_gb;JYUo|{#5O7Wv9$|B^2c!&OnltjhYx&9ibF|Dl#|bMOd1c- z3WJ3m5pR`T^>8L8ua}4T<#?)Wzv1yVq33bn>@N@Cu|MkyM$men8_|YkL2;zM8^VXm6?H8kZ)_YDB2z`YkC# zW#&3kG2#gf@G<#ym2N0cMa|dx_)W`|bib22I|eRhdqE@a{9GlaST$7W0BF4Z z$tPPUUMJ`0CKy=mcWxzl206vCE66hE)*noVR3=|&kW-LA4+`y%6L6j(;DM0@uEmto z1aqZl_oX4` zHCnZ5U~>lSPGQ!(wGQtSr%HrS(7-u`ioR^km#Vq%;jN35O2PNh>K*Jr|<-LHpP5DGSBi)QnD z>MADEqDz<&*PP2%@iVJ)q~f|UxcLr^Kn{H5SUtZKp05b-&RJ_C_|e7lAwupt|AISh zIZ@2`o=l{qr&3-_;LKrKqAw@j<*X>}e`^|IpVxms5*+j}64VjqpkWjFQD3lb#Os$O zCw{X%lU^jZ9q+IcyCzCS%MhQtn10n|Qry5x!887QiDIl}kUH~756A@GgPC=c{M|q% zE+5Bx9G41Aj7&;K;2Y31lN>rb}@f0idlMZrlnWLtsc=g8vp**+;0J`HhskRr+xIR(4qEY=k)N~ zZa-5#zqvY=n9ndkbLT8UEd`@RZi0LQnBZmUSC2s3jgh#xI8^D(0xr;#2q7@LVFm`r9tHKy|>kJOzUk2Fi*SGU2e% zWYa$Rdz9eMb^O6!fm*qB><*#3`xp$#AfvK@3G43>oSf2ak-Pd`T?=A=qR?{~kg-->ohlL)(rNSeUz zrnsU4J!98u;`smIEAAqABb$15At1o@GA0R%$dFv94uhAt-bEuGeO!}|&X5nl^0;u zo6jLS=s&;o?(1(e`oEYKZ-jp|yr}iRMpb|P0CG9JDBuC`3XL7L{_BSS zqW?N={8~qAAGDq*Pv06GpP5hDf zjtTO6md`Hn*?xfO8OAqVHv&82R(h~n2IJ2UZm}t%SN*^wsmaF7^u~iJDe?ykgzr?# zW5O<6{RD$SN;7+(Mn7U?HUY2`A$2S|Uo~OAe#3g53M%#^H{uME7sovWLa6jpZ=VzYY#81-X z|8NCbr5txfVCMdWR(S=HgJJitqaO0|LK~9iqxH05B`+Sxqn;Pg4n$TepdNNPuoO&$j1Pg z$`SCkah&U{$DEu@`p;N6zgC@f0>qa_FtTX({aclr|9B-j0IeRUDdcyi8lOg7O}lgS zM=^_S$u|cD_g6mBzqvY^1ky7?Pn@F51|N={r+fi!WIe?;`sclP*HP8O6emxfd=J`L z(aSa+FY1f6OYi^XhWEQlmC*YkA_<(6as+Fk9|p+I0pNcS_AtAU(BWa8GvoK$cjkV2 zeXKf7i0vkVI!8)m;wXHQ;!YrLw(r;#>&YR3A1*UohLmRC6=(QE`DtJNfg2q7kov@N zEYnfb(T}u#9ym>0qmzlrGLa7IOqUwhFe$H9x>d~&y57up0nQtqmY)8ph4OxNvr1er z0K}T#*K}lMW$~hOSm}4Y4wny0&6un!n45I#JHMSbizw(zn4{PM$R#wR50V+JpI z?hQSm5gkg0NOICS+RX#B3R{554?y6v0vEQeuh}V!AV0Cb$;(?jG(D0T3P0$`9ZNdn z0*5*8JFR)|-(LW}OGxv?fm?&2g-)ZV%+aDab2bIYn)rz&R_hh;l+v-&dQ2rW0)&Hl z#{}C=FEg`rFo+s>#%p5@$57VBZpMFoxfejrfjFbrE@sM1AJtZwH6 z^xFLk7cOvmE`tom3N)eC`oNvhVSv$G!3{14%u*lZ4feIUMkMOIY^o8i><^62OWOAC^#3jmpTNyu}^|IrtE zo2F}+|dbPU-X~bXZT-PjnupW@} zS{DMBg@`QFU5(z09QjnGu6Vx+s*$!<4`a86S$l}wLBReSex9$z6j8d$5eq4!42#g$ zW+LzQ&}3_CYk~%y>a8O$@WWsDlJI{o#J}Bx@MornWIF5N+&BIH_TB&d(m?tQvEAmp zs(|Z8#>clfoiHX2cY+>8I0)`neRE%5XF;$56WJB1xsY=l`M{$xkUSDu4GE{XG0}YY z%X9BDAhA%^(`0*gOp$mT;?DFV`e1*=r)y!PvfR|fWGJ_q)vCnvpv(gX&Si7Ww*wi* zAs{daIs)}^vzP!2AVon5@!n(4sdofy1Ivv2s=d4{`f^i&$ZbsWbsly^bQ;W0jclac zAd4EzHw}4nHY^JchM7m#EPuZnGX$|jLTV(>Qsps>a^!&l>;UXI$U!|BTua?|9t>7s z3RpO;A=w?=Mm(*=o|(AE!x=Cg+LU+h4F}A=x_A9vC;dD4g)70?ou*Qmi2#W3*o+K$ zY%TT^6ZzI_FSaKM#6BToA6I%Y0rLlkxbf&t&3yZ&1-YJVU`?}-{Wm!Q%-Snr`hxuY zx7TEOI2jn~gKC1u-9b5Bz6r@Zy*NH6L9f_Dz<*ggM<53DgT1MoQLdcvIM8g@lERUa z{N~P7Buq>z5DRv`#}gxXu%Y6N{@U4@39kVs_SUKY2|~!L^?oZ(9Yw~zEF2^|H#et% z|M}?C=v0Y{R3gW=KYl7KlL~{8!!83C;w&c7OYHZYX zA#3)tT{QvB^V_)H4_swnLRBHwOnXW({FN^tPO;>w-p?wc%+4(^FP zSAt$Q<(k-rb)UstX{p<7j%Ksg)vL(|miifG?bOW7AP~`uh=CLs^^GX2s0{zi^{NOs z1PCID#2al{`Rez3F#~-fwax(Jx&=8s=5!Un6ugBJsejOuET}%PSu>E2VZo{$sdP&^ zZ2`i5#7mT3MCJ>$9lYF}`^u!Ls#^M>s~tKa_&Rc+Y>0`Cof+tZ@X6%YBA|881Qaes zG@_46Hpf!nJ(gX@j^)yS5BcsO3_=`73;Nz`)>03A2vm4H`Y9eR?hsR)8fDFzgvV3T7 zdf~mUpU(U zGq+?adUl@laLOmh#_e7MB6JT-tmb#u2P|@;`>2*fc4?s;qc*ZdYb*x*XKeWMnCtcv z&cK->aVyO1ts`a9SXG~X%Dj3X61g_I+c`N*Emu0PW;!pAC_Idm>V7Fnz~nR6{llcr zMP5yS?PcO})m|I7YCj6QUY%;Wl;x=&6;37YIFS`U(P0 zL#o(#-Kkf6)1Tnqe?3DqblyhSC`UxEaY}xn(xP65Hp7V8P30z&LSi6x8*umj;7r&? zB67=0g}jb7^bJAB!4sxRESvZVK3NKNFVcwmfN|`YtSuQrwZ(5Z#7tPu;yt9elaB?y z+v@EKI>?%sCz94biBWwN;Fb8c|IGg;0`Lfxq_IRWryAZ4;+d_rZ6v=&`3N2E+efMn z#g+Zb3!pD1RDHQ1zaZ0Mm&<4!=#9h`ro8*JUsCg)snKmA-6@nVW5?YfTIeR4QT3|p z)m}N&wKJBzDj$(s&YMMXk6;zQHkEMbW0Yg*Y zt{cTXXC;_?E-j|o=L_10HVmdEQiE|^bzGX>0pZiFRDRyA*~@F7v>&NQ6bQ1Bp+ak4 zx?S{&?nmETYoyDT0$=Mk=43KOD-zNA&y+uxrT3@C6WJq~D*OzVt+HIF{n;4>#l-r` z94t333s1b?-U^FV5AD{E-wpZw!PSL-g7lnkC2yLaK~Ob{lC)u?a(l$yXaqR*6cXjV zttI=BrZv=O-Q)%Il}Keoqe?7MEc}bxYzzi(wtq>f#eLb#qhAls$|?Q+u6fg^@&_M z9&67e@ST`{{PDZ@ejm#lH>5@{@eVT%PR*5|dr*iLsW2%roQ#<0Y%aKc9kVti6|WU5 ztWJ*hqf1gS5I<2VXXn&HtRPB5G1b2$Zj2LEWBZ3*T-59$li~X3bwk1{SFav0u8-5u zk_W4kc-9FiJ?UF&cp}2kNOG!1``*q$l?fMV@+}%p=#;vMbAG%smo!3z+sD^KZP4Lt zRD+LPw7#LbR^eSk+|NYU+8IkYM#>(Cv0IL`>GGEcQTOI(FW1>(r1vH%ayYj{f1738 zu|i%J!#5VEHx=H#>C4kZ)V`)a9Q|c}%)SiQu^d=jw1nok_X$d}6A6sXwK7a&m6kl? zeIDDx>aH)yw5N;M6pd_x$H_@JJzTIcNfK@ZZpZrrh;9o`$DV9iF_q+6=y|hnUHVIp zU5}U*ZluycogeB=caO<4BLfSaLl9&<_7=OxrS;Ax*ecWWt_OI_a=y(Xh_twG$Ge5=KTLAHDp$Z0orzDuXz^ z|5=~R$dlbh5=pg%HHv6yzKF{(a};-`y96=?rkLUGJ~@-wPu_5#G6A^x z#wfm?XV0c3Dc|Vzb04Fkqsw86Vr4J_p;SkZ>zdxMI)$epRGz#J>OODR<|pg5Fp)Ba zU3N)`^JByrvpYL$$3{WihG#)I#B@vL#1fo1Ng8i2la3Vd@_)CvoT9(nHy3M51kGa6 z@4UVbm1bAm?NHBiI0QGO8+m;*@;%DqOeD{48QU~Ip?PXl*_bUoa_0~#z@z|srLoN* z*-2J2{s0}RM-wH@f{T9bFYU4q5cQm0NjQ~|wSL=9r+cVb3TLbGzGKyFWZ+z1vwL8K z*jmK7-)7d&?;}~YtWu|+?QY|~bC6*^5X`-mGMpW^iIb3Cl<#E=oa;WxvUj|Nf_2&K zWGI6H=yEb7U{%ry`&KBkRqy-NO0$@)pvjMJCi9UhDxIyNX7V!x1oktSy3QK7Ln z=1ah@s3TAR`bR0?hMZ`T%gerWR1PO9w|6)9=m!8i>G|&_6V)XGQykGbG;aM|twtk;dqpShKzf6PsrH@@fVg z<)9S1G<3&EcO(xb?zS;IPKl58wYf!OB40!`sv$~!{Ngp7B){7@gFxe1zG9ZoPQOeF z_19A?UJLO_T|+h!R)*V=QkgDQc_Nnslfh4WV^wie$~V1w&CjQ*;wgO zM@8la)*B9UBgGc06kKFTP8p2Kux%YP_=EmOwE6~K%tzNXoG7L{4{gMihBgO%S|B0= zHed~cPXACcZX-XuxzuYY?v|^JBwt<|3BR6@cv*DxmP28+~MQDiuDeR@x%5+;Fw;_{dL{DKM7tnR&O zQg$07`9iWw`ZsQ0#&t`bd`{Md%I0}PdFs!|Ueb@Xf?w5N_lDrtE4L;+j`p=7#mxH) zFVQ}^(>^xf|9B)u;yDyYX;um@+ODo;bG{4`)bS%bYmJO1vPS#TzfBF^Uh!5UZd9E< z`{nv?llMO_9;kSXn@^BV`RLLvV+#wL`1TTk;k2Ux9GTWAZuto{BJIMvPoW#}GaMVD z=L?W>^TAZ0?CA4be~@1`5FV-s=FaSU4}F$F2{Bl|3y5$8WLOhA_YlF7>t7YdaX*K9dcoij2!+-;sF@HLlJM=5;K`fUw zWBu6!#a;Q~&%NQT+FB8$4g)`KPMTk0aYk;fNf}c=B42xZd)-j&x-s&zIOM;7ab@B4tw81~ac`MyLoQNpt!FyW$|?Z49dPh}_~W z=qyMi6awslX}9^Rj?c;^=OLow8HBXEE_)xTevUm34__~b4p^XH)*t{nrwWpoiEXX7 z3keXcpn{rzyr`HTgc)Eui3SWvFi=d)@Y>F)y5F4|re zL2!vFNB4Gic1EZ=gmFp|AFz;ogrUvqEsc$~A|8OliEe8eGSFkS6lN>YtZkebcz(+_ zvH1NcW2#{Cy|o*d?PlX2kXAc4k#5}@8QqOK=R~XV4H&K=b^r1YN9?R)-aoJ+7*Bys zUX4b<>N|p1BLFawZk$*bKrf%lX~hx-1=wOWongaG>j2b2<^_&@y=;FIlcc3AT$zwCi-Lo>x_t>jPZN!yPOyJ0VW4| z@X&5>Yv@&>ZdW3i31HSQgXKELm$v13M<j3362$Nu41lS5GcuFn441e8Fev%=Ri^Fc*^w)vh@Mc$1+y1 zP=`}4)4Y0D9?B9m3t6m-nJ-^(A{-AkTE`I@)Jjdxan##TYe6I>ORkO{Dm z>IqIyP4(^XtP|fu-J|&prjC@-@33EyRNp^L;XAJSd;#SN;ZO6VmU6+f=Qx8V5F#JD zoZ6$jpMv&W=q13y`?;;)ulK~+76z?ZH_oi9JFw)~1huVg$#;4F+%fNcEw=uY@~;%m zd;XRIRCO@}SMqWVcFfnZL{}4WkX#3z+jD;C*>Un?Wo7?Ka&RTD?0W-({0!ZM2v?A; z%dh_W`xiC%sbCcL^aUO9L4^VoHKFX?up-5gGGK_MpQqPFg1-NY|4%9O4A+DcX6@z5{ zZK~XnJNFSWHpeNd8}4;6?!|RL7+HWq`Mu}CZnW@K^EL`5tklsik6sgH;W)Jp5fLG$0afOjx~F7?I6XtT>*m{aT*&QB<1AADVAH+RASEkhsny4ZT%m8!T`OPfJT6g$hUwDj>BkkK=4}gH$MEUab9%VCM z&lhv@Z=CHeh@I3GEte@i@8j#^PD=lKwC&OM+dw`drhCo z44Uj5Nb3g!?aC1Ke`JorJb3YSHHe;KyYkOPcY#_yI59Ynrgcx5RT4~ z-&EUszTQ7-tB$D=!;7{Vtr`ZFf%x9Ls)eu6^^m}4_u>pCy6+oToDF;kr+ZU02yG$I zaXsSA2{v%*$>n#PFNv#TQ_GZ+nGM3dHWOo_S`r4VLIPt4G2YP}sW2^;a3tF1 z&q9$lm-roJmHG>gNuv12wW)mN$@NTr0r|9WPK;!gczzC zAk5r8mirl^iB;dLS9sC}fYWg9Kk_^Vj0BO7=mWY7N3C0>2w~;=u?X>Xjtf}d!SSZ0 zwY3na91XQ|#0?C-ET%`)Yk!9D3CRNhkuxSZUSW<5!j9A*vL#s(3_Si(Wp0}Zy&$QTfE zs67|0eEg_eKIYAQBDH>uE+D)wVmYP{=wU0c*zEzqkkoA`Yl=XPRI&_}jRH0z?B#%e z5saE_;PaH1$bF-Sw#XkqP{f+Nv~{q?N3+bRMkMemG+m5$yuxN=%iMxJOfz zgdxHimx;9BTjt%+Z(~udi{e9r2BjQ$prtV05nCg37!PU*7+KytM{gCZLTw=SOwCLO z0MW^W%d2W(t11INz0ri0Yt$ zU#9R4tLG}Mt%QVxswJhB4{Y?`<~#TKklhb4g!Sqv+x2swFTvpK$Kpzu@pbU|eriRk z+4QftKDM#paNbS*g)M+x5dEDqfZyf<@p|&Qy)9Qn;^{0&8PN~-)V6c?Qot; z&k_Pxv1ZEW2YThr_8{Wc&=g&6X>NN^n_h9E)}~6*RL2@1}!nTn@>wi>6>3{nT-JWH`T|zzVowSQ&gO z?2{%qZKU@N0$T-~7aiUtL(^*ScS2366G!DN!qoS$M8HvkQ|GeK@n`qjKr9Es+-D+U zjb9z%^+IgET3iLq)hbc}t*vrMC9iz(ZGH~qYJWw2b!*t(5z1SiSzj5x)YrH>A#ijM z2B8WRG9%ECpQTyA*m>Qf_cf^KeluWB&n+{5r09~H=sVOcYJD;XhcQ$8hsv7>>q2ozEA=s;2-zIft>Hwu60 zFm^$=6w@8bT3M-(r2lQc+-21$+L|fs|6=}sUEuz&zgm$ER9Cf@>%yl6^($Mzx(`51 z(}@d-iXwq_S@kQyY4}HPZ5$%0V6sG|up)Ll0PRBT*lp%QLqlKH-_ABAo8vrkG2c27 zSVM|M#^na@UY_b7o^BTXiY)b@cK?a_M@MaPSWhyDFk>$8&2N&gzZS`dEG9yJxjG1& zOE7iN2}lNTlZ%Eygx?B~poEMdk)WMLsl5HQ%9GBUwP+6gCP zCcuPXKdFeoR~(Sr3+^pmEq^c_JgBdsaTl3j2?8Mc%Gm#n<9;iN4$UOkN3GD zoxQ~02M+6*u&%NX6Run550RE4>X#P4%s@G=fy8UDAL;>MGC|Oz48fH!*t`}!?Dtxc zG7|OdFVL<{-~z$atMN4IqweteDI|YkV;B3Pkcb|yf@_R`@^Vf@691rEi3yACP8}Rr z!$096BHngBnk_|D&=Wlfb0Y!BY(f-y^IdPU6)-GFu?>v2O7?>$tfTHt`1yN6DVGyB zFn=r$m40>I{%(a3d|zKH3aBj!6gs!SQK%sy@d)qyxcGQ*ZL3tf?Pe1j-_4uD{wYtG zhY(Q<9J}HnOh=~0$Gk5pQUewW2niyxJfB?Swtrvbh9*eBtw7-Fv^pV94mNEelkYZ7 zG}N48h&>rjg+q+)&aowd*vW%o`iBikO_(5jm-|PpKi+kC&@*Syprw?011HUc1@rax z0m$0#B3fswuO<(mVhVlrZZ;Sl!K0ZZqPEyy*#H<6s}}vv5k)#2WnC9fq#B(H`GkyM z6cIwNw_a^q=p|l@Z1&(;W}CT``Q^-rc8T;PqoAgL%)f&26%buhu*fd>o;ve@vi50M z<`MZ5vPnLiK`3<_DC^{Ld0jVV$tyfOT9GF1^OPby6rfg9?JAX>A!lW8e*~JiQdiQ~ zE|B?WV{?F(&<3~%HpQn?M+@r!Wq>kn1O}t}MnE01>w*X#Lq-P?Dfuzpd!2Qhx@9Jy zH+Img>+kOuyyqVS@5A1Z3DJZjfDai0`1m)|Up*>*0GuiYsvI5LtPk9p`6kjh*I^Gl z82Nyxay+2XWD6{KCAI+Jddd08#zgd_+F1&|vAStUkp{pmBJNuD3HB!3qZVeD!U+~Q z%GqVqdZ&xrcGgUhQJ~%L&dZorrQXM>h76^`P~h)K`h695{K>z#phKbH zzu5Y>;3@usmJNYEU{>)s7nT211)QshO23Seg~Z&^NJN|FnD-PhbBd9+ga9mz=Dv?+ zM{Q7ebtTbQBJ$MEY~)hI{}s*ue%dP8ApzE03g_sbd5bz)spf!faF;mlKd6)qiWnHq*50IJed{ggQaBkrxnnWJ8^Qaw%=-q$KfTo8nv9TZ;>U5r5oi>Ni-=HI+VbX(cj zL61hg4lAWY-9f>|&cnka*Mal;2=!U(10*mb!iGv1Fv>X$M1bYtj|QPk2qjzKd+@21 z+~#$&ay5v|GGW=TMyT(427SG+2nBf-1Ygp(HlQhW`qp{@NtW5}C}g%Gm^!&Aqzz%w znL2w2I0FLatq*??Xh2U}v+Ak%zrU2frKn0O*r0_JaF{iK6gn_4>Be zE>!?W$DNA_f6Kuk-ef_TwDULMNzce&?o5YW)CU4LOV~gK(D@+#zTklcHYo0z5J#S9 zno~0%69Y2b3It7oHO1dnl-Yj!xP?h+A&apF%Zebz6h2I?N9^{S#j3sU_7iXG2ATOG63s|Vx-6ZPF1b2n416>98jC8b0(-ah>nNp30t+FS`vkY25{&AsM>31-G>oafm0Vw&l>WCG^WTMS;&`-M= zr#-;th@Fn^ER9K#2OjwIwZd&P+{}r2X87@pbmKIVFfl7U=ZpEtbrnlIA<_=btng?}KXcd*58i z2DFoW2VAUJ(Q9h@C-eYoffjTTW|IV3nx8xZbL@U9JR4V2Wsf=q<6`Iht_FEs!St)8 zo4ztH*`+U$30;KRlb95lFuW#l)&fOH+B1&Wkaw8j#2@bVRxir^yZ{Xbxp>=`;V5MC ze9Ia1r6q z*7hL4&0`fdWP*{=h~nAakf%$MO40@JP?*83%#I(;x7r9>Y!06jYK)R=MnQIF($?gHiv_y2fN87Ne`cNkK`6fDi4!U z&%LZc4jMhvg_m)?`EXzHlzJPWbA+B4Q#fjPUE<%diaC5|zX?^zq ze1CO3>q}xV;WOZ%SF*gu+RGiQZd)i#2Haso*1ZCKN>{Pv8G+x1mSb3O27WQht)QRY zWU(285@lpVu$fMkG7mNTAa;h|rbFx^&C}Z$())%$ATWCOC%kW>cQ`FSPHuvGiTb!2 zW#!uQXpMI`Yg+FuFA({99W3ZeR)h&S&CYg)#Mdfjd>`|3Me%`Ra!E|bU-^qC7uM+o zf^6B$4MQf9YRwmga?|hhr{gOW&^$rOejU~O(_C%#>J!Da+%77EpQb`r^@q8i1hT6f zPT?%c?`#BZz4F-TkfdAk04t$Oyf`0rr5>tX>KK_8w5$WWo#_z8yDS~Oc-2}Gu{mV* z&&Y1;wYZkh*lDr9;NVkuvAuffsQnxCAuRj3X`!AW0sT;uuPW`x=fe1_=4E{aYs%5) zZz~)~7gQI5i^zLOC`C@p4&{8~x6w&aq^uZ7j*ceX!6&=?f#m%y#vEmwN5NE?N?5_S z1_94c8RjU0tm zeZAMMBwN1>;B6tXLU;AyUiD!}-LmTOw@kC#i4hSKXm^kCu#4B%-dVF4@|F<<6D-Q> zH5z^j=la+{vbEEq*BGbtDj+9KnMDO~F zQD-V8=RYr&7?thfR9)jvkdyV74?JgTTqH}oXdHg?Tb&F}>j(+I$i&Oqb9b+x51U$B zH>7nm@Y8O)>*Ku0W6X+torlhdR;bMqK}(fvGAil>wWGwNKLtGd@)LaAYZ*cqtal$& zNU`1OPY6H0SRJ6rYMm`|vvE>;lhse0OeNzimpfY}nDnZre0=u!-4C&ZZN45qOj^8{ zxqr$^dfexZZ{p6F%duu_WRzFULJzjR4vX<5Q!mq|6{QtcSRPSE3p>&w001Pr3$e9~ z%#>M&jiPGlOV2*ED$b^bc?=dV&3rF#0)-wK3I%XK6F6ghL zG)TIx_RYAX?ME+u?|Gcj;7#Za9FWRueqDB_nlUdjj=T^m(^5fMlNjE2wGxda>Kf_N zQ+1{tFT@{*U}?nrsjMPiObHqCkTIPXvzriN(eXax#}WY&sDjDadtrqAR!Gn-y&9jKFE^SnlrB}O#9Iaygm+9OK&6&G)u>B-(=K|6)LotnGRsB?~~SuNJX7jU)0Aqqq*`e5c7lK67mW|3KLSHkhI$y;gPRnV(ir_S(bGZEf%2DfNR{uO*1l9f%uPvi$VxpPqR( z(aRc+4y?p1>x`P?bU&kSb0cLtw}Q~d6KBA6eArZ_)j-~l2@#VgVzbw%oXB9*LoA$$ z`E`Bxyry_KxVK&O3^GJ5(5W;A-SRzsW7!JlgMRByZP*zMkHtjQ^EXMTNyk>i zW~x|r6a;+!%P)u$i!mu-pN!^EG{DL3V9e<=&>5RO$g6s%=O3&T zIT~g-9@@9nv6&J|bzn0L&?9Zemg;UR@ zhW$uP^v=*LGvy;ojAPz$z~mZFa5Eiikgl~py6jR-7EWRv6}fmMQUkR%DIKS+5q1~GGcLQ6*eLqFi->! z^cnySbsT4%`gc8&^Xw?u39=@mM5xDcrl!tRfM9B2xV#u#-Zmc5k}8PU`9pSR_pxft zy7B+uCO}8V#m1pC6lvLR)@I*1qNIaA45$rGP|a(Fq4<<=O8UGUh#;KgCwVqpoN56BP`2T^M$m1x0bVYjprgpL(CB{qKhm#Rxht`Xw{Cgce}Dz} z%yFr6M~(OCE`UZCg^@$_;upoT|B5qzgTbGxArMY?KuX)+qUe7DgGK(Jcd@$Bxj}qH z?n;=^jp$uA$6v1>k>ouTwxWUHr~UZa!I8WpJP=nyH?MqQhuY6^QV>8R#5=&~017cyvqQu)gWozFy%xx# zf6r-S_BLXloFwQm5G@2fnVk{Q771Pztxqca3ZQkQX|3=RePtJ2(jJ{}=F;_&kj~1CUvZ;j*Hen_Vgj z8;IV5xRL&t?8DB_Jzy`P#lq|M`-q-H*eV`!F5Vhd`GyKrCJ|1VR7m~_fb~95=6|>L z@wn-U0RdPG*o~Z~ba^h9=Lo9KbO+O*8l+Kzn3O@QNBWKNX#$GOT(V5#9k2-fs*uPx zo^T%-mjeKqcvWv#G*pCr&{HXQBI8rRCj{b1I#vNK3VVFLiokP`jcMzu9$)@Q9R0af{4_J~lQI46 zu3)l^&sT2lKV_gj6#}@1=lt4SIM#!kcMxZ97|O+-e{&?b4&(Lt9yGcNs83G6S=uw! zHICuZw+43v=FNdSmr4$xCGNX(2B!v zhQ;RS@b!~?OgU{YU~e;iama)_=megx@L>=`(((5oizWvxi*l)yUu14HXr_=HRzz9c zADRN7UzDTVeJ3B8&+;B9Jm*M0v(`9+hzB_cUcburd5;q)rl3dg;MrC1L5VlhlBr#r zSRu$Ce%3w6{3_XpR?MbiJ%2FZcZg;jzU^d zu^9UQ)*jJcTyQ=1B!et-eG(cvUO%{fa^KzWNHTvfINQK09H zD-5WL9`$S^Uarh)sHgZn&@b0+M z_{k|dmdMN5YuyB^DNs-BQEp(Xmd#Pg7jIn}Xdt~f-OZY(oYU1za_&}b3!E)UWN65e zif@8~iDPrd#%nJ@x@+sj25=OJ>#ss+R0%7W5|xqIpfgM%gOm6PGCS#)9-3$P2IhC^xI94$b}18Lq-6!N-Y1`iw#=AuMe^CdR7zH>nQ|0 zCrS`Iar!EmS6!=}c9Bhco1po?HnTV0Mi4xmyA?a!G&XMWe2GHNBVpXtii00r#^>YS z=(-9|wku-F_VOpgB)b9qUhadjkHZ#=u8q%E*%Yhbo!xi#&r%rXM<$gm3>3X{i<@e2 zj`Nu&Vz1c)TcnCxA3B`E==^0m>{L>CMjR|n@;+%-lmUfjfk-A`OAiPF7`)9`XH`6G z2HiTD3n6O_ljjz6p5!tqI$WaC)xP%F0iX1QDC4p!revPti$>R`D8X#4pH+}^m>ns} zVF%__IQoNQg|7`wl%vNwrbSDQu^8!q&&%Fw-Y9K&BN0#Xm4;MuU{?BVX_VH)8PTin=s=xKi~*#SnAW%d*; zQyu;Yn7fscy5O0or=E}O4VYLf5hDqWa(EqEXFc%xKA>&8Wh zZ_4*)Z`Uv1b(PJ4a{hbqErM5@ZXSpdd%h(k zLX!TP#|z&Scsp>HHxtr_z)d3-)X{jIeY1LJ)67$mGr4Cn4U z2rzigJ=#F}ZOgNDswTwZfST+ZE}8$P%-d8p7bR_?>_#skvvMagcsrfH@?bUG%fXR3MQ>@_ zxXmk}$x7DgJe z$!Sm#+0~ZR-Qu#8ZHyYDefj}U4;4fI$dsf(bc-=nZzBGa-j#ZTpnlY+jj>y`p4U!y z=NL0d7`>Z(d-^1Q`g{(SLs65E(#lfz8fec$u=nR*3SPOXql7`7tZcIA8KnBTa+<06 z$8s7+<*^s*udapgQbtRL#*(;kEYwIq%u0G)A(WmLt@+f-=#@QV<{ZTrOsYNY0R1s( z?C-$(NoRE5VC9-P+|4vXFVU@FZ{*<2WIZ;zT!NN<9`lGbeqY> zNrpA{=~uvk2)xN;nY*Yn=JM%IeTU`1W08j=Ha~gV1+vcc!<_F$PUP?A*-OJ;zzyqZ z%4GD$MPOv~kW&Y*aUHiZ~(e>(3UeOy$x^=?sP-1D$2u_&%KYbBw$^aAV$ zbU+f|ob1CWPqK3@n`OvZ70Llgnv&ENE9Pa#O7nZ6_I5d}keL4K+h;rsjYW9f;eR^> zjMJ5QG`hY~8-(={8#lN8CCDMMB|lRE0E7dKygeA$^8I0! z3wcptn3ZS=ajD&nC0-HcgWX{27xQrVM#6fZ;aZj*_7X~_#k;omaICkGsGCNMVZ`?t zo)`5+YXU{mabbK1mlq3d0jj)sOqdOEl-8F@7j^|$H!GIJVY^#caON23(w*qUe%sPr zx^qn8He`{DGwykudA8M_Mie69S?J4zwYZfm9_g1urBT>Kj}PS+pUQ@ku!rS~%AA9G zR5a6V=H)W-*k|DoukWB4k|Xc!(Q81MA63s4@VUHSNnOo`^PKQN19K~!m)t`qCWC_= zSNTHg=$xrG8ok_Y9N=XJ^}$ac8$ zibfU>w{8|*-@~kwz#^lSTdz%sZx0DC(b=Xl}*)% zT3F3e%^H927{!94$NLHQ4G@(*suacZp563_o<$6DLBFs-L=8|!xS);9932!fs}JI0 z4SCyf)o6D*P2Y`g>G$OeN<~`79gl?&dLHV-DMPxLW#9fa)%PV9A+-)a4f;E%Oa4zhp3gUxE z*-#PAWRZPuu-R28mVV=c{@lfh=7Z7#pk(RlSVi0*6^?z<06N}sLA?r&-Wb;Jt05A} zqdV<&#?c@tQ1r$AW|t*PFCjLw7@Y0+yyAxK!ONrZ`xHLU99Q%riSzHUU9qAIzdiIA zJY+5C8rFbkln3%OV8R|O&i&d+k9D{lafyl<<+0MN!7$7S^gELuv`K79=sYk|qifd1 zf|y}Q;NaKHba-7W8`9p+H}GmeJN|o7I*kq&1T1>&U+~V^y}#74X5<7hc@sgJ89Jv1R5WiIEY zORScu&p-)87CZKJp1?(@y|!!l%w3kj*-qT^9^7;qax{6~`Mvw&pQuJgn5~#g~^*RjL#HsqO{BZM^~4yuc^2A7M<#1fnNH&#|6fe0>eI z6hn#YGj&z5MrOaP&lihoTeY;|LM|b&ru_0Cy}9nZMy{F|b@PLxvNN(|w#aRPv08&w zmz5I>z1ziDyWJSAv|0fj5ixtiiLd7_Y8Hy?@>)}Que_5hE36M?G3^TjJ#OTbLt8uG z%;V1E)`h4nC%S8Mw+Aa^P4x)#t@i52ZV3o8sOgs>yK!bx6yt_o@{QfUe&0%qaWN?I zP}}z6j?=flIeF5gkY}+Td#NvJLO#B{7G^lRyos|Ug#`}ARqGYd_7@}fQiS4x8ofyu z@Tp2gySO!wRWP27;o0pfxX$>!0y*9&V9z)FEbVBj6N|_>uKhHyhE~jVSH?DQEe4+2 zMh*FZJJ=Gi9Wg+w+@0u$;xE1vRn>JaOP(_evVDu1GeZ=KT6!`sKb%ovDaBBR3qml= zTyMx?=E`wum>PI!u&lgRT+N}Q-^};>{@QMah%KF#q4H!?2WnHV&aBz)j*Vw2bh}5^ zL0DtTT^n~0m%t}x{8*m6rDBd$O7wE3v?84}bZZ^GUcVr;!_3ro? zstd2RePVi|n+%i!p@z*X*${-!5M)A+HiSohop0h1eW?2qM2Nf$bY>thccQ@9& z$odySoK}|hyxCsmP`uKG=FHA>V5{97@^lrg)v5C`4I zN&f|{xKi{wQ_ZcHVPqSj3w?5^4i~!|WA1>mYA^P=B?d69%mq1orxBnr5yAH%5^}(7 zA$3m1+EW>!@&4#hXs=T2Y3Y8Ob<71bE4Tvm{BCgFuGg}NDpf=zbR^N0U}Bgt>0U-w zrMIMg_}s_Y-V=+4re2kv}#!Xzs!zo&YhVLu2xTi&Po@7}1-)N%G2kt%f zJ~`;m)|Y!z@zj|4;+ehi6y4y=-Ib3doATnNMZ>k8>w2TxVdM)IJ{_;4A__RzgP`Nj z<652nR`ebXMFvWIyc%DzDzm@dQz0>6AzlkmK?z+z?Q!7F+U(+j2;$QI8vGh@rwjLU z)Tl^%{b<1s*k@HRt5}6LG^<@`x9g34HRhVhku8tvijpaA=pI&#Qt3Jno;+7JDSGzG z_4umN#U!3;sSK*OEG|=AA)>BMy&PxmK7_XViBuBr91O+nO z_S%5gZ&z=o2z@s@3*EtEKdY=ETRO?mYB(bsun7qB@8}1uIhnQyYAa!{o4Gjp^{2D1Jm;F>vKtr2L_ztumN8i@^SR~j zlXS&pNZLPy!1GJGkq)Qo#0|)J*W(K96rVlv;rQ5U!&y%KNT=1LPl&<}HL8@fJXJ*I ziF7r~=1Ch#IDv(;*>VhTdi@GPtwhS=AWBW&)6mnhzSh0+O>i`|$9g;q+6W>(C$5z4 zICcmUIk0t&WUtsux;kD)RO?S3wvOXiYDZei)vxPX9L};>=jkGXCW$W}DV#Ie7xd)o zqO5^q&DT6!uH?DB12#(`3!#;pI@;&z+V!Rn*B??Tz8R(9aZsX4R{b%E$m%b)j0%fs zn=UK(Vx_8ek1TE_@WUB}1Xj!ve?g#R7xaUchUi2JDGKx!BQpQxzm(clb-x{p`QK3& zoz=>X zyp`vZ{OK6hQo3TCPfoMyjG0dk0KR%;laK%1Y1KCC z2QzP1O464IeMWg*sje#d9{Vqp$drud*TjHL1BoUlg=1^cz-{BjGL3dqu|%f8_Z@yB zA4!;s`@4Bu&109Aw0<~hBK9nqc-vcJ#$n`Y!niOG4G1f=e0xk#9a$G0m=O2 zNqDulKI$QT9%g1{l1&AAHA9f*$a~TTgBzRw4i+Pe-6$0BCGWmDs7vOL50M!)eK#7iks=OKjfF1yEh7mh4 zjfD7NAQ`I2UY`eN4F5az2zm9yN8}6Mqrz*NOOM2wf>dmZ!76<@8L<%|B#Q-eUV7gX5TxTz=6sW z)DC=|j_3G13!uIq=|G?#`MiuSU$yh2aNV(%j*gCGFwOrhqb>njmZULM5HyNKiWkUM zx4?o-+(lLV6L`I!HQdpR-U8g-`_>=pJV6F(9mvB$4nVnc@UagHy}sglJ@5!YsW&N7 z`CC~@%?72;d9xDfDsiN`mTPca8DUHpqWO0cX!;F`f4e;m0Rb|jI+HjE6|7B5Yk~z8 z?j!+cNyDi8S5Gj|ZeycA@ty`&^CWmNI1Y%HoE4YcL+<`S;v$-6S$4@;@_m&c8-|E{VW20c! zX;^hsrTnWQ#(Ppc%^*Gu#2iM!0Di^D$cSV(BU}3dfqY6;QP>RR&MH9Mw5)!S7efL` zs6=8O|{PWs-beg6qo{b4XQcIwU5t?AmzAXAQ6c8L*)fd`LmeKxyHGM>npb^^89O{^m0OE zx}y>jpb{#{n)`_UD&S|zZ|62=^;mOt z5ZM@i!MdlmiLGm2YvRO3qCt!n4D6$~j#aSIz&)nw1}xyL%!zkIcrT5V{yKn_Op!LO zC3WI*JbJHP{Fs4X^0QGfwBK2PdCOJ0n_4MF0_of`iF;Q2?_jq(cix6du?ZHyZu#p( zAk+tFB3SiG2EtPLtZ}CUh};5a?cijQG~4z2VN*^gJH|w1Vl8_>tA7jdB5a|69Sn92 z<%V=P1}*i^bbkQ4Me_4(gY!{Nw}l{js+MwDr4EXq=eW$U<1!&VTI)_z1%pg`M4ux+ z=wnVr`$u-eXBf0#-2^IZWp9PNNwkm=Je0&7ch~IAKEQqa(J~Q`6ej&m2Kb;rXGcb> zxrO~)enmdl)43enjT>OJ(cUhkw}As!!obq`=8OZ)s?+PkT>e2#&Vz7i(Q z0jsnmMYdNa<2}?~l!r?ZTGpfWFj{$ll+hMt9{>nhrFvb}?8_h{+#8_vaj1)82U2|< zIHmkUeO*MVufVjrl|Wr==NkEk{<=GKtM)bf+|XnZ2PH7Qy{p4tXcHY7aMT|su@MMA zh`du>#9nLQ3hGY3fDBrN1Vff;);Vqh3b*3)AzW1JgX1NLKt*_*F2(TxM~|7mt85&Se7%EHyLGaj>=`(M zw2;-9v}u!{zR9m%yTFzGEZSeLKi!%XxK`KlJ;xyEYhDipo^Z}^{OVj|qlLZ&7Z=yG zi!(vM3H7$_r6&`OZ&BSIN-XxeJR+*&tieQ4zd{%Bsi>>0Y%+_N>EMxk$i=QQ)Jxud zH8XB{BFvGE0d|JuK7%#{!Tg33KxPcYHC4CxAK76B3U7g+SHq#{)*JY2Vm^MnGt*ad z*O#ftjDhsAdoR*vxVo&i@YTAL7JWsk8hDNxAnH!JBvWAdsXLNZ`!uj9-!cvA@a%$7 zULE)WV!XdF$kGzsY;fPl;=4u4eAom#PP}6px^5aihDKClNC}9!C~$m=V2rwvr5WRh z$3CdHs^%#a^?_=35ap{MreW2yp4Y~@o4e<<(=U>u(7!eSYtVlE>ZoIo8g3ws(yF5o zd&-OQSR?EZRTd{Ei7$b?LAVd^u4!_~v#7_RQn3Ge7FS$av`9B^&=RsR-re>BrB+-5 zb!vOx3+$?R!1nK0-D9LyWX%`VqwpF8U&h%R6=l8TJoteIyOIes*eI>cvX^@@2TN|d zI#Z^hyqm=z+69POqM7GqP;xue%B2rf-OWRP)NR%Fgvz_(Sd#Xb{HeSWGd~XPsOqB+ zov|VG#FKs)tXpsYRJ<6>-Y)@LbgV|z+&LxRHq_?&r{sCGlqpMrMUp32P}pkPB)z^9 zT+bw;D*6WxxMl~U)LQ3=mhBlxL~DKrmbzO^`oj)I>u2;SwN7nySNb*X?G=m2PS~Zz zw96j@ixjyIpN@ZH)-0ZQfAdz-4uufH0TvktEAqglr}aEjqAz;S=QZL9`BKXk9PD}S ze)Y3Lp*@G)B&(8fxrmQBG%Vk|=&ENSIWW%|Nk_3RuCGyV6usYFJg2MAjI2ca>FEm+ zXh>oCw!~wpe=T3g3dG3cevOP|UCst4lGA+jWzm>AxS>Ld|BCAMn>ti8vU3-NI7ezn z?Z!`U-{#)Gn+4vBp&=uhGj>=SE`Cvr zl%pRGZH&gO1MX%$QMR9$D{(iTl3lbJu2XqY#BCF*ey-0Qac7j?GAXTGLG!pI4jV9G zWE;3TEh=R$`9s+er9_%vo#%?;dIKX>$PiOeI4)m3N6{6-CdmE%?DI7uF9gD?NfpQ~ z0>m8COJ+NR#vb6d36wI`ZmpC7IW<%5^FWps zLcHF`K|azo{MZOe#_WnJTm9*{NbXAsDm0X2JJ(>$;~dlY&Dr)Spm;Cxkz4Kd1@qY@drcG%@vWA#!WtodB)$E+^6Um|Bg*v9XR0M> zj|J{Opz?01i8B3s_RV_ICpZnCEsgBSZjSA|rW0nrA3H2+IGNP$%fssuQM=v1%lDOO zKV`!&O^n_6ud#jb1or3d2|XMQ4X#M4i;wk4xTdqS{+cbAIasPxAW=(P^X8B#@FPRZ ziNQ@Yd?m5}d|B}kb0U_r;j=bf#@fbb$vVVs%j5_XWm+99&ntY6bMrY&iK#d7yn7EF zcLk?vr^~>%pZX+pEqjY(__HUS#y{l)U@0w9$2P04o5s!HOWiQQoDWx&}cing+P z4lPD4W^`YAa<4xk3voQsFgnM14|W%l8tngmC4{>#2XoX%-7%v7fQ6Seaa^azycT*+ zGFPEFnNPA`;mlHArPm+}ml3%CaX5Q+>L5wc-(b(L+0Ta&v#6>u)Ab>2XgL=2u83!TA*f$0I^)Y54rIcOFq&yv z2bJPeW@~HE77v=_W>uYi_NDx9#8dQ@qBe~#hlWY?t$)|N*rvB?L&KW%Z#gFV-jJq3 zuol}_8iQq7FO3KrnSDYRM8PR1k$1eJoMB4*{rb-1JQ7O2<>|;4PnlvPQa&Dr&@g|# z!RzZ*cdbG%`|&#?gxUKr6by#6_kVs7YXC#zJL{JVV=3Ymikd@=xn209W_noJN3ZW= ziP?jQ0~Oh-Mj{Hm_Va~_n$Xy9*$wbe(O;+3Nuoun12P^f!tE5ht54l?!ve{iw4fx0 zsx~5?(x%+p?83K$Z)$Poyx6WetjlP%P%>``Skn!J+*BNOhu`y>26p(4&Zv~0aLN`%ICG(-9K9EaM&cf+rBgcO>I#=^8X?{oHZ}`@%$CW zG-zG2_#*Q;`%Mzz zrJ9Jg67Ffnh)Ea>O4OJ?zPy^$QeKYZZ&CN|e$ZWub9(=t3j?8sQ{m$a_{sO&ub1yB zO&I*Ha!cyfRA%x62^h!d<#o58_@}$~a}brA>WHQi2;2K;E$6ben|R+?xNY#w{6(GwbS#_XYS~#oqtuNO>kcfFj__&ug?5X<>_@uQK|d z8wb6JRLER1t?!T}iL%Jp8O$;Nc4fge-e8I#xNXnOp8iPKN7c4!{fo#s4Vrqp(XVed zf>&WF+97UT6vY?w{jB*y-vdO$5^;E(>!rw2eN%!^9Y$fL=VXB}*6~Ni%q=EVx+W|)=Q+(p7zN(^7iwQI>tA#kK{nKO?_xdaTgBxZK z4mhaAa>j4Pb7Ts(Y8|;&ezwi1&33SJOFT_^^v$3|qPkzP@Kb=&$<#t)1U^ zpVtP-!HL=HShZ@~Hh&z0_0aU>tX@|$cN-Zf$3sLCKA(L-7H4a4&%(5_&~w7ZMGx3w zgguXN^AGrtm;GsS@B^~T?PrGq+PD2LLk?{f5RJQ=$rAj_=!2i>vaJtpzUecnCOI8K!5yQ{MuSUoaWDU0DwJ^=>sA6cT;uJG2GkKS)G9LtY|*T0etb3XHU$L{c0fY92Kq{H7M#3P(1 zqr@F!eQ^uDpypAlB(cnM@#c}9D5qtumnSg`w|*_4SY8Z|+P?^u+&WO!?DJyGc}0xU z=Or{t;h9)X?8xXzS{iL*bC=nU@QGV$$id?s8i#!`PtC`=Z`n0 zX5e=;Wxx|R4^t28!N0-nm0{{_p^~o8SVN>glJz&b5oW?Gj^t57 zVlE!)d2yzT$nRFYy-cWrez<-;7f%g}HD7)k8=vW6JZdsYbg{*Q<+#|A`iq56ZQ5hN zvL4v%gSt+*aHeaT&ch+!(+A;k5x>n+tIg(E?Ey~De$mbU2u z!CV(g)mC*A-x78s9^6X0)1|EC1m#e_c(j=6p!{Jr+3i z#cD>vEyADP`dz^u$)&GUizgP!lRf%NXz`CE$23W$(qLvhU;%&c+N8@={q-_*(f>&9 z9A|jvM?s~%jkU3>pUVN=aEbz-`jIw@b)Oe? zKsdT7JhX6Q|1A+#7R`p-W=DGxfDDw9AI{=6Wkr0h&D!n#oU%`GP6-CZTRu>AF;SdY zD63xdwewV+vLl~Ak;|O5dtub$&*5WDZX`YL-ZVhD8KcuNI&5EHDrRGZV$4ST4vuk6 zHs7K!N#W8->hBTCSuk9}&F`2&j(M;7nusG>wG@o(q4EnawAOU&2BJVO>ACZ~|E}N2 zM+5xc23D~9>sX|d-e!BxxtmFi37S>S?>4i0J=+TQ7mF&z7sQQScoxof^#Ptt{`P?C zywABo^?`1@#fsevl1y?R_gLK(vN)6=%VL;Q_75%Irvy)>VdB-@m7MYz57*>%FZsQj zKtvCZt!|^Nd-F%T#R_8=>-WV;pY>OSYjTQm(4`?d)RonvwO2*igJmmLFQ$g9$yOoV z?;ymx)YS!}BIRUomUEJ39c>I4Gvo#&2>pBBt#?QEN5_2|>xY==z13-UGmhi!DK%tP z!R6CZaPitHAFDhOjrh1Ds_Ct_Zg%x0_m{U{H;u*EznHu_G7xmT$}o6K)GS1lL2P){ zL1Hs)p+0T*R7`u_XGj50$)Z=e?tw4&(T7o89(JQ8_CKYx0oBkJAveU z$sdC)8IK*FDG|C-7-ZhDVZUSChnP9A;pfJ@AsdeLnSEr1m^)%iP4;I_9; zjSL$8#1v`*;cec#uMF?|3cl%Ny=pugEp7qqEz^Dl^4F66qSigIv$cG|+G?k4`tLtA zoS>}35+PG{mS`cHY&82lkCV`*p6@(YtYj{#rn0xDpCUqCxs>gfLMdGE9jmpRQYHSb z#2%YM8L`Fp^WwQyw?4)O9wxnnqK51#Dqo7%(YMt0kKEtk08DI{ke=3^tzYzz zte_CN6%<3DVfqT%Ad)Z`BG@CI$UVDSNP6dtF^~~F!rmL( zhJ70Tef#_SkI)izl5@vrYwppwk*~E!4F}#MsqD$x|5*%Fg!cZ^6wbG-e_6YK|G#bq zkC-t%LG`NzG;+fM98Us3Y$F3@QbLL>2k>-&h{su5p$h=Cw1SEwaifiuRcNX{pq9(e zwItd!@PBVGa^f>uxoHf*qVoYH*62V4vpR$7rTxR3DdB~@^lt`SW(-YzM|EP5|C>G{ zifzCqWYZxA%~=SL8RQh#<Zsi3atO<%7n0;-}Xli$5GiVeun6X1%(6wCdo6mIk+ zsJyBZW{iJbcK-blHO(S7klZZKO6xECrgp~xZM|b0X}}2~6RgBV-8aSvTSV2rKny;o zxChjgH^??kEkMU4(N*z z09aONSvFFiIqbwSJs(gl0MJax`Zl0Ig8m9p!zhSXBg1C@G>C-N9|>Y2g;OMnpTup& zz+q4_X7h*F_2uF@h?el_IsGu3UkGNuKX*1(wP1U{Czdr|ZhVyY;Kj1BBc^ugyY_Bm zSM4IUp;LnY9^ij}hM>6 zXy!5J{2u7_(gN_xrX21-N^r9H+*k1X-S^vX+mJ=kHr}Z$ochw`gLc=c?6Z*TyZ9%T z8hl_U-U9SSlSgt4a-Rhp-w=CDtR!b`Z?FT+M~C#w!hi%;sP}$nB(-221LD8)&0jy8 zBWZ6W3}be-fOW3g&eR1Uw^~5ZOX8tybw&@}N%CV0mFMR6&_8Q0-Y_Mj_B{gQ6>kZ_ zax+#E-Ikoz`|ATj3jg}^n^Fj0&ol_XwXr(?G5b(Q*ZIPB{19Zl9wIxeb&%Sfq!fKw z*A(-HiGeF(yX6G*J8y4_;D5N0(!~04JE8CO`t!;v{_*0ja?o>89ZxNeUb)>xa0O(EkBBeAc3Un$PeKj}={au+V{sc8_=3R$>MCcQgrqWFRg92aV)Ri_AE(-v*KKQDY#(x|QktZI~%NQU#lbS6UxTT+frq zge6x`W%+lSZZR7UCq_RCO=$&Gk7D=P7+%yK@%7R2NPbNiA4QSto4WauYsgE7~!1AQ8J;Ls}8%&%;>UFWZ`_1cWKh+rU zhdB9unxF``4`WliD}!;<*K503Q|lmtlsS)X$4W3o+il;APQSt_7+Nko0j4@ zrX4d|elST*n#ygiKn&vDW|Kwr#=>vU0YgF?;-gJ|lf~ZvhG{2u3Y2=}Y`=I80NU!% z&daOg#_)M`cqhP~FO*;dSoM8PJv+&y%KYnttkqyvL$b;@(GbkdW1|F_oPHePf0Tl?0(&%dxhU_VQLRI!Qs^T={d>7)fOi59J5* zRP9kG%3v!~ua}}3kKZ6k;vH7z6WHA#4NuYsBKs#PUa!rea_rHqn$p;+QUbDx$iAS9GovhUB{45?u{(k$HA0icO}J4a0fTHf-TSCcU?sc?HOS_mkjkHNQSp&vmd*T~S^v3E6{! zIcn@FY}y#K4;mXWlEzTDX5n8W$JWq8Z>q100{NTC8;c@b*$Ml4qoyUc@ z><5rvKvkMHQ21F^j9;00BkU0snhmO$#f3I0IviaOy$C(irJwZ_l?4z)Kc;44R|)ej zn|pxnS`!vrJ`v?2ExMi`Gqosu#9MI1&s7!pX~&kpuCGY0k9ES26&b%bi5Cy&UX!DA zfAoS*O?n9_=JybTJ}AI>NM6Ry5n%SMd-923hnRfFXbC-+yro&~Va0=UHfMU<-JsJNY)LICX$eBocRQ4J z!~|>s_%_MrhOZ3R3OK%ZgI4$70;?=WUgchmTJ<$Nw;0K?xY8-+x2{-b_C&Id+LdDM zA*K@F8jX4-pY`3tBhTroQ=SfP=K8&Rf6aCd!XXExwOnOai__*-}R zScw>h?D}e=TO@G;1dQv5MdLhvdR-rTEtXEi*xTs2Oo<*&4;{ZHIcJAM&L3XuE1a9U zIW8{TA)-ZZRh<`-*-2%~$y^Mx_%X-d_>Me>O)uw{xbiNkns8|2-}V&;EZzkv&AHvE z&KFN#^!MqQj_uu{g?&9K(ojl@9UdG9J(K-7P2p`uAEu5@TcgFwm2H~Bin6m66ki{T zUhj)?)>FN#2ImUPBaA{#ujR(&1WS_>aa%E)8Yx=;l2LEWeWE~A3mg7}E$WTav8o0F z!_|AzHr(QY__uK}cT)^P{6J0QNJo$692=$w`MAO_M|tDr2aHJ(0x17*UA$76_EKwa zs_=B-7}x5(=O?H8snUdJX)uvH&{Iro zuy{pry-P8etR!-1`&P{v>M`7Kz(S8swPHM%Vf7J<=q!Bt}E6I`|R2ymKLxjsnM* zU@8xjpf-wjhm+MTwy5iA&8LEJvhM zo#D>P0kuYc6<1I z3m_gsDQ(Vfl|p+~JYLuohoO><_T(H33OBBxywd*{PH9poN;{my?Cens7IQ5%bj)v0$utv9z^nR*C2A z@Nnqe8`D|xKc7Y@v!HVKlGD-ni=f6;wA>Sq!YAL;*|@1wKw=exGQ#e$DjPHPJ$QHo zdVNgm6>j>DsF6DU6d3unPd_ZCO`MC2^2;mL8;K=m+cqlCJn-~ZnYkM=+ur_e-k(SV zlX#DE)3DtlZ*Dj$@-p4P##3Aj>bDS!aekwjP&gGL$e3g}#MuOdBi13IP1w-=T$oh& zL$X_JeO49^4ktqlVEAlGUpbiYWpzy^${UBwl;L@v@$K6_2!akglQ2ERIWsRvP z@0)Msu)|5A->-J&4bFQcs={Q#ZypHv-M#78oJ-^aTg^uxj-ihEPpo#oNfjmOo}}+2 z9Fce`O0*6L3KKxx@NV1BKk}IUY|q|f`K#lxUXCo*{x4yKSqnnSu{HKaoI$VN>&1Gx zh#p&J{Jsj(Y|>)TJ4*K=Wd9D_XqPX3MAO!DZP4x;CA@?TYv_b9>=;26E^q~>!E zB~S;;ihAWImU4&5Tc`P>w^v%3(+WX8TyPG_mT{3{=Rv_}P!t{?x|rEbXbHv7E3XRs;&JHk){UwC5u=C3w&UJlRO`Sk z(Ywz+v-$5^qtt%otXjQDRIB=797t9~9ZF(v?J{k4@3fxUF?jLn zXvWcEvxdv@@YsScaiNA|=0SSLh+wCzVOiHXsgZUj7!_bbNUezTB z1ix_R`|6RL4qel)HonZYwa&tyqVz!gef96h16zLx1q5vsC&{O7P5$(_@rtaG`rx%w z`Z3-jFK*kw`Nyd`S0yZO@lqn=Z{t(Lv2b4x^iRZzv3II*xNx$*iq1kzhxyWba|GvS z@+!%HvB$TF9#(IDhpzQXJkrLbU6&t^>0cjB9*E;vKDvZX&HOkVl6p(j$~wY? zS~pFu=<>Ob`$EC(aUEMhYD$P-4gYx`aIBkD_gKPa!{dkWk|@Yk;5(Rl_0mMvEBf87 zq^n)s`h=uCq{HoErLnSS1qO+k+v_l%I(fkbsbmPfYWNV+Tzr(Juqm4ip@aDH7mu%9 z8rsyo@h~VLIhW^bqkX;HXnN~x+a<>!p;34oU&kJ&z-5pcW>{llwlx27RgZsTpr6et z)6wDJ3B0gc?uZ+sRpZx4sKM%nbG7|_(}oec)h*00c#^$yt&;9`HN)8O@t-F#9#}f> ze^>zj+M&4_7{NWp@2xevgyCBgAb3^}qM)d|Gg(-?W`Pgo<|4(h7*MF75t+KRHTU95 zIA%JgB9~$ub)YG`aQ=A=(`}WLTPeC9HCYUC0!2lLNPyJyAcLa) z&MyMQ`kEWmvC^%dW2sg2pKH>^@e3N30a5*v9Qnfvr257>PQibia0*k6bDE4DoaYg4nZ69Z%HNK1=% zgo#uHbk-`gGnO7_$lFizNj>0ANp*-5!yt5xpn)?Hm7!Y%BN+QE^^9Z?N7M7%Lr&xQ zy0yfTKf6Tqw#BvGigK%%;XGCfiaO+v;6evshDdo+jsI#$;vxUGW5us(Wr_d|W35ks zPu70doNsw7G}w|f_@XK%STq;=8Q%SGdg8B+G<~eYdUX7+DSl48Fn(8PaUu7Y`h`*& z0{tZ|?rNo`ILw6dD<#L*0ekzMwB}upZ`bo!R##$~a6GSrcjs#jIxg6F^S7(gxS!pg z=kOl!IG!VFbsL`z_~Veqe4l z#Zn}vc|O1JZ1<^o%;!m02`z(M70^emSh9qYvLd_nj`4I+ia zmz$jQ`Ml3v(`lZJot-DpwpqaJv_tn_;CcYZ-v_1QK!Bzd_LO9h+TM6Y(DDM~{E?$^ z(qvN0Rj&j<3Sk>Y1Xj?Q&P4HJ2c?%=X6XMb=LfXly$LDR`K|& z(8>A-Rtbwz#7QWv+wKTKJ5mc2PoHPdAF6zw=-(gQsnv=PyZmCXeZ5e=Dr_a5tI69( zwjsOP?C0`r)&S;~DcLp-EpiWHbu?vJNxeGAwu5>i^jc>aH{cehBY~ymtMYo5)hT@T z%cA_&dJLZhovrn#tdcqm5WYuiv3X53A2%0HmM54AW82FhyNqvj3+p^jyiod#lek&_ zypR8N?=E4ljE=4Ee!FSzCYB8Gq?b<^x%EaRg=xS(*ub&fjz3Y`4Vso|y9JJeGZurW zVh7mtAvu~Wv@f)J9y2!O@>(4tgsOuLV&e3jv^NShE``(YRt4bcIcrK+J+-g%aJ>BC ziDJ>x9u{UerGMC~Z=F#){0eh@AaX~!}L{p|%mW@HJN`KOD=j~YSYN3o$muH)2 zy!CZ|uFc`&gEw?E1&($;2C;t0dIvd<<0v-`1T+M{S%pp()E134I+x2QE_GCwy9_c{ zYfuhk(yw88?)cuvg81;W9Zp8?<84MlEyi`cs5SezSpq%E1e&ttb0yX!^(;@5FAIcu z!|?ig-#Q<>FD3tz0O}rPTWC9}~Dz z>Y>haugM!V|EPW&Dc&uwy!6^U8mmm*6eOSRx!$*XPMy4c;x)wie8U~bOL2VskU33^ zAIM`pKdkFuV;8&qQA?2O75T10#3ovkey&k1>*v|xX&r^|DIm>#VGeefC8ujDT=J8i z&Qc(D@1l)!u)L?5<2tIYIl3`FQOL}`+fp|iTIsbtbKJ5vt^qd~Js2r^f-e_zdeohV zcVpNz-Y7L?_n@GXVPFO&pm~OMWA6_o0QwPBW)&?U>*-CitU#k{o0b1LwuC-Fz4eD2 zXEO9HYU*PhC?tg#biB&ELtYg+>^4&74B=?Q6b&ImN*_+=9fNM4IEw^_Nvfup$9**i zlC3c*nmKMw4>oboU^1Joo}%a_net(hFHgSy@F}J7$(Yh#CX|U|)Qt!A+Z8psf0o~) zrJ7QjGcA~y|InldByYOicE%a_L7?rC-=a>9myA@GR%0ns~#-Ou;T>@Y*!n0dI=kg%=xxD{>K~6~cA}_xP12pFdn!0Kk zB5G-oyn9R0HPXMs4y^)9LgXB5QgbLjCVlPO2X38l33K(Jzmty zlNjI!pYF_-^Va@@-Z)@|)Hpz0y<8P-))TRLat>sRMP`^Eh(p}Jn13lOIoo4KI{oiKB|NOIQmJJ*iy+8qG9(iDd zc_WL=$$Fso4h~4J;ocWM7?|*_LYA9B(N$1>c+_wuavB8Anj#38H4897Woc!ND%tSA zfAC)(O_-yWbzn-F&epryh0OM_H@e3KR{p>CzB`=CKK{QXQ3_F%-I6kn2w6uoWff(w zJXW&z&aSMUhGZ+N?Cg1PLS^O%*(2HeILP|F?|Rbn`11Ypcm1yGSATTf+{b-B_h*0J zukq$o&qFmR%md69=*}s69~`@Bkxatz_>N*2hggt*Gtm#khnc?N+czmEw)nMGGH)iv zY>WB<7@P`7Kt+JNO@V2sFQj}&k73qU3pxG*gu`M?({bVksP~z)$h^a@BalI!W+nRx z^Y|&pfoDp$nm_x`<&keO1hvf$AWya&_?WjszE&!C3ItSs{quozVn^WQe}X`)Pv;9E zWG>mql~l4gJAw_w^xb_MK~cq-tc4LH!NMNtF9X^^=U;Wbp&ylsu>pQ;)?UC_4ncBl zw&)K}*nDAc=#9kXM3&!Udnw!&B$p$Z${|OObFJn(VNRCW=PjB}ocpeqg}ehi@Xp5u z>K942XSRBQG+6%l-(d;*CLdtwXLOoEx=@7Xv9d{UyA=|}@c!mgjfoN=1UE@w$;9lv zgDeF=SoQs8ar@`930dN!gmE1wIM!uH@{8DtwmOY zdZazV)ifL@RJUw1$4K$!9bX+avs3=wVs?ubzf26Ii3hL8KBZ*eqeV-K59Z;AQOI7>Pdg@gwxzeHk-XraUG{xj+a5C!wLLo3%4_1 z2$OhAA>RHsHJLdKx4g%V~!;qRd!ZIC&3uYeb1(dKs%%|5sQ_|_3;0Q!27D55Y!PM;NM zl6+z2d)fT55dQ_S{t{P~5Wm85gxmO2C;%O|2~{xx1gM=^ku%=4b1`bj`Osi z7CCs|CJU$}peM&xgEJ-SdY8iieL44J@{EjKbHK_n7>@c6cIo?+aXg=`hn{c<2~bRZ zPIEm=&Lm?0++=pg#_>it;hv1LZ%Vx0`#;yW;?bdJ3&42rWmkyzf}|}Zj;Fwy7_%27 z5zN@^7R`3WB*^_!we9Y**R;;+@?wVF%mjqIxjeK#g7s}V8C!TmjEOGOawsDSbyGiD zr`!t#Vp+P>?5wP@;7C(sXUew#h5O9v*Y}RQXi9>--%*ur836m8L|EQ>k<9&y7>LqW zys27=<~3=%Dd3^l>{NTU8OaVe1D(5+PELzpAmVjxWoOIO{slW$?d zjqhl_mZs#GLiK+CiB8ASlnjAWkUm5p2=ajCO-wBNrh97U;un@$&}%ozJjt>rK(sTJ zo#w(x2#()}=YOTyy;x^=yI#fAH-Yj5q$d?CVdgWGkE1<|<5qcl48<4)@S9fM#OI|J z%<5Sf9q(l0==AS)6`g~$5u!ZKSih<9(^`vC7(mY;1(IulfBACe&`V)7ho?R zaLHytN$(hF)eYJUG$C8(sMQ#@ZwSA}>7_tY3(L}>CGOlfTFEHxz$Bwe z{(^Vho;W@EN}hnqvxf8G8{#lE{4){Bv#>;zs{o;qBZl zXJ6wgNuihXtgB*-_O*ruo4G8l))421X&1;1T_x0Fj0@lUy;^H^Kf(x*%>Ako9;7x; z*7v1_Zp6!v>j$#;IBI3gKnF73BD=s01DVwj#H4OXrpsJm|MXH26phP(e8La@{}Ak9F0qT?}B=xY5f{=1AA*V`CxRxJU3n z-Sh5+N`Z1XVDF!RczkJo{mYF@@ee8&tw3Z~DZ-Z+Ba@~(^YtEMvG*Zv!MT~E4Kc7R z#0VNEbO+H3bCGZp$0vJE8wZLu=v zXd!S%K|j8vDZ(Vyg*0Z$?2-TD4IbH}b7D#Qjgyl}Xr@Ux8bOxrFQ1>oj|F*IC{u}LrdhDM zcdZ$nBqQh%z91AY=}95Ipm+~-X4)aQXpBtxj(_`*8 z-8FJ1iPQgt*NI26J2MqYGZlKv_f=`Wl6YV9om7}?yrn4P1YyRYB>ZRB(WMvcj(r>` z>AF@%;o5+DZ>lKZp;Q-P5N>fPE|-s%Bas5ziYT=Gbj+apj9+Xw*Gx8L<~o+az@?s! zMsd;#8YYaU7IK;HPYt(PoFu{G&G+?o+|tNvsB-YOQWVNnlIx$`xDy%MOT@U_b9k}x z5i#)&WP_uhf!*@MUYr1Cv3BZ7)N*X&?6U?xR`DO(U5=5; z9NHaUR7bz@I$CF%IghG!KTN~pk1gou?X0|ceBt){ZAAPMGP~zu8Y8D3KLWZeEYSn^?#47s=^6GF^!>IFZ)44Cl2eU-UbdlP)dKlth&Imsool zs~ETIWl)}s*GxtD4%kt%u_29Gdn|ETg~QY}Jz#qAJPn`rKGgeFK{k+?*7t%@nvC%r zUZz?mjX0f}OIKN?J(kAUDI!7SCB0 z$#@3fl<`~{>?84>B|ncE9BwKM^Sk^x#!{7u)`Zm4sLEQ0_kRTQ?-4oqA&SCDVaG?G z=oeo4alN^Vc5A?4pD`)pXI>hxu`*A-$WifT@)j}lk9@c%NESq?VRC?&bHuZ6ddYRql zZ|puzHlRT9c|oroO>BVRb~_!syt5@@E}4XmT#A0dG(w2uc6oiqTKs!S*J$_iWzoP7 z8q1M;P{ebuQC{0vMX}dor;_MMwOkhmj2h{r$$1nx*<`19>ir&t!2NiyvbS9mv5a2e@tL^yw)N#VHMoVw{a-rt&v~~ zQylRwFKI9Pit?0F{I{dYNBMMyc1ZCwvCDRUnkT1yR2dMr%lK4LJtH??E$)Sq2l?*) zh#Prvf_uHG(1uICk4``9dqg$NtLcZMK~G}b*80+TD6i=bPv9>GXpwdpcM@u1jSjI7 z^=8s&#%br!wI;-{gm68(E%qfpg%@@I}+vx(MmG7h^EQn4j^hiIdb$u?C zQ8JvVkBOH*xifK`6Qdb+hN3-E>J!gzu+4V@e>O(dM#rAi(eez}>D*8C-`Bdz#-b{Q z^V1)>CRCkuH$MFQh2C3CJSFEiZ&Fg?5Ic6OOr${iKN*NBf9F`%*;YYyS^dQCI43 zndT3k|BLuy?e6e)HFP`H7H9Q$xsxNDc2M3)>_xGI$28gVq^bwzRk8Y%{zVzz0{B$M zwqKoWp}BIaByrY<<)IEUsn?sqt1+CSr*RFtUq3e~?s{Q!iyEyzCMU(C{(!k@kuAFx za)xLcJY)>U(^JIsPbV-$|U~NVd`UlFT%(_Z2Mp z#RKWwGxBJk`osg)8E8v-v3?PSL|%8Z!?e#9Te}-A0?y`swc38H^QD%$n{3#@1i*X( zT2q@&7cVeQlG&b%Pc(Z&fx$7&wM9D~idc!Tp}|UfXx2FZ>@~C)7F^xAZP~oW!rh;h zqY+E5U|yTg%IIWN60XGchKdWP@84b9d7vV4X^xN!gE`Z*PI8Pof^%-9az>ClqZ>uM zW4n`+=%TP=pix?hi{oTowYwFs3D&+m9E-d7V8j8pv+B}d*vR6t#>Ug+88YYQfwvK= z)XwsBEsrBDhS}y~`+`^blDBVfdy}JSB2sgDr_eZu(CBH$Kq^Wqf6AR)LA8TXV zW)C4ADjgcu50I*sF4c(s`D13LiA$__nHWY+0OxpxSZ1_|^Q~YkK@_xl!rtypo<^h{!@bfP>>ZEgglADFJxsqD z6w(F8Du=8hx;=8ohf#KOE8yX!YxvQ7mxMEsLV5n}*Ddh~rj|C)+$F-WD}?fHhSm-4 zz;nr@_>*3tAt`r+_c^f8FNyE|ny(jC?zxh#n{4LMK=YouBs1B24{ha2nD%4s5Z%bf zxOj_lp|y0^U=?FS>Pn?}c>}`HN;dr^OCR0WnFV7(3h$BJExRhYBGGQEva`28Fzy~p zz`q#`c|{{RVl8cPHxtu$NqFEXBp2q)IXX`7bS$^_Lg9gH#mG+R;PdN~E(MR?5i&72 zzuYdyUD?6c(@c%paI(UjOmbsk6|6@V#O4{W`P6QjRv(A3wqv9I{WbZ1{XSM!DYLxQ zy37XMgH@YtkAaxp-fUvAL^k)(`;q>w_LSjzvDv%IYt7$3Ds@cMgv8gL@=72+iqGi} za&&nC$4v=sM7_id$8zTKEK zAILN~mN`~!gpLz)u3Y3k5bVoc?CoI4?A>iexJINcQLU(a%O=fAh@+jKh&ZSw!V7$q-Vjyl_J+FsI-9n7( zOza+JBO%R2Gy1ANIK8PYxBf=J{|U&A*$@@CIl8;>x0kA!IOc{(j}^%qf-q<0kLzJ=?} z^ax0>S4s`=kJdH&ZF`cQQ*838i%u~JBPBX~QA+fUa0D)bI%o4PGZCC~#bl#Xo98`$rnJxP5x97x zbDz@~`b8E32P}DF3&tDYp1bsBzJEl|hG6HT6YV<#jA#t*2&r?v_VxOJ0t<305k+RK z(fP3_9KMq{)S2PJOKCNiS@}}<2WJU{+;D!qL*oZLZUlq2jWRJL3Lk3jWeHvn&gNYE0?F zfz!5!u3@fos`BS}T{l_l%=xJtkUs$;xf<=C;Z=LKlWuOP8s>=03&Ogfk$Y}rl?;3l zygb773mB&qozFhcu{0zfusy>Vqf|Wk=DEV89Z{^K=;DZrrkTpH z)kueCX+*s7tzDD!5_dCD;(k9`~y zS4CE2I}b;UwzA>U_ZXFQv|JiBGjppp!d;rv#u#NyHmO~*=^Sz?ev?8ggcU*^BNGiW zX1rD_Sk?4~TC!*gT|;gxXk{(-WF#U0GcFi$`Ji8Z@IjPs`TZR1P0k zKXF&4{}ydaTf`up55EK9i$VWF3qHQ5yfQqk=C%42)^r07dnPF<7L$$J5^XI?!;dJc zTCY>1@IRlq%I{sIFfqf4a?$h1da12NWgc?hB`&U0b|)dZv@R&AJJZHu{P$-!Ty-AP zrt6SN=h`YxJ#-a;nRuH|P-yf$m=+X7Ja;6QhYMIXHv7*K zw1(|`Ti(Me>7z(Kbg0M(7f)BdpBM>YPR}3G3Ze4U_IYRzfyjP55)>;3wCwb(Euj`N z-5o||=qZSySD?N(tCO}z2>~aUBgp`_B4QVKFdLBx zFrG^S8P1+55u15`6p3>8LP$5st9;szYnj({EU)$Si>~*Ou_-{*13~4?!AZ^sQbgM6 ze5l(XQ(ngw$LNWQtpuJlJkM}v$M4fDadvQ);|cEjgrD z{*H`iL9ZCl1ATJOLxHzJ(4zD8JtM0%Z*%{d&07ovQ#V-(4NzFosae?B`apU)R1xTM zEToaNPnW~y)w(F~&7#AxPl%#SK+WcpQ|qCYedi`a)Izl9zTI$KrMu)$_bK^eA;abk zB(I%D`+nk3_%S-R1u&kukJ;Z4KkdcrJXF>y91=r+!>reR2TA;w0=y%z?tPJ{D9I;* zPHP2wy~WeLFg;TgY2}&gJP#hP+1R81>FT8=EDgOVx~C9Sy%dIsAS%Z?b4=72m^`@Y zltL^QsC=thQddEIH|PcG5;VA39#?msuCTBu(u7cg&UgH_?w8>g+P|z3@1dMv!lm-$1Wd8Z*`Ib7IgeXr?7OmUJ4SJVf?@Wv@)8T`hs$V7h3$%xmH?O61v)%YM2nZ9xWfCr%!eEb{p9Fn{ zZcxw2V#qf{o`6`=w3=a(!}A@f+hRe8e>Ut2@0+Rp_Gedp(- z7%fed(w^%(o??P-SCcdz4P^HDx(wUae)9<3P(B)i7t+X*#izw0oWaO*`$A7bapTYu z;oF?>7F|Z%eS&(ZX~)-sG$AgP4X2LvAESi<5-gPbtoIwXTyM1>#TJz=MdkUPz5jXR z%>7HuSs)1?5LVQP-(svcV2R%{v*hYk2d6`R@SFryQwl^+0||&Hb;54jY|)V0ca9)> z<7>ejWQe+CsI$<{5ERVSLXdD13);+6)fzc>RkRBL%s%D}N)-xlT~2Wxq~EeGL{9@~ z$81%@gXnz`QbC5n6cXE?61Ev>Y5??o3g8`Y(A;>zTvFAAb+Ub;zLkJDT09$cdLp1E z`{;}(?*hMRL}o|svDgRAMFCxQA9Or^^3*Gfm)0x#S9Se--=TBJY5cPu-F=Y6`~Vy^ ziyghsS$$K?@nC7)cve`CtT$NgbR4{80-$*e&rl|jrHUxBjETaDV?eEX3(=is{?{dI!<{tKf3pEg!)Gz|IO zUGUoOAOpNx4PLnYC-xrZOWb7^dUH#{%oQRP?1dkM6vzOo1CxRdBA-NDLn!_Z15vN~ zK3%feMMMqg1`fh5%MrV(_Z~WPETvj$>oz_HZJef2$Ul{7Ki#KXi_|GVD_-D5S<%hx zgwIG{O__TpV?UNr)#p^+54?bzV)Fr8^xNptNJKk0So4B4Qc2h}&!n~U9{S@9>`WktnPFPpeO+iHB#7@ma9%%k^Y zTZ|OdOBWE>4%ElrBiqlktP{L`cc`CN@<=oMdpAUfuLCP+N=ms1p|gk} zmO(g=UJrV8I!Ff+rSxXuWc{{V=$ZU4y7QyZOy(BvcR7-nLE2;dc)I{e zSyQME>q9*lVmhVwwv&`yq}k|kwO&3ad01=WUGaF0jN2ELb4@jd0Cf%lsbTFMG~^)Q z;V9|x*mK8q()V}2*l<;-6>T?hYIwZ8v2j0Jg54Bw6YO5h#XZ-27&8qhH6?64GOOoZ z*Y`2&U;WQ>(t!))L{cAqj&};XqmK#L15-c&@T-Iu~G!G<#M^_8(e15a=k**v`IZnspd|_ecIVSwX+|4Z}H2G*> zQfoo-_%7hC1bjxIVduEZC_RFAW`EM!#$vC(52CfwIT8u-fQDSNZj{5!*K|-6PC;Ve zD&O}qfcco#0yXXsB2T;pM(w!f07hYc*AkrX>XuVvdONa}g`P~}dXQU_;dCoZ#VFlM zf24CP2=|k^0p6+uw${QXr9kDS1wV#doWcL{xFzIqFfn_<@xV7YI2iG4KmkN4MJfFZ zp+`)vh8AAhV&sXt--Y8sNL&CN&ym%?xWABXS^19s;t5PFExLEiE^ax%-oi?q84|vc zj`FKPPUgZe&u_19+iE&j0dA*?v~2A9YO>x8KicQ18m!|T;|Akf6w;kzJGO`81lx`O zu4L=|=Od>!o%#eJldz$W|N2op7|M6>$(D2Bz(Eu_m)(+z(-xJG`a(- zcfb^FoVMY?zkd9SjE=;B=iFB0EO}F?Udvptq1&Q<|1|%f=KnML|BU`kYv9l1|7Y_5 zv-ba4`~SP8TiyB|{|U z=8}1;h)erNr}~n?%-)X7`Yz+N6}2dvsUYrl8&XOZ@q6306A}No5NF@6W6pje;J?59 z=eE2F+{_-7oBR3M^*{P~aq>6|Iaft2)vrhW=k^;;7V=cW^6SX|{Px$k#1VUy#f9)4 z`By>KKPR5O)2r-rB>FS;zsvdScJ$MGs63h{mo_a?Vh-GFn@w>1=jDF=(a+a?x}TEd z`R&I0|NZShwek5NC5e*Ls-oQQs`>pn_ZvjzJvXEeQ*BzJHn@4GK5B&QKNb7WU&~90 z)2TnadUVqgDXPKE@kIsEO{-9fjMVcQkI{uqOLS)ssb`X?+Z(n`tME^w{-~D!pBmM@ z`4WfQaARD6a&YLcVfLRvBCq1^L+-h2?_ot1dGGbHwd_FTN6Wc7D}XXi0H)r_4@0jt7V0H2z20=+Vo}AqqC{1 zx<7sTbYJiFgH4LHo#@#CLydyJj3f_qw-ugE8B+cvf0o^6(@)-{H8i1bxHZRP1}vQKou(ZbKk_}O{W`EXmh(A1!|jBgm`ok zw44U#gzNg-|NCNy1S;|Wy|0bVPBL4#JTVV?@z~rh$J28F$&Z6khY=8VvM0oV#gB({ zL2(ELZ4L;7D>K#H!vO+aSAe*0(gxOdccP4GmveXlpY4#daItXp~z&9#WY@HpV7h1-H#Nmea? z`&V5pw-uZ3@MXAk9c&{ckoK!q_+)1hT@ozV+|?Bch8j;~=iW`fUD*qQ>!1|i=5Qr- znn4oh*N~5XQF+>+Q?<{&DHD7q22Ld1N|CBZuQ?<61lmA&j{}eYw{8~2cE7sBzdP<> z$>=jWQ$XO0Lj0>kL94F@w)$a>@T$Q<8g`vF!;mpp7Hlg%>L6K*p9F&P*SgIpAr5>& zM1a!4;g)JM)m{gtfGl> z{%B6f*c-9ugT1rt^s$)^2P*}2UT}(`8AKc-tg9I9 zz9v{1)P(#E6j~)Whpr%$pu~v%8~*S2$6qHzN?l(AXNm~)WkJUC=64Yzg?+bXeGAZQ zNx=oU2-|7`?R=X}i-y(x73hz4Bf65zsrlbRL)-n=YTnBrPCpF9mr;zpPQ+Nm(3QVU zUaM}iy-LFz(KAxdqcj1WL?1}?;cHs0L0u3>`rxQ@M~Dfi<*Um~y$Yuv>Kz-&z3#^R zj6h$kB?QGErAR=e>u!PDge1N~z4OXMisUPVeW$q0U~yrUPLAmE$z`bbf%Gip&|ddl zOz#JERlG@aUUe3h9{q!ts}Is}^yknxENKWEhlB+pPpPK_T)O8aJEmM37a*#a|k z$6d;+tb!ud1ss&Vun~1ahEUXGRpXwFxyf!cri!AfA_ub4FPAKS3mcs8lM|_>M@uLb zHSfL9|Del$FQX8j3()LdyEr&m7zr(P8w{>^?h{HR?6{NtWOVFZXg;E7aJ9@Qz2kSB z9r4~@&N5fGoX8*iHid`wsvSMtVwV}CI@11ZoV$`5txPL7h4j{8pvI+5>U%C-*U|xN zm8=eakA($RjQz|aSdDy;Q$RZ0FnDftX=}-DwjliMW{BMb2d~LUmNjIOh_B`<w zk#p$*+Wa|Jso4hRkt_q;`SQ!H9gt^!X1cE)5~|?#a2LrtaVn=r3qI>+lM4-(6&jAU zF2nDc83b301ltm=HLaFiTpojM59Y>bu3darP?n)3a@869o(E0`qE{D+^evVfH11a1 zpjgDVq3Pem8B$N`Eg@!EjX*{Wc_{A!2|DIFV_#(I$CI}8-$YLs(#S0zG_4nzl`ffp z8D9p;(oq1s`NmTB9XNQ)$e^X^yqX@0O=bz7{e~uqcbXNXIpiqO40b?S{>o$PH@Yj=xhxy4&-Q7A^9NWiUY1YcK|cGL?JDsMzZMPICPM@YUX!AC7;c( zJ`~l=x+&e=g%{-+;pIY0`>R$|FLPX@Lih1QLQ!=sJY&pV5fyql%BmYQ^a|CLcsnpj z8H~9glTjp@!DVTIftSqVZ$fxg<-j*YW9K+LSfd ztVYXRYs81n9sr{uj#&TZ4AC!$qLD_H3ZK~~~rrtLaYlh>{GZtP&%)TB*m6b!vn1!Yqw z^5VG2C=HLeg9GEd>C4{3r#5~%dV1#(;y^YJ@3H{2(y3$e$K5rJo0C7xrD_%zNZBMk z+qidZ&?O#u?s0#cU=9Omn#=vPg$`X3V-{jk>lfmGuT<_my0*g2%_*lyOX$(r2LG;J Ml)aEDq3!X107>qCl>h($ literal 0 HcmV?d00001 diff --git a/docs/mp/graphql/01_mp_graphql.adoc b/docs/mp/graphql/01_mp_graphql.adoc new file mode 100644 index 00000000000..f32247c0807 --- /dev/null +++ b/docs/mp/graphql/01_mp_graphql.adoc @@ -0,0 +1,206 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2019, 2020 Oracle and/or its affiliates. + + 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. + +/////////////////////////////////////////////////////////////////////////////// + += MicroProfile GraphQL +:h1Prefix: MP +:pagename: microprofile-graphql +:description: Helidon GraphQL MicroProfile +:keywords: helidon, graphql, microprofile, micro-profile + + +The Microprofile GraphQL APIs are an extension to <> +to allow building of applications that can expose a GraphQL endpoint and optionally a link:https://github.com/graphql/graphiql[GraphiQL] API. + +== About the MicroProfile GraphQL Specification +Helidon MP implements the MicroProfile GraphQL +link:https://github.com/eclipse/microprofile-graphql[spec] version 1.0.3. +The spec prescribes how applications can be built to expose an endpoint for GraphQL. +GraphQL is an open-source data query and manipulation language for APIs, +and a runtime for fulfilling queries with existing data. +GraphQL provides an alternative, though not necessarily a replacement for REST. + +For more information on GraphQL see https://graphql.org/. + +== Maven Coordinates + +The <> page describes how you +should declare dependency management for Helidon applications. Then declare the following dependency in your project: + +[source,xml] +---- + + io.helidon.microprofile.graphql + helidon-microprofile-graphql-server + +---- + +== Getting Started + +=== Defining your API + +The MicroProfile GraphQL specification defines a number of key annotations to be used when writing a GraphQL endpoint: + +* `@GraphQLApi` - identifies a CDI Bean as a GraphQL Endpoint +* `@Query` - identifies a method as returning specified fields for an object or collection of entities +* `@Mutation` - identifies a method which creates, deletes or updates entities + +NOTE: Please see the link:https://github.com/eclipse/microprofile-graphql[Microprofile GraphQL spec] for the full list of available annotations. + +For example, the following defines a GraphQL endpoint with a number of queries and mutations that work +against a fictional `CustomerService` service and `Customer` class. + +[source,java] +.Simple ContactGraphQLApi +---- +@ApplicationScoped +@org.eclipse.microprofile.graphql.GraphQLApi +public class ContactGraphQLApi { + + @Inject + private CustomerService customerService; + + @org.eclipse.microprofile.graphql.Query + public Collection findAllCustomers() { <1> + return customerService.getAllCustomers(); + } + + @org.eclipse.microprofile.graphql.Query + public Customer findCustomer(@Name("customerId") int id) { <2> + return customerService.getCustomer(id); + } + + @org.eclipse.microprofile.graphql.Query + public Collection findCustomersByName(@Name("name") @NonNull String name) { <3> + return customerService.getAllCustomers(names); + } + + @org.eclipse.microprofile.graphql.Mutation + public Contact createCustomer(@Name("customerId") int id, <4> + @Name("name") String name, + @Name("balance") float balance) { + return customerService.createCustomer(id, name, balance); + } +} + +public class customer { + private int id; + @NonNull + private String name; + private float balance; + + // getters and setters omitted for brevity +} +---- + +<1> a query with no-arguments that will return all Customers +<2> a query that takes an argument to return a specific Customer +<3> a query that optionally takes a name and returns a collection of Customers +<4> a mutation that creates a Customer and returns the newly created Customer + +After application startup, a GraphQL schema will be generated from your annotated API classes +and POJO's and you will be able to access these via the URL's described below. + +The above would generate a GraphQL schema as shown below: +[source,graphql] +.Sample GraphQL Schema +---- +type Query { + findAllCustomers: [Customer] + findCustomer(customerId: Int!): Customer + findCustomersByName(name: String): [Customers] +} + +type Mutation { + createCustomer(customerId: Int!, name: String!, balance: Float!): Customer +} + +type Customer { + id: Int! + name: String! + balance: Float +} +---- + +=== Creating your entry-point + +As per the instructions <> ensure you have added a +`src/main/resources/META-INF/beans.xml` file so the CDI implementation can pick up your classes. + +Add a main method or dedicated Main class to start everything up. + +[source,java] +.Sample Entry-point +---- +public class MyMain { + public static void main(String[] args) { + io.helidon.microprofile.server.Main.main(args); + } +} +---- + +=== Building your application + +As part of building your application, you must create a Jandex index +using the `jandex-maven-plugin` for all API and POJO classes that are used. + +[source,xml] +.Generate Jandex index +---- + +org.jboss.jandex +jandex-maven-plugin +1.0.8 + + + make-index + + + +---- + +== Accessing the GraphQL end-points + +After starting your application you should see a message similar to the following +indicating the GraphQL application has be started: + +[source,bash] +.Sample Startup output +---- +[Mon. Nov. 02 14:54:11 AWST 2020] INFO: io.helidon.microprofile.server.ServerCdiExtension addApplication + - Registering JAX-RS Application: GraphQLApplication +---- + +Once you access the built-in GraphiQL endpoint at `http://host:port/ui` or +using your GraphQL client via the `http://host:port/graphql` end-point a message indicating the +GraphQL Schema has been generated will be displayed as shown: + +[source,bash] +.Sample Schema Message +---- +[Mon. Nov. 02 14:54:16 AWST 2020] INFO: io.helidon.microprofile.graphql.server.ExecutionContext - Generated schema: +... Schema displayed here +---- + +If you wish to suppress this message then set the following configuration value `io.helidon.graphql.suppress-schema-display` to true. + +.GraphQL UI +image::mp/graphql_01_introduction_graphiql.png[GraphiQL] + + + + diff --git a/docs/mp/introduction/01_introduction.adoc b/docs/mp/introduction/01_introduction.adoc index 1be4abae08b..f63c9750daa 100644 --- a/docs/mp/introduction/01_introduction.adoc +++ b/docs/mp/introduction/01_introduction.adoc @@ -85,6 +85,14 @@ Add support for CORS to your application using a Helidon module. Defines annotations that improve applications by providing support to handle error conditions (faults). -- +//GraphQL +[CARD] +.GraphQL +[icon=graphic_eq,link=mp/graphql/01_mp_graphql.adoc] +-- +Expose GraphQL API using Microprofile GraphQL. +-- + //gRPC [CARD] .gRPC @@ -92,10 +100,11 @@ Defines annotations that improve applications by providing support to handle err -- Build gRPC servers and clients. -- + //Health Checks [CARD] .Health Checks -[icon=favorite_outline,link=mp/health/01_introduction.adoc] +[icon=graphic_eq,link=mp/health/01_introduction.adoc] -- Expose health statuses of your applications. -- diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 4f76984c30b..1d7222d0f39 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -277,6 +277,14 @@ backend: items: - includes: - "mp/grpc/*.adoc" + - title: "GraphQL" + pathprefix: "/mp/graphql" + glyph: + type: "icon" + value: "graphic_eq" + items: + - includes: + - "mp/graphql/*.adoc" - title: "Health Checks" pathprefix: "/mp/health" glyph: diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index c159f5bda17..4b5486a4343 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -36,6 +36,7 @@ import graphql.schema.GraphQLSchema; import graphql.schema.idl.SchemaPrinter; import graphql.validation.ValidationError; +import io.helidon.config.ConfigValue; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.eclipse.microprofile.graphql.ConfigKey; import org.eclipse.microprofile.graphql.GraphQLException; @@ -50,6 +51,11 @@ */ public class ExecutionContext { + /** + * Config to suppress schema display. + */ + public static final String SUPPRESS_SCHEMA_DISPLAY = "io.helidon.graphql.suppress-schema-display"; + /** * Key for errors. */ @@ -160,6 +166,11 @@ public class ExecutionContext { */ private Config config; + /** + * Indicates if the schema display should be suppressed. + */ + private boolean suppressSchemaDisplay; + /** * Schema printer. */ @@ -190,6 +201,7 @@ public Schema getSchema() { */ public ExecutionContext(Context context) { try { + processConfiguration(); configureExceptionHandling(); SchemaGenerator schemaGenerator = new SchemaGenerator(context); schema = schemaGenerator.generateSchema(); @@ -206,8 +218,9 @@ public ExecutionContext(Context context) { .subscriptionExecutionStrategy(new SubscriptionExecutionStrategy()); graphQL = builder.build(); - - LOGGER.info("Generated schema:\n" + schemaPrinter.print(graphQLSchema)); + if (!suppressSchemaDisplay) { + LOGGER.info("Generated schema:\n" + schemaPrinter.print(graphQLSchema)); + } } catch (Throwable t) { // since we cannot generate the schema, just log the message and throw it ensureConfigurationException(LOGGER, "Unable to build GraphQL Schema: ", t); @@ -218,8 +231,6 @@ public ExecutionContext(Context context) { * Configure microprofile exception handling. */ private void configureExceptionHandling() { - config = (Config) ConfigProviderResolver.instance().getConfig(); - defaultErrorMessage = config.get(MESSAGE_PARTS[0]) .get(MESSAGE_PARTS[1]) .get(MESSAGE_PARTS[2]) @@ -243,6 +254,16 @@ private void configureExceptionHandling() { } } + /** + * Process any configuration. + */ + private void processConfiguration() { + config = (Config) ConfigProviderResolver.instance().getConfig(); + + suppressSchemaDisplay = "true".equals( + this.config.get(SUPPRESS_SCHEMA_DISPLAY).asString().orElse("false")); + } + /** * Return a new {@link DefaultContext}. * diff --git a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java index e37be1a570f..eb136723b22 100644 --- a/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java +++ b/tests/integration/mp-graphql/src/test/java/io/helidon/microprofile/graphql/server/NullIT.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.util.Map; +import static io.helidon.microprofile.graphql.server.ExecutionContext.SUPPRESS_SCHEMA_DISPLAY; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; @@ -30,6 +31,7 @@ import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddConfig; import org.junit.jupiter.api.Test; /** @@ -37,6 +39,7 @@ */ @AddBean(QueriesAndMutationsWithNulls.class) @AddBean(TestDB.class) +@AddConfig(key = SUPPRESS_SCHEMA_DISPLAY, value="true") public class NullIT extends AbstractGraphQLIT { @Test From d0220be4e656757ed944c6e6b62d335cee9b42ae Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 3 Nov 2020 07:35:38 +0800 Subject: [PATCH 137/178] Minor cleanup --- .../graphql/server/ExecutionContext.java | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java index 4b5486a4343..689532357a0 100644 --- a/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java +++ b/microprofile/graphql/server/src/main/java/io/helidon/microprofile/graphql/server/ExecutionContext.java @@ -36,14 +36,15 @@ import graphql.schema.GraphQLSchema; import graphql.schema.idl.SchemaPrinter; import graphql.validation.ValidationError; -import io.helidon.config.ConfigValue; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; -import org.eclipse.microprofile.graphql.ConfigKey; import org.eclipse.microprofile.graphql.GraphQLException; import static graphql.ExecutionInput.newExecutionInput; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.ensureConfigurationException; import static io.helidon.microprofile.graphql.server.SchemaGeneratorHelper.getSafeClass; +import static org.eclipse.microprofile.graphql.ConfigKey.DEFAULT_ERROR_MESSAGE; +import static org.eclipse.microprofile.graphql.ConfigKey.EXCEPTION_BLACK_LIST; +import static org.eclipse.microprofile.graphql.ConfigKey.EXCEPTION_WHITE_LIST; /** * Defines a context in which to execute GraphQL commands. @@ -101,21 +102,6 @@ public class ExecutionContext { */ private static final String EMPTY = ""; - /** - * Config parts for default error message. - */ - static final String[] MESSAGE_PARTS = ConfigKey.DEFAULT_ERROR_MESSAGE.split("\\."); - - /** - * Config parts for allow parts. - */ - static final String[] ALLOW_PARTS = ConfigKey.EXCEPTION_WHITE_LIST.split("\\."); - - /** - * Config parts for deny list. - */ - static final String[] DENY_PARTS = ConfigKey.EXCEPTION_BLACK_LIST.split("\\."); - /** * Logger. */ @@ -231,19 +217,9 @@ public ExecutionContext(Context context) { * Configure microprofile exception handling. */ private void configureExceptionHandling() { - defaultErrorMessage = config.get(MESSAGE_PARTS[0]) - .get(MESSAGE_PARTS[1]) - .get(MESSAGE_PARTS[2]) - .asString().orElse("Server Error"); - - String allowList = config.get(ALLOW_PARTS[0]) - .get(ALLOW_PARTS[1]) - .get(ALLOW_PARTS[2]) - .asString().orElse(EMPTY); - String denyList = config.get(DENY_PARTS[0]) - .get(DENY_PARTS[1]) - .get(DENY_PARTS[2]) - .asString().orElse(EMPTY); + defaultErrorMessage = config.get(DEFAULT_ERROR_MESSAGE).asString().orElse("Server Error"); + String allowList = config.get(EXCEPTION_WHITE_LIST).asString().orElse(EMPTY); + String denyList = config.get(EXCEPTION_BLACK_LIST).asString().orElse(EMPTY); if (!EMPTY.equals(allowList)) { exceptionAllowList.addAll(Arrays.asList(allowList.split(","))); From df319f7fc71fb9345f87ec5341509994455c50b9 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 3 Nov 2020 15:02:34 +0800 Subject: [PATCH 138/178] Add examples --- docs/mp/graphql/01_mp_graphql.adoc | 10 +- examples/graphql/basics/README.md | 108 ++++++++++++++ examples/graphql/basics/pom.xml | 63 +++++++++ .../helidon/examples/graphql/basics/Main.java | 7 + .../helidon/examples/graphql/basics/Task.java | 117 ++++++++++++++++ .../examples/graphql/basics/TaskApi.java | 132 ++++++++++++++++++ .../graphql/basics/TaskNotFoundException.java | 30 ++++ .../examples/graphql/basics/package-info.java | 20 +++ .../src/main/resources/META-INF/beans.xml | 26 ++++ examples/graphql/pom.xml | 35 +++++ examples/pom.xml | 1 + 11 files changed, 544 insertions(+), 5 deletions(-) create mode 100644 examples/graphql/basics/README.md create mode 100644 examples/graphql/basics/pom.xml create mode 100644 examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Main.java create mode 100644 examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Task.java create mode 100644 examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java create mode 100644 examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskNotFoundException.java create mode 100644 examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/package-info.java create mode 100644 examples/graphql/basics/src/main/resources/META-INF/beans.xml create mode 100644 examples/graphql/pom.xml diff --git a/docs/mp/graphql/01_mp_graphql.adoc b/docs/mp/graphql/01_mp_graphql.adoc index f32247c0807..02372c00ec1 100644 --- a/docs/mp/graphql/01_mp_graphql.adoc +++ b/docs/mp/graphql/01_mp_graphql.adoc @@ -32,7 +32,7 @@ link:https://github.com/eclipse/microprofile-graphql[spec] version 1.0.3. The spec prescribes how applications can be built to expose an endpoint for GraphQL. GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. -GraphQL provides an alternative, though not necessarily a replacement for REST. +It provides an alternative, though not necessarily a replacement for REST. For more information on GraphQL see https://graphql.org/. @@ -85,7 +85,7 @@ public class ContactGraphQLApi { } @org.eclipse.microprofile.graphql.Query - public Collection findCustomersByName(@Name("name") @NonNull String name) { <3> + public Collection findCustomersByName(@Name("name") String name) { <3> return customerService.getAllCustomers(names); } @@ -112,9 +112,6 @@ public class customer { <3> a query that optionally takes a name and returns a collection of Customers <4> a mutation that creates a Customer and returns the newly created Customer -After application startup, a GraphQL schema will be generated from your annotated API classes -and POJO's and you will be able to access these via the URL's described below. - The above would generate a GraphQL schema as shown below: [source,graphql] .Sample GraphQL Schema @@ -136,6 +133,9 @@ type Customer { } ---- +After application startup, a GraphQL schema will be generated from your annotated API classes +and POJO's and you will be able to access these via the URL's described below. + === Creating your entry-point As per the instructions <> ensure you have added a diff --git a/examples/graphql/basics/README.md b/examples/graphql/basics/README.md new file mode 100644 index 00000000000..2496078e22d --- /dev/null +++ b/examples/graphql/basics/README.md @@ -0,0 +1,108 @@ +# Basic Microprofile GraphQL Example + +This example creates a simple Task APU using the Microprofile GraphQL API. + + +## Running the example + +1. Build the example + +```bash +mvn clean install +``` + +2. Run the example + +```bash +mvn exec:java +``` + +A Message will be displayed indicating the GraphiQL UI URL. + +## Issuing GraphQL Requests + +1. Access the GraphiQL UI via the following URL: http://127.0.0.1:7001/ui. + +2. Copy the following commands into the editor on the left. + + ```graphql + + # Fragment to allow shorcut to display all fields for a task + fragment task on Task { + id + description + createdAt + completed + } + + # Find all the tasks + query findAllTasks { + tasks { + ...task + } + } + + # Find completed Tasks + query findCompletedTasks { + tasks(completed: true) { + ...task + } + } + + # Find outstanding Tasks + query findOutstandingTasks { + tasks(completed: false) { + ...task + } + } + + # Create a task + mutation createTask { + createTask(description: "Task Description 2") { + ...task + } + } + + mutation updateTask { + updateTask(id: "1f6ae5" description:"New Description") { + ...task + } + } + + mutation completeTask { + updateTask(id: "1f6ae5" completed:true) { + ...task + } + } + + # Delete a task + mutation deleteTask { + deleteTask(id: "1f6ae5") { + ...task + } + } + + # Delete completed + mutation deleteCompleted { + deleteCompletedTasks { + ...task + } + } + ``` + +3. Run individual commands by clicking on the `Play` button and choosing the query or mutation to run. + + +4. Sample requests + + 1. Execute `createTask` + 2. Change the description and execute `createTask` + 3. Execute `findAllTasks` to show the 2 tasks + 4. Change the id and execute `updateTask` to update the existing task + 5. Change the id and execute `completeTask` + 6. Execute `findAllTasks` to show the task completed + 7. Execute `findCompletedTasks` to show only completed tasks + 8. Execute `deleteCompleted` to delete completed task + 9. Execute `findCompletedTasks` to show no completed tasks + + \ No newline at end of file diff --git a/examples/graphql/basics/pom.xml b/examples/graphql/basics/pom.xml new file mode 100644 index 00000000000..eefb7b9f945 --- /dev/null +++ b/examples/graphql/basics/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + io.helidon.applications + helidon-mp + 2.1.1-SNAPSHOT + ../../../applications/mp/pom.xml + + io.helidon.examples.graphql + helidon-examples-graphql-basics + Helidon GraphQL Examples Basics + + + Basic usage of GraphQL in Helidon MP + + + + + io.helidon.microprofile.graphql + helidon-microprofile-graphql-server + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + + + + + diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Main.java b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Main.java new file mode 100644 index 00000000000..bcc3855a5ba --- /dev/null +++ b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Main.java @@ -0,0 +1,7 @@ +package io.helidon.examples.graphql.basics; + +public class Main { + public static void main(String[] args) { + io.helidon.microprofile.cdi.Main.main(args); + } +} diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Task.java b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Task.java new file mode 100644 index 00000000000..97be6ce2d55 --- /dev/null +++ b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Task.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package io.helidon.examples.graphql.basics; + +import java.util.UUID; + +/** + * A data class representing a single To Do List task. + */ +public class Task { + + /** + * The creation time. + */ + private long createdAt; + + /** + * The completion status. + */ + private boolean completed; + + /** + * The task ID. + */ + private String id; + + /** + * The task description. + */ + private String description; + + /** + * Deserialization constructor. + */ + public Task() { + } + + /** + * Construct Task instance. + * + * @param description task description + */ + public Task(String description) { + this.id = UUID.randomUUID().toString().substring(0, 6); + this.createdAt = System.currentTimeMillis(); + this.description = description; + this.completed = false; + } + + /** + * Get the creation time. + * + * @return the creation time + */ + public long getCreatedAt() { + return createdAt; + } + + /** + * Get the task ID. + * + * @return the task ID + */ + public String getId() { + return id; + } + + /** + * Get the task description. + * + * @return the task description + */ + public String getDescription() { + return description; + } + + /** + * Set the task description. + * + * @param description the task description + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Get the completion status. + * + * @return true if it is completed, false otherwise. + */ + public boolean isCompleted() { + return completed; + } + + /** + * Sets the completion status. + * + * @param completed the completion status + */ + public void setCompleted(boolean completed) { + this.completed = completed; + } + + @Override + public String toString() { + return "Task{" + + "id=" + id + + ", description=" + description + + ", completed=" + completed + + '}'; + } +} diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java new file mode 100644 index 00000000000..c905f5e101a --- /dev/null +++ b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.examples.graphql.basics; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; + +import org.eclipse.microprofile.graphql.Description; +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Mutation; +import org.eclipse.microprofile.graphql.Name; +import org.eclipse.microprofile.graphql.NonNull; +import org.eclipse.microprofile.graphql.Query; + +/** + * CDI Bean to work with {@link Task}s. + */ +@GraphQLApi +@ApplicationScoped +public class TaskApi { + + private Map tasks = new ConcurrentHashMap<>(); + + /** + * Create a {@link Task}. + * + * @param description task description + * @return the created {@link Task} + */ + @Mutation + @Description("Create a task with the given description") + public Task createTask(@Name("description") String description) { + Task task = new Task(description); + tasks.put(task.getId(), task); + return task; + } + + /** + * Query {@link Task}s. + * + * @param completed optionally specify completion status + * @return a {@link Collection} of {@link Task}s + */ + @Query + @Description("Query tasks and optionally specified only completed") + public Collection getTasks(@Name("completed") Boolean completed) { + + return tasks.values().stream() + .filter(task -> completed == null || task.isCompleted() == completed) + .collect(Collectors.toList()); + } + + /** + * Delete a {@link Task}. + * + * @param id task to delete + * @return the deleted {@link Task} + * @throws TaskNotFoundException if the task was not found + */ + @Mutation + @Description("Delete a task and return the deleted task details") + public Task deleteTask(@Name("id") String id) throws TaskNotFoundException { + Task task = tasks.remove(id); + if (task == null) { + throw new TaskNotFoundException("Unable to find task with id " + id); + } + return task; + } + + /** + * Remove all completed {@link Task}s. + * + * @return the {@link Task}s left + */ + @Mutation + @Description("Remove all completed tasks and return the tasks left") + public Collection deleteCompletedTasks() { + tasks.values().removeIf(Task::isCompleted); + return tasks.values(); + } + + /** + * Update a {@link Task}. + * @param id task to update + * @param description optional description + * @param completed optional completed + * @return the updated {@link Task} + * @throws TaskNotFoundException if the task was not found + */ + @Mutation + @Description("Update a task") + public Task updateTask(@Name("id") @NonNull String id, + @Name("description") String description, + @Name("completed") Boolean completed) throws TaskNotFoundException { + + try { + return tasks.compute(id, (k, v) -> { + Objects.requireNonNull(v); + + if (description != null) { + v.setDescription(description); + } + if (completed != null) { + v.setCompleted(completed); + } + return v; + }); + } catch (Exception e) { + throw new TaskNotFoundException("Unable to find task with id " + id); + } + } + +} diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskNotFoundException.java b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskNotFoundException.java new file mode 100644 index 00000000000..12a4cbe5a63 --- /dev/null +++ b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskNotFoundException.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 io.helidon.examples.graphql.basics; + +/** + * An exception indicating that a {@link Task} was not found. + */ +public class TaskNotFoundException extends Exception { + /** + * Create the exception. + * @param message reason for the exception. + */ + public TaskNotFoundException(String message) { + super(message); + } +} diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/package-info.java b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/package-info.java new file mode 100644 index 00000000000..f3db01ac8a7 --- /dev/null +++ b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * 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. + */ + +/** + * A set of small usage examples. Start with {@link io.helidon.grpc.examples.basics.Main} class. + */ +package io.helidon.examples.graphql.basics; diff --git a/examples/graphql/basics/src/main/resources/META-INF/beans.xml b/examples/graphql/basics/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..e5a9e54aa5e --- /dev/null +++ b/examples/graphql/basics/src/main/resources/META-INF/beans.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/examples/graphql/pom.xml b/examples/graphql/pom.xml new file mode 100644 index 00000000000..aeba21c0eba --- /dev/null +++ b/examples/graphql/pom.xml @@ -0,0 +1,35 @@ + + + + + 4.0.0 + + helidon-examples-project + io.helidon.examples + 2.1.1-SNAPSHOT + + io.helidon.examples.graphql + helidon-examples-graphql-project + pom + Helidon Graphql Examples + + + basics + + diff --git a/examples/pom.xml b/examples/pom.xml index 6956b328d57..8d54103342a 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -40,6 +40,7 @@ config + graphql grpc health quickstarts From ee4cc1bcbadf9c366380d37162cc31aa4ba5b53e Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 3 Nov 2020 15:50:02 +0800 Subject: [PATCH 139/178] further example updates --- examples/graphql/basics/README.md | 4 +- .../examples/graphql/basics/TaskApi.java | 51 +++------ .../examples/graphql/basics/TaskDB.java | 102 ++++++++++++++++++ 3 files changed, 115 insertions(+), 42 deletions(-) create mode 100644 examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java diff --git a/examples/graphql/basics/README.md b/examples/graphql/basics/README.md index 2496078e22d..245c19977e2 100644 --- a/examples/graphql/basics/README.md +++ b/examples/graphql/basics/README.md @@ -15,9 +15,7 @@ mvn clean install ```bash mvn exec:java -``` - -A Message will be displayed indicating the GraphiQL UI URL. +``` ## Issuing GraphQL Requests diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java index c905f5e101a..0f3143fc1d9 100644 --- a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java +++ b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java @@ -17,12 +17,9 @@ package io.helidon.examples.graphql.basics; import java.util.Collection; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.GraphQLApi; @@ -38,7 +35,8 @@ @ApplicationScoped public class TaskApi { - private Map tasks = new ConcurrentHashMap<>(); + @Inject + private TaskDB taskDB; /** * Create a {@link Task}. @@ -49,9 +47,7 @@ public class TaskApi { @Mutation @Description("Create a task with the given description") public Task createTask(@Name("description") String description) { - Task task = new Task(description); - tasks.put(task.getId(), task); - return task; + return taskDB.createTask(description); } /** @@ -63,10 +59,7 @@ public Task createTask(@Name("description") String description) { @Query @Description("Query tasks and optionally specified only completed") public Collection getTasks(@Name("completed") Boolean completed) { - - return tasks.values().stream() - .filter(task -> completed == null || task.isCompleted() == completed) - .collect(Collectors.toList()); + return taskDB.getTasks(completed); } /** @@ -79,11 +72,7 @@ public Collection getTasks(@Name("completed") Boolean completed) { @Mutation @Description("Delete a task and return the deleted task details") public Task deleteTask(@Name("id") String id) throws TaskNotFoundException { - Task task = tasks.remove(id); - if (task == null) { - throw new TaskNotFoundException("Unable to find task with id " + id); - } - return task; + return taskDB.deleteTask(id); } /** @@ -94,15 +83,15 @@ public Task deleteTask(@Name("id") String id) throws TaskNotFoundException { @Mutation @Description("Remove all completed tasks and return the tasks left") public Collection deleteCompletedTasks() { - tasks.values().removeIf(Task::isCompleted); - return tasks.values(); + return taskDB.deleteCompletedTasks(); } /** * Update a {@link Task}. - * @param id task to update - * @param description optional description - * @param completed optional completed + * + * @param id task to update + * @param description optional description + * @param completed optional completed * @return the updated {@link Task} * @throws TaskNotFoundException if the task was not found */ @@ -111,22 +100,6 @@ public Collection deleteCompletedTasks() { public Task updateTask(@Name("id") @NonNull String id, @Name("description") String description, @Name("completed") Boolean completed) throws TaskNotFoundException { - - try { - return tasks.compute(id, (k, v) -> { - Objects.requireNonNull(v); - - if (description != null) { - v.setDescription(description); - } - if (completed != null) { - v.setCompleted(completed); - } - return v; - }); - } catch (Exception e) { - throw new TaskNotFoundException("Unable to find task with id " + id); - } + return taskDB.updateTask(id, description, completed); } - } diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java new file mode 100644 index 00000000000..ecbf5fc0634 --- /dev/null +++ b/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package io.helidon.examples.graphql.basics; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; + +/** + * CDI Bean to work with {@link Task}s. + */ +@ApplicationScoped +public class TaskDB { + + private Map tasks = new ConcurrentHashMap<>(); + + /** + * Create a {@link Task}. + * + * @param description task description + * @return the created {@link Task} + */ + public Task createTask(String description) { + Task task = new Task(description); + tasks.put(task.getId(), task); + return task; + } + + /** + * Query {@link Task}s. + * + * @param completed optionally specify completion status + * @return a {@link Collection} of {@link Task}s + */ + public Collection getTasks(Boolean completed) { + return tasks.values().stream() + .filter(task -> completed == null || task.isCompleted() == completed) + .collect(Collectors.toList()); + } + + /** + * Delete a {@link Task}. + * + * @param id task to delete + * @return the deleted {@link Task} + * @throws TaskNotFoundException if the task was not found + */ + public Task deleteTask(String id) throws TaskNotFoundException { + Task task = tasks.remove(id); + if (task == null) { + throw new TaskNotFoundException("Unable to find task with id " + id); + } + return task; + } + + /** + * Remove all completed {@link Task}s. + * + * @return the {@link Task}s left + */ + public Collection deleteCompletedTasks() { + tasks.values().removeIf(Task::isCompleted); + return tasks.values(); + } + + /** + * Update a {@link Task}. + * + * @param id task to update + * @param description optional description + * @param completed optional completed + * @return the updated {@link Task} + * @throws TaskNotFoundException if the task was not found + */ + public Task updateTask(String id, String description, Boolean completed) throws TaskNotFoundException { + + try { + return tasks.compute(id, (k, v) -> { + Objects.requireNonNull(v); + + if (description != null) { + v.setDescription(description); + } + if (completed != null) { + v.setCompleted(completed); + } + return v; + }); + } catch (Exception e) { + throw new TaskNotFoundException("Unable to find task with id " + id); + } + } +} From 21508d780cbf74773c5f27c1bb0ae63faade5cf7 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 3 Nov 2020 19:58:02 +0800 Subject: [PATCH 140/178] Move example under microprofile --- examples/graphql/pom.xml | 35 ------------------- .../basics => microprofile/graphql}/README.md | 10 +++--- .../basics => microprofile/graphql}/pom.xml | 12 ++++--- .../helidon/examples/graphql/basics/Main.java | 0 .../helidon/examples/graphql/basics/Task.java | 0 .../examples/graphql/basics/TaskApi.java | 0 .../examples/graphql/basics/TaskDB.java | 0 .../graphql/basics/TaskNotFoundException.java | 0 .../examples/graphql/basics/package-info.java | 0 .../src/main/resources/META-INF/beans.xml | 0 examples/microprofile/pom.xml | 1 + examples/pom.xml | 1 - microprofile/graphql/server/pom.xml | 2 +- 13 files changed, 16 insertions(+), 45 deletions(-) delete mode 100644 examples/graphql/pom.xml rename examples/{graphql/basics => microprofile/graphql}/README.md (89%) rename examples/{graphql/basics => microprofile/graphql}/pom.xml (84%) rename examples/{graphql/basics => microprofile/graphql}/src/main/java/io/helidon/examples/graphql/basics/Main.java (100%) rename examples/{graphql/basics => microprofile/graphql}/src/main/java/io/helidon/examples/graphql/basics/Task.java (100%) rename examples/{graphql/basics => microprofile/graphql}/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java (100%) rename examples/{graphql/basics => microprofile/graphql}/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java (100%) rename examples/{graphql/basics => microprofile/graphql}/src/main/java/io/helidon/examples/graphql/basics/TaskNotFoundException.java (100%) rename examples/{graphql/basics => microprofile/graphql}/src/main/java/io/helidon/examples/graphql/basics/package-info.java (100%) rename examples/{graphql/basics => microprofile/graphql}/src/main/resources/META-INF/beans.xml (100%) diff --git a/examples/graphql/pom.xml b/examples/graphql/pom.xml deleted file mode 100644 index aeba21c0eba..00000000000 --- a/examples/graphql/pom.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - 4.0.0 - - helidon-examples-project - io.helidon.examples - 2.1.1-SNAPSHOT - - io.helidon.examples.graphql - helidon-examples-graphql-project - pom - Helidon Graphql Examples - - - basics - - diff --git a/examples/graphql/basics/README.md b/examples/microprofile/graphql/README.md similarity index 89% rename from examples/graphql/basics/README.md rename to examples/microprofile/graphql/README.md index 245c19977e2..2814b681001 100644 --- a/examples/graphql/basics/README.md +++ b/examples/microprofile/graphql/README.md @@ -1,11 +1,13 @@ -# Basic Microprofile GraphQL Example +# Microprofile GraphQL Example -This example creates a simple Task APU using the Microprofile GraphQL API. +This example creates a simple Task APU using Helidon's implementation of the Microprofile GraphQL API Specification. + +Refer: https://github.com/eclipse/microprofile-graphql ## Running the example -1. Build the example +1. Build and Run ```bash mvn clean install @@ -17,7 +19,7 @@ mvn clean install mvn exec:java ``` -## Issuing GraphQL Requests +## Issuing GraphQL requests 1. Access the GraphiQL UI via the following URL: http://127.0.0.1:7001/ui. diff --git a/examples/graphql/basics/pom.xml b/examples/microprofile/graphql/pom.xml similarity index 84% rename from examples/graphql/basics/pom.xml rename to examples/microprofile/graphql/pom.xml index eefb7b9f945..41563bee623 100644 --- a/examples/graphql/basics/pom.xml +++ b/examples/microprofile/graphql/pom.xml @@ -23,12 +23,12 @@ 2.1.1-SNAPSHOT ../../../applications/mp/pom.xml - io.helidon.examples.graphql - helidon-examples-graphql-basics - Helidon GraphQL Examples Basics + io.helidon.examples.microprofile + helidon-examples-microprofile-graphql + Helidon Microprofile Examples GraphQL - Basic usage of GraphQL in Helidon MP + Usage of GraphQL in Helidon MP @@ -36,6 +36,10 @@ io.helidon.microprofile.graphql helidon-microprofile-graphql-server + + io.helidon.microprofile.bundles + helidon-microprofile + diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Main.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/Main.java similarity index 100% rename from examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Main.java rename to examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/Main.java diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Task.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/Task.java similarity index 100% rename from examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/Task.java rename to examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/Task.java diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java similarity index 100% rename from examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java rename to examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java similarity index 100% rename from examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java rename to examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskNotFoundException.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskNotFoundException.java similarity index 100% rename from examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/TaskNotFoundException.java rename to examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskNotFoundException.java diff --git a/examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/package-info.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/package-info.java similarity index 100% rename from examples/graphql/basics/src/main/java/io/helidon/examples/graphql/basics/package-info.java rename to examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/package-info.java diff --git a/examples/graphql/basics/src/main/resources/META-INF/beans.xml b/examples/microprofile/graphql/src/main/resources/META-INF/beans.xml similarity index 100% rename from examples/graphql/basics/src/main/resources/META-INF/beans.xml rename to examples/microprofile/graphql/src/main/resources/META-INF/beans.xml diff --git a/examples/microprofile/pom.xml b/examples/microprofile/pom.xml index 87cfff264cf..da8b3a1b2d8 100644 --- a/examples/microprofile/pom.xml +++ b/examples/microprofile/pom.xml @@ -32,6 +32,7 @@ Helidon Microprofile Examples + graphql hello-world-implicit hello-world-explicit static-content diff --git a/examples/pom.xml b/examples/pom.xml index 8d54103342a..6956b328d57 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -40,7 +40,6 @@ config - graphql grpc health quickstarts diff --git a/microprofile/graphql/server/pom.xml b/microprofile/graphql/server/pom.xml index e9709fde2a9..778a105c0e3 100644 --- a/microprofile/graphql/server/pom.xml +++ b/microprofile/graphql/server/pom.xml @@ -26,7 +26,7 @@ helidon-microprofile-graphql-server Helidon Microprofile GraphQL Server - The microprofile GraphQL Server implementation + The Microprofile GraphQL Server implementation From 6751556bcd621ff558e6ab367f6fcb9ea1580067 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 3 Nov 2020 20:00:08 +0800 Subject: [PATCH 141/178] Minor --- examples/microprofile/graphql/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/microprofile/graphql/README.md b/examples/microprofile/graphql/README.md index 2814b681001..c6a72c4c6bf 100644 --- a/examples/microprofile/graphql/README.md +++ b/examples/microprofile/graphql/README.md @@ -1,6 +1,6 @@ # Microprofile GraphQL Example -This example creates a simple Task APU using Helidon's implementation of the Microprofile GraphQL API Specification. +This example creates a simple Task API using Helidon's implementation of the Microprofile GraphQL API Specification (1.0.3) Refer: https://github.com/eclipse/microprofile-graphql From af5a52578fb7a84baf9913e0babb3d8963224983 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 4 Nov 2020 11:26:47 +0800 Subject: [PATCH 142/178] add graphql example under mp --- examples/microprofile/graphql/README.md | 2 +- .../main/java/io/helidon/examples/graphql/basics/TaskApi.java | 2 +- .../main/java/io/helidon/examples/graphql/basics/TaskDB.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/microprofile/graphql/README.md b/examples/microprofile/graphql/README.md index c6a72c4c6bf..ae639f54f7a 100644 --- a/examples/microprofile/graphql/README.md +++ b/examples/microprofile/graphql/README.md @@ -16,7 +16,7 @@ mvn clean install 2. Run the example ```bash -mvn exec:java +java -jar target/helidon-examples-microprofile-graphql.jar ``` ## Issuing GraphQL requests diff --git a/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java index 0f3143fc1d9..7b1f68ae0ed 100644 --- a/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java +++ b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java @@ -29,7 +29,7 @@ import org.eclipse.microprofile.graphql.Query; /** - * CDI Bean to work with {@link Task}s. + * A CDI Bean that exposes a GraphQL API to query and mutate {@link Task}s. */ @GraphQLApi @ApplicationScoped diff --git a/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java index ecbf5fc0634..a98cbad8ee0 100644 --- a/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java +++ b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java @@ -16,7 +16,7 @@ import javax.enterprise.context.ApplicationScoped; /** - * CDI Bean to work with {@link Task}s. + * Simple {@link Map} based implementation to store {@link Task}s. */ @ApplicationScoped public class TaskDB { From 80ce0bbae6c688ff62b52e1106306be324f89f7d Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 4 Nov 2020 13:24:05 +0800 Subject: [PATCH 143/178] update to graphql example --- examples/microprofile/graphql/README.md | 43 ++++---- .../examples/graphql/basics/TaskApi.java | 56 ++++++++-- .../examples/graphql/basics/TaskDB.java | 102 ------------------ 3 files changed, 73 insertions(+), 128 deletions(-) delete mode 100644 examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java diff --git a/examples/microprofile/graphql/README.md b/examples/microprofile/graphql/README.md index ae639f54f7a..598b9d86c74 100644 --- a/examples/microprofile/graphql/README.md +++ b/examples/microprofile/graphql/README.md @@ -26,7 +26,6 @@ java -jar target/helidon-examples-microprofile-graphql.jar 2. Copy the following commands into the editor on the left. ```graphql - # Fragment to allow shorcut to display all fields for a task fragment task on Task { id @@ -35,6 +34,13 @@ java -jar target/helidon-examples-microprofile-graphql.jar completed } + # Create a task + mutation createTask { + createTask(description: "Task Description 1") { + ...task + } + } + # Find all the tasks query findAllTasks { tasks { @@ -42,6 +48,13 @@ java -jar target/helidon-examples-microprofile-graphql.jar } } + # Find a task + query findTask { + findTask(id: "251474") { + ...task + } + } + # Find completed Tasks query findCompletedTasks { tasks(completed: true) { @@ -56,21 +69,14 @@ java -jar target/helidon-examples-microprofile-graphql.jar } } - # Create a task - mutation createTask { - createTask(description: "Task Description 2") { - ...task - } - } - mutation updateTask { - updateTask(id: "1f6ae5" description:"New Description") { + updateTask(id: "251474" description:"New Description") { ...task } } mutation completeTask { - updateTask(id: "1f6ae5" completed:true) { + updateTask(id: "251474" completed:true) { ...task } } @@ -92,17 +98,18 @@ java -jar target/helidon-examples-microprofile-graphql.jar 3. Run individual commands by clicking on the `Play` button and choosing the query or mutation to run. - 4. Sample requests 1. Execute `createTask` 2. Change the description and execute `createTask` - 3. Execute `findAllTasks` to show the 2 tasks - 4. Change the id and execute `updateTask` to update the existing task - 5. Change the id and execute `completeTask` - 6. Execute `findAllTasks` to show the task completed - 7. Execute `findCompletedTasks` to show only completed tasks - 8. Execute `deleteCompleted` to delete completed task - 9. Execute `findCompletedTasks` to show no completed tasks + 3. Execute `findTask` to show the exception when a task does not exist + 3. Change the id and execute `findTask` to show your newly created task + 5. Execute `findAllTasks` to show the 2 tasks + 6. Change the id and execute `updateTask` to update the existing task + 7. Change the id and execute `completeTask` + 8. Execute `findAllTasks` to show the task completed + 9. Execute `findCompletedTasks` to show only completed tasks + 10. Execute `deleteCompleted` to delete completed task + 11. Execute `findCompletedTasks` to show no completed tasks \ No newline at end of file diff --git a/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java index 7b1f68ae0ed..3a974208a20 100644 --- a/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java +++ b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskApi.java @@ -17,9 +17,13 @@ package io.helidon.examples.graphql.basics; import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.GraphQLApi; @@ -35,8 +39,9 @@ @ApplicationScoped public class TaskApi { - @Inject - private TaskDB taskDB; + private static final String MESSAGE = "Unable to find task with id "; + + private Map tasks = new ConcurrentHashMap<>(); /** * Create a {@link Task}. @@ -47,7 +52,9 @@ public class TaskApi { @Mutation @Description("Create a task with the given description") public Task createTask(@Name("description") String description) { - return taskDB.createTask(description); + Task task = new Task(description); + tasks.put(task.getId(), task); + return task; } /** @@ -59,7 +66,23 @@ public Task createTask(@Name("description") String description) { @Query @Description("Query tasks and optionally specified only completed") public Collection getTasks(@Name("completed") Boolean completed) { - return taskDB.getTasks(completed); + return tasks.values().stream() + .filter(task -> completed == null || task.isCompleted() == completed) + .collect(Collectors.toList()); + } + + /** + * Return a {@link Task}. + * + * @param id task id + * @return the {@link Task} with the given id + * @throws TaskNotFoundException if the task was not found + */ + @Query + @Description("Return a given task") + public Task findTask(@Name("id") @NonNull String id) throws TaskNotFoundException { + return Optional.ofNullable(tasks.get(id)) + .orElseThrow(() -> new TaskNotFoundException(MESSAGE + id)); } /** @@ -72,7 +95,8 @@ public Collection getTasks(@Name("completed") Boolean completed) { @Mutation @Description("Delete a task and return the deleted task details") public Task deleteTask(@Name("id") String id) throws TaskNotFoundException { - return taskDB.deleteTask(id); + return Optional.ofNullable(tasks.remove(id)) + .orElseThrow(() -> new TaskNotFoundException(MESSAGE + id)); } /** @@ -83,7 +107,8 @@ public Task deleteTask(@Name("id") String id) throws TaskNotFoundException { @Mutation @Description("Remove all completed tasks and return the tasks left") public Collection deleteCompletedTasks() { - return taskDB.deleteCompletedTasks(); + tasks.values().removeIf(Task::isCompleted); + return tasks.values(); } /** @@ -100,6 +125,21 @@ public Collection deleteCompletedTasks() { public Task updateTask(@Name("id") @NonNull String id, @Name("description") String description, @Name("completed") Boolean completed) throws TaskNotFoundException { - return taskDB.updateTask(id, description, completed); + + try { + return tasks.compute(id, (k, v) -> { + Objects.requireNonNull(v); + + if (description != null) { + v.setDescription(description); + } + if (completed != null) { + v.setCompleted(completed); + } + return v; + }); + } catch (Exception e) { + throw new TaskNotFoundException(MESSAGE + id); + } } } diff --git a/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java b/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java deleted file mode 100644 index a98cbad8ee0..00000000000 --- a/examples/microprofile/graphql/src/main/java/io/helidon/examples/graphql/basics/TaskDB.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2020 Oracle and/or its affiliates. - * - * Licensed under the Universal Permissive License v 1.0 as shown at - * https://oss.oracle.com/licenses/upl. - */ - -package io.helidon.examples.graphql.basics; - -import java.util.Collection; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import javax.enterprise.context.ApplicationScoped; - -/** - * Simple {@link Map} based implementation to store {@link Task}s. - */ -@ApplicationScoped -public class TaskDB { - - private Map tasks = new ConcurrentHashMap<>(); - - /** - * Create a {@link Task}. - * - * @param description task description - * @return the created {@link Task} - */ - public Task createTask(String description) { - Task task = new Task(description); - tasks.put(task.getId(), task); - return task; - } - - /** - * Query {@link Task}s. - * - * @param completed optionally specify completion status - * @return a {@link Collection} of {@link Task}s - */ - public Collection getTasks(Boolean completed) { - return tasks.values().stream() - .filter(task -> completed == null || task.isCompleted() == completed) - .collect(Collectors.toList()); - } - - /** - * Delete a {@link Task}. - * - * @param id task to delete - * @return the deleted {@link Task} - * @throws TaskNotFoundException if the task was not found - */ - public Task deleteTask(String id) throws TaskNotFoundException { - Task task = tasks.remove(id); - if (task == null) { - throw new TaskNotFoundException("Unable to find task with id " + id); - } - return task; - } - - /** - * Remove all completed {@link Task}s. - * - * @return the {@link Task}s left - */ - public Collection deleteCompletedTasks() { - tasks.values().removeIf(Task::isCompleted); - return tasks.values(); - } - - /** - * Update a {@link Task}. - * - * @param id task to update - * @param description optional description - * @param completed optional completed - * @return the updated {@link Task} - * @throws TaskNotFoundException if the task was not found - */ - public Task updateTask(String id, String description, Boolean completed) throws TaskNotFoundException { - - try { - return tasks.compute(id, (k, v) -> { - Objects.requireNonNull(v); - - if (description != null) { - v.setDescription(description); - } - if (completed != null) { - v.setCompleted(completed); - } - return v; - }); - } catch (Exception e) { - throw new TaskNotFoundException("Unable to find task with id " + id); - } - } -} From 59225b9bb60e50deebfbc1e306f69cbd1b0d309a Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 4 Nov 2020 13:25:52 +0800 Subject: [PATCH 144/178] update to graphql example --- examples/microprofile/graphql/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/microprofile/graphql/README.md b/examples/microprofile/graphql/README.md index 598b9d86c74..42c06456e7e 100644 --- a/examples/microprofile/graphql/README.md +++ b/examples/microprofile/graphql/README.md @@ -7,7 +7,7 @@ Refer: https://github.com/eclipse/microprofile-graphql ## Running the example -1. Build and Run +1. Build ```bash mvn clean install From ac0e48447eea6572e240dc60069544f4e491b2a9 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 4 Nov 2020 13:29:22 +0800 Subject: [PATCH 145/178] update to graphql example --- examples/microprofile/graphql/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/microprofile/graphql/README.md b/examples/microprofile/graphql/README.md index 42c06456e7e..61cddaa93ba 100644 --- a/examples/microprofile/graphql/README.md +++ b/examples/microprofile/graphql/README.md @@ -2,7 +2,9 @@ This example creates a simple Task API using Helidon's implementation of the Microprofile GraphQL API Specification (1.0.3) -Refer: https://github.com/eclipse/microprofile-graphql +See [here](https://github.com/eclipse/microprofile-graphql) for more information on the +Microprofile GraphQL Specification as well as the [Helidon documentation](https://helidon.io/docs/v2/#/mp/introduction/01_introduction) +for and introduction to using GraphQL in Helidon MP. ## Running the example From 0f173d03cfa294770fdb8b8acd164c15165ff4e6 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 4 Nov 2020 13:30:03 +0800 Subject: [PATCH 146/178] update to graphql example --- examples/microprofile/graphql/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/microprofile/graphql/README.md b/examples/microprofile/graphql/README.md index 61cddaa93ba..58c86f44069 100644 --- a/examples/microprofile/graphql/README.md +++ b/examples/microprofile/graphql/README.md @@ -4,8 +4,7 @@ This example creates a simple Task API using Helidon's implementation of the Mic See [here](https://github.com/eclipse/microprofile-graphql) for more information on the Microprofile GraphQL Specification as well as the [Helidon documentation](https://helidon.io/docs/v2/#/mp/introduction/01_introduction) -for and introduction to using GraphQL in Helidon MP. - +for an introduction to using GraphQL in Helidon MP. ## Running the example From 4abb628255add6ea9337dde460f722ab1d422d7f Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 4 Nov 2020 13:37:38 +0800 Subject: [PATCH 147/178] cleanup --- .../server/src/main/resources/web/index.html | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/microprofile/graphql/server/src/main/resources/web/index.html b/microprofile/graphql/server/src/main/resources/web/index.html index a8dfcbc5538..8729643d486 100644 --- a/microprofile/graphql/server/src/main/resources/web/index.html +++ b/microprofile/graphql/server/src/main/resources/web/index.html @@ -26,18 +26,9 @@

- - - + + +