diff --git a/graphql-jpa-query-autoconfigure/pom.xml b/graphql-jpa-query-autoconfigure/pom.xml index 23ef872b7..8f971f476 100644 --- a/graphql-jpa-query-autoconfigure/pom.xml +++ b/graphql-jpa-query-autoconfigure/pom.xml @@ -9,6 +9,15 @@ graphql-jpa-query-autoconfigure + + com.introproventures + graphql-jpa-query-scalars + + + com.introproventures + graphql-jpa-query-schema + true + com.graphql-java graphql-java @@ -25,7 +34,6 @@ org.hibernate.validator hibernate-validator - test org.springframework.boot diff --git a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLJpaQueryProperties.java b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLJpaQueryProperties.java index 8cec53f35..dd24595e1 100644 --- a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLJpaQueryProperties.java +++ b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLJpaQueryProperties.java @@ -15,11 +15,11 @@ */ package com.introproventures.graphql.jpa.query.autoconfigure; -import javax.validation.constraints.NotEmpty; - import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; +import javax.validation.constraints.NotEmpty; + @ConfigurationProperties(prefix="spring.graphql.jpa.query") @Validated public class GraphQLJpaQueryProperties { @@ -61,7 +61,11 @@ public class GraphQLJpaQueryProperties { */ private boolean enabled; - @NotEmpty + /** + * Web path for web controller + * Use 'spring.graphql.jpa.query.web.path' to customize default /graphql path + */ + @Deprecated private String path; /** diff --git a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfiguration.java b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfiguration.java index 0f35f52d2..83d8e668b 100644 --- a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfiguration.java +++ b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfiguration.java @@ -1,8 +1,7 @@ package com.introproventures.graphql.jpa.query.autoconfigure; -import java.util.ArrayList; -import java.util.List; - +import graphql.GraphQL; +import graphql.schema.GraphQLSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -13,8 +12,8 @@ import org.springframework.context.annotation.PropertySources; import org.springframework.util.CollectionUtils; -import graphql.GraphQL; -import graphql.schema.GraphQLSchema; +import java.util.ArrayList; +import java.util.List; @Configuration @ConditionalOnClass(GraphQL.class) @@ -27,9 +26,6 @@ public class GraphQLSchemaAutoConfiguration { private final List graphQLSchemaConfigurers = new ArrayList<>(); - @Autowired - private GraphQLJpaQueryProperties properties; - @Autowired(required = true) public void setGraphQLSchemaConfigurers(List configurers) { if (!CollectionUtils.isEmpty(configurers)) { @@ -39,7 +35,7 @@ public void setGraphQLSchemaConfigurers(List configurer @Bean @ConditionalOnMissingBean(GraphQLSchema.class) - public GraphQLSchemaFactoryBean graphQLSchemaFactoryBean() { + public GraphQLSchemaFactoryBean graphQLSchemaFactoryBean(GraphQLJpaQueryProperties properties) { GraphQLShemaRegistrationImpl graphQLShemaRegistration = new GraphQLShemaRegistrationImpl(); for (GraphQLSchemaConfigurer configurer : graphQLSchemaConfigurers) { diff --git a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaBuilderAutoConfiguration.java b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaBuilderAutoConfiguration.java new file mode 100644 index 000000000..349d0c0a7 --- /dev/null +++ b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaBuilderAutoConfiguration.java @@ -0,0 +1,46 @@ +package com.introproventures.graphql.jpa.query.autoconfigure; + +import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder; +import com.introproventures.graphql.jpa.query.schema.RestrictedKeysProvider; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; +import graphql.GraphQL; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.persistence.EntityManagerFactory; + +@Configuration +@ConditionalOnClass({GraphQL.class, GraphQLSchemaBuilder.class}) +@ConditionalOnProperty(name="spring.graphql.jpa.query.enabled", havingValue="true", matchIfMissing=true) +@AutoConfigureBefore(GraphQLSchemaAutoConfiguration.class) +@AutoConfigureAfter(HibernateJpaAutoConfiguration.class) +public class GraphQLSchemaBuilderAutoConfiguration { + @Bean + @ConditionalOnMissingBean + @ConditionalOnSingleCandidate(EntityManagerFactory.class) + public GraphQLSchemaBuilder graphQLJpaSchemaBuilder(final EntityManagerFactory entityManagerFactory, + ObjectProvider restrictedKeysProvider) { + GraphQLJpaSchemaBuilder bean = new GraphQLJpaSchemaBuilder(entityManagerFactory.createEntityManager()); + + restrictedKeysProvider.ifAvailable(bean::restrictedKeysProvider); + + return bean; + } + + @Bean + @ConditionalOnMissingBean + public GraphQLSchemaConfigurer graphQLJpaQuerySchemaConfigurer(GraphQLSchemaBuilder graphQLSchemaBuilder) { + + return (registry) -> { + registry.register(graphQLSchemaBuilder.build()); + }; + } +} diff --git a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaConfigurer.java b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaConfigurer.java index 88226dd1c..a802d4189 100644 --- a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaConfigurer.java +++ b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaConfigurer.java @@ -1,5 +1,6 @@ package com.introproventures.graphql.jpa.query.autoconfigure; +@FunctionalInterface public interface GraphQLSchemaConfigurer { void configure(GraphQLShemaRegistration registry); diff --git a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaFactoryBean.java b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaFactoryBean.java index f7d813404..962d12fa2 100644 --- a/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaFactoryBean.java +++ b/graphql-jpa-query-autoconfigure/src/main/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaFactoryBean.java @@ -1,26 +1,5 @@ package com.introproventures.graphql.jpa.query.autoconfigure; -import static graphql.Assert.assertTrue; -import static graphql.schema.FieldCoordinates.coordinates; -import static graphql.util.TraversalControl.CONTINUE; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import graphql.schema.GraphQLNamedType; -import graphql.schema.GraphQLSchemaElement; -import org.springframework.beans.factory.config.AbstractFactoryBean; - import graphql.Internal; import graphql.schema.DataFetcher; import graphql.schema.FieldCoordinates; @@ -29,8 +8,10 @@ import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLFieldsContainer; import graphql.schema.GraphQLInterfaceType; +import graphql.schema.GraphQLNamedType; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; +import graphql.schema.GraphQLSchemaElement; import graphql.schema.GraphQLType; import graphql.schema.GraphQLTypeVisitorStub; import graphql.schema.GraphQLUnionType; @@ -38,7 +19,24 @@ import graphql.schema.TypeResolver; import graphql.util.TraversalControl; import graphql.util.TraverserContext; -import graphql.util.Traverser; +import org.springframework.beans.factory.config.AbstractFactoryBean; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static graphql.Assert.assertTrue; +import static graphql.schema.FieldCoordinates.coordinates; +import static graphql.util.TraversalControl.CONTINUE; public class GraphQLSchemaFactoryBean extends AbstractFactoryBean{ @@ -145,7 +143,7 @@ protected GraphQLSchema createInstance() throws Exception { if (!types.isEmpty()) { schemaBuilder.additionalTypes(types); } - + if(!mutations.isEmpty()) schemaBuilder.mutation(GraphQLObjectType.newObject() .name(this.mutationName) diff --git a/graphql-jpa-query-autoconfigure/src/main/resources/META-INF/spring.factories b/graphql-jpa-query-autoconfigure/src/main/resources/META-INF/spring.factories index ab8188c15..878f1d69a 100644 --- a/graphql-jpa-query-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/graphql-jpa-query-autoconfigure/src/main/resources/META-INF/spring.factories @@ -1,2 +1,3 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.introproventures.graphql.jpa.query.autoconfigure.GraphQLSchemaAutoConfiguration \ No newline at end of file + com.introproventures.graphql.jpa.query.autoconfigure.GraphQLSchemaAutoConfiguration,\ + com.introproventures.graphql.jpa.query.autoconfigure.GraphQLSchemaBuilderAutoConfiguration \ No newline at end of file diff --git a/graphql-jpa-query-autoconfigure/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties b/graphql-jpa-query-autoconfigure/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties index e3068c380..4e43116f0 100644 --- a/graphql-jpa-query-autoconfigure/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties +++ b/graphql-jpa-query-autoconfigure/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties @@ -3,4 +3,3 @@ spring.graphql.jpa.query.description=Query root spring.graphql.jpa.query.useDistinctParameter=false spring.graphql.jpa.query.defaultDistinct=true spring.graphql.jpa.query.enabled=true -spring.graphql.jpa.query.path=/graphql \ No newline at end of file diff --git a/graphql-jpa-query-autoconfigure/src/test/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfigurationTest.java b/graphql-jpa-query-autoconfigure/src/test/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfigurationTest.java index bf72e150e..0366faf7f 100644 --- a/graphql-jpa-query-autoconfigure/src/test/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfigurationTest.java +++ b/graphql-jpa-query-autoconfigure/src/test/java/com/introproventures/graphql/jpa/query/autoconfigure/GraphQLSchemaAutoConfigurationTest.java @@ -1,33 +1,9 @@ package com.introproventures.graphql.jpa.query.autoconfigure; -import static graphql.annotations.AnnotationsSchemaCreator.newAnnotationsSchema; -import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.reflections.Reflections; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.StringUtils; - import com.introproventures.graphql.jpa.query.autoconfigure.support.AdditionalGraphQLType; import com.introproventures.graphql.jpa.query.autoconfigure.support.MutationRoot; import com.introproventures.graphql.jpa.query.autoconfigure.support.QueryRoot; import com.introproventures.graphql.jpa.query.autoconfigure.support.SubscriptionRoot; - -import graphql.Directives; import graphql.GraphQL; import graphql.Scalars; import graphql.annotations.AnnotationsSchemaCreator; @@ -35,6 +11,7 @@ import graphql.annotations.annotationTypes.GraphQLInvokeDetached; import graphql.annotations.annotationTypes.GraphQLName; import graphql.annotations.annotationTypes.directives.definition.GraphQLDirectiveDefinition; +import graphql.scalars.ExtendedScalars; import graphql.schema.FieldCoordinates; import graphql.schema.GraphQLCodeRegistry; import graphql.schema.GraphQLDirective; @@ -42,6 +19,27 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.StaticDataFetcher; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.reflections.Reflections; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.util.StringUtils; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static graphql.annotations.AnnotationsSchemaCreator.newAnnotationsSchema; +import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment=WebEnvironment.NONE) @@ -124,7 +122,13 @@ public static class AnnotatedQuery implements QueryRoot { public Greeting greeting(@GraphQLName("name") String name) { return new Greeting("Hi, " + name + "!"); } - + + @GraphQLField + @GraphQLInvokeDetached + public Long count() { + return Long.valueOf(1); + } + public static class Greeting { @GraphQLField @@ -151,6 +155,9 @@ public void configure(GraphQLShemaRegistration registry) { .field(GraphQLFieldDefinition.newFieldDefinition() .name("greet2") .type(Scalars.GraphQLString)) + .field(GraphQLFieldDefinition.newFieldDefinition() + .name("count1") + .type(ExtendedScalars.GraphQLLong)) .build(); GraphQLCodeRegistry codeRegistry = GraphQLCodeRegistry.newCodeRegistry() diff --git a/graphql-jpa-query-autoconfigure/src/test/resources/application.yml b/graphql-jpa-query-autoconfigure/src/test/resources/application.yml index 97b45f456..183917371 100644 --- a/graphql-jpa-query-autoconfigure/src/test/resources/application.yml +++ b/graphql-jpa-query-autoconfigure/src/test/resources/application.yml @@ -11,4 +11,3 @@ spring: name: GraphQLBooks description: GraphQL Books Schema Description enabled: true - path: /graphql \ No newline at end of file diff --git a/graphql-jpa-query-boot-starter-graphql/LICENSE b/graphql-jpa-query-boot-starter-graphql/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/graphql-jpa-query-boot-starter-graphql/pom.xml b/graphql-jpa-query-boot-starter-graphql/pom.xml new file mode 100644 index 000000000..36194fb8a --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/pom.xml @@ -0,0 +1,61 @@ + + 4.0.0 + + graphql-jpa-query-boot-starter-graphql + graphql-jpa-query-boot-starter-graphql + + + com.introproventures + graphql-jpa-query-build + 0.5.2-SNAPSHOT + ../graphql-jpa-query-build + + + + + + com.introproventures + graphql-jpa-query-schema + + + + com.introproventures + graphql-jpa-query-autoconfigure + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-graphql + true + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.hibernate.validator + hibernate-validator + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.graphql + spring-graphql-test + test + + + + + \ No newline at end of file diff --git a/graphql-jpa-query-boot-starter-graphql/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryGraphQlExecutionAutoConfiguration.java b/graphql-jpa-query-boot-starter-graphql/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryGraphQlExecutionAutoConfiguration.java new file mode 100644 index 000000000..c50778c98 --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryGraphQlExecutionAutoConfiguration.java @@ -0,0 +1,53 @@ +package com.introproventures.graphql.jpa.query.boot.autoconfigure; + +import org.dataloader.DataLoaderOptions; +import org.dataloader.MappedBatchLoaderWithContext; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.graphql.ExecutionGraphQlService; +import org.springframework.graphql.execution.BatchLoaderRegistry; +import org.springframework.graphql.execution.GraphQlSource; +import reactor.core.publisher.Mono; + +import static com.introproventures.graphql.jpa.query.schema.impl.BatchLoaderRegistry.newDataLoaderRegistry; + +@Configuration +@AutoConfigureAfter(GraphQlAutoConfiguration.class) +public class GraphQLJpaQueryGraphQlExecutionAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + BatchLoaderRegistry batchLoaderRegistry(ListableBeanFactory beanFactory) { + return new GraphQlAutoConfiguration(beanFactory).batchLoaderRegistry(); + } + + @Bean + @ConditionalOnMissingBean + ExecutionGraphQlService executionGraphQlService(ListableBeanFactory beanFactory, + GraphQlSource graphQlSource, + BatchLoaderRegistry batchLoaderRegistry) { + return new GraphQlAutoConfiguration(beanFactory).executionGraphQlService(graphQlSource, + batchLoaderRegistry); + } + + @Bean + InitializingBean batchLoaderRegistryConfigurer(BatchLoaderRegistry batchLoaderRegistry) { + return () -> { + DataLoaderOptions options = DataLoaderOptions.newOptions() + .setCachingEnabled(false); + newDataLoaderRegistry(options) + .getDataLoadersMap() + .entrySet() + .stream() + .forEach(entry -> batchLoaderRegistry.forName(entry.getKey()) + .withOptions(options) + .registerMappedBatchLoader((keys, env) -> + Mono.fromCompletionStage(((MappedBatchLoaderWithContext) entry.getValue()).load(keys, env)))); + }; + } +} diff --git a/graphql-jpa-query-boot-starter-graphql/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryGraphQlSourceAutoConfiguration.java b/graphql-jpa-query-boot-starter-graphql/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryGraphQlSourceAutoConfiguration.java new file mode 100644 index 000000000..ec4b243a5 --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryGraphQlSourceAutoConfiguration.java @@ -0,0 +1,102 @@ +/* + * Copyright 2017 IntroPro Ventures, Inc. 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 com.introproventures.graphql.jpa.query.boot.autoconfigure; + +import com.introproventures.graphql.jpa.query.autoconfigure.GraphQLSchemaConfigurer; +import graphql.GraphQL; +import graphql.execution.instrumentation.Instrumentation; +import graphql.schema.GraphQLSchema; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.graphql.ConditionalOnGraphQlSchema; +import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration; +import org.springframework.boot.autoconfigure.graphql.GraphQlProperties; +import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.graphql.execution.DataFetcherExceptionResolver; +import org.springframework.graphql.execution.GraphQlSource; +import org.springframework.graphql.execution.RuntimeWiringConfigurer; +import org.springframework.graphql.execution.SubscriptionExceptionResolver; + +import java.util.function.Consumer; +import java.util.stream.Collectors; + +@Configuration +@ConditionalOnClass({GraphQL.class, GraphQlSource.class, GraphQLSchemaConfigurer.class}) +@ConditionalOnProperty(name="spring.graphql.jpa.query.enabled", havingValue="true", matchIfMissing=true) +@EnableConfigurationProperties(GraphQlProperties.class) +@AutoConfigureBefore(GraphQlAutoConfiguration.class) +public class GraphQLJpaQueryGraphQlSourceAutoConfiguration { + + @Bean + @ConditionalOnGraphQlSchema + GraphQLSchemaConfigurer graphQlSourceSchemaConfigurer(ListableBeanFactory beanFactory, + ResourcePatternResolver resourcePatternResolver, + GraphQlProperties properties, + ObjectProvider exceptionResolvers, + ObjectProvider subscriptionExceptionResolvers, + ObjectProvider instrumentations, + ObjectProvider wiringConfigurers, + ObjectProvider sourceCustomizers) { + return registry -> { + GraphQlAutoConfiguration graphQlAutoConfiguration = new GraphQlAutoConfiguration(beanFactory); + + GraphQlSource graphQlSource = graphQlAutoConfiguration.graphQlSource(resourcePatternResolver, + properties, + exceptionResolvers, + subscriptionExceptionResolvers, + instrumentations, + wiringConfigurers, + sourceCustomizers); + registry.register(graphQlSource.schema()); + }; + } + + @Bean + @ConditionalOnMissingBean + public GraphQlSource graphQlSource(GraphQLSchema graphQLSchema, + ObjectProvider exceptionResolvers, + ObjectProvider subscriptionExceptionResolvers, + ObjectProvider instrumentations, + ObjectProvider> configurers) { + GraphQlSource.Builder builder = GraphQlSource.builder(graphQLSchema); + + builder.exceptionResolvers(exceptionResolvers.orderedStream().collect(Collectors.toList())) + .subscriptionExceptionResolvers(subscriptionExceptionResolvers.orderedStream() + .collect(Collectors.toList())) + .instrumentation(instrumentations.orderedStream() + .collect(Collectors.toList())); + + configurers.orderedStream() + .forEach(builder::configureGraphQl); + + return builder.build(); + } + + @Bean + @ConditionalOnMissingBean + public JavaScalarsRuntimeWiringConfigurer javaScalarsRuntimeWiringConfigurer() { + return new JavaScalarsRuntimeWiringConfigurer(); + } + +} diff --git a/graphql-jpa-query-boot-starter-graphql/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/JavaScalarsRuntimeWiringConfigurer.java b/graphql-jpa-query-boot-starter-graphql/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/JavaScalarsRuntimeWiringConfigurer.java new file mode 100644 index 000000000..6730fea7e --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/JavaScalarsRuntimeWiringConfigurer.java @@ -0,0 +1,16 @@ +package com.introproventures.graphql.jpa.query.boot.autoconfigure; + +import com.introproventures.graphql.jpa.query.schema.JavaScalars; +import com.introproventures.graphql.jpa.query.schema.JavaScalarsWiringPostProcessor; +import graphql.schema.idl.RuntimeWiring; +import org.springframework.graphql.execution.RuntimeWiringConfigurer; + +public class JavaScalarsRuntimeWiringConfigurer implements RuntimeWiringConfigurer { + @Override + public void configure(RuntimeWiring.Builder wiringBuilder) { + JavaScalars.scalars() + .forEach(wiringBuilder::scalar); + + wiringBuilder.transformer(new JavaScalarsWiringPostProcessor()); + } +} diff --git a/graphql-jpa-query-boot-starter-graphql/src/main/resources/META-INF/spring.factories b/graphql-jpa-query-boot-starter-graphql/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..b9fb93032 --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.introproventures.graphql.jpa.query.boot.autoconfigure.GraphQLJpaQueryGraphQlSourceAutoConfiguration,\ +com.introproventures.graphql.jpa.query.boot.autoconfigure.GraphQLJpaQueryGraphQlExecutionAutoConfiguration \ No newline at end of file diff --git a/graphql-jpa-query-boot-starter-graphql/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties b/graphql-jpa-query-boot-starter-graphql/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties new file mode 100644 index 000000000..e517e7b7d --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties @@ -0,0 +1,2 @@ +spring.graphql.jpa.query.name=GraphQLJpaQueryBootStarterGraphQl +spring.graphql.jpa.query.description=GraphQL Jpa Query Schema Boot Starter GraphQl Specification diff --git a/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/boot/autoconfigure/GraphQLJpaQueryGraphQlSourceAutoConfigurationTest.java b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/boot/autoconfigure/GraphQLJpaQueryGraphQlSourceAutoConfigurationTest.java new file mode 100644 index 000000000..c8024f518 --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/boot/autoconfigure/GraphQLJpaQueryGraphQlSourceAutoConfigurationTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017 IntroPro Ventures, Inc. 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 com.introproventures.graphql.jpa.query.boot.test.boot.autoconfigure; + +import com.introproventures.graphql.jpa.query.boot.autoconfigure.JavaScalarsRuntimeWiringConfigurer; +import com.introproventures.graphql.jpa.query.boot.test.starter.model.Author; +import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.graphql.ExecutionGraphQlService; +import org.springframework.graphql.execution.BatchLoaderRegistry; +import org.springframework.graphql.execution.GraphQlSource; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class GraphQLJpaQueryGraphQlSourceAutoConfigurationTest { + + @SpringBootApplication + @EntityScan(basePackageClasses=Author.class) + static class Application { + } + + @Autowired + private JavaScalarsRuntimeWiringConfigurer javaScalarsRuntimeWiringConfigurer; + + @Autowired + private GraphQLSchemaBuilder graphQLSchemaBuilder; + + @Autowired + private GraphQLSchema graphQLSchema; + + @Autowired(required = false) + private GraphQlSource graphQlSource; + + @Autowired + BatchLoaderRegistry batchLoaderRegistry; + + @Autowired + ExecutionGraphQlService executionGraphQlService; + + @Test + public void contextIsAutoConfigured() { + assertThat(javaScalarsRuntimeWiringConfigurer).isNotNull(); + + assertThat(graphQLSchemaBuilder).isNotNull() + .isInstanceOf(GraphQLJpaSchemaBuilder.class); + + assertThat(graphQLSchema.getQueryType()) + .extracting(GraphQLObjectType::getName, GraphQLObjectType::getDescription) + .containsExactly("GraphQLBooks", "GraphQL Books Schema Description"); + + assertThat(graphQlSource).isNotNull(); + + } +} \ No newline at end of file diff --git a/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/GraphQLJpaQueryStarterIT.java b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/GraphQLJpaQueryStarterIT.java new file mode 100644 index 000000000..2c5f576a9 --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/GraphQLJpaQueryStarterIT.java @@ -0,0 +1,86 @@ +/* + * Copyright 2017 IntroPro Ventures, Inc. 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 com.introproventures.graphql.jpa.query.boot.test.starter; + +import com.introproventures.graphql.jpa.query.boot.test.starter.model.Book; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.context.annotation.Bean; +import org.springframework.graphql.ExecutionGraphQlService; +import org.springframework.graphql.ResponseError; +import org.springframework.graphql.test.tester.ExecutionGraphQlServiceTester; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.NONE) +public class GraphQLJpaQueryStarterIT { + private static final String WAR_AND_PEACE = "War and Peace"; + + @SpringBootApplication + static class Application { + @Bean + ExecutionGraphQlServiceTester graphQlTester(ExecutionGraphQlService graphQlService) { + return ExecutionGraphQlServiceTester.create(graphQlService); + } + } + + @Autowired + private ExecutionGraphQlServiceTester graphQlTester; + + @Test + public void testGraphql() { + graphQlTester.document("{Books(where:{title:{EQ: \"" + WAR_AND_PEACE + "\"}}){ select {title genre}}}") + .execute() + .errors().verify() + .path("Books.select").entityList(Book.class).hasSize(1); + } + + @Test + public void testGraphqlArguments() { + graphQlTester.document("query BookQuery($title: String!){Books(where:{title:{EQ: $title}}){select{title genre}}}") + .variable("title", WAR_AND_PEACE) + .execute() + .errors().verify() + .path("Books.select").entityList(Map.class).hasSize(1) + .satisfies(result -> assertThat(result).extracting("title", "genre") + .contains(tuple("War and Peace", "NOVEL"))); + } + + @Test + public void testGraphqlErrorResult() { + graphQlTester.document("{ }") + .execute() + .errors().satisfy(result -> { + assertThat(result).extracting(ResponseError::getMessage) + .isNotEmpty(); + assertThat(result).extracting(ResponseError::getExtensions) + .isNotEmpty(); + assertThat(result).extracting(ResponseError::getLocations) + .isNotEmpty(); + }); + + } +} + diff --git a/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Author.java b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Author.java new file mode 100644 index 000000000..d5800ca6e --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Author.java @@ -0,0 +1,37 @@ +/* + * Copyright 2017 IntroPro Ventures Inc. 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 com.introproventures.graphql.jpa.query.boot.test.starter.model; + +import java.util.Collection; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +import lombok.Data; + +@Data +@Entity +public class Author { + @Id + Long id; + + String name; + + @OneToMany(mappedBy="author") + Collection books; +} diff --git a/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Book.java b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Book.java new file mode 100644 index 000000000..1dd12a722 --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Book.java @@ -0,0 +1,40 @@ +/* + * Copyright 2017 IntroPro Ventures Inc. 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 com.introproventures.graphql.jpa.query.boot.test.starter.model; + +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import lombok.Data; + +@Data +@Entity +public class Book { + @Id + Long id; + + String title; + + @ManyToOne + Author author; + + @Enumerated(EnumType.STRING) + Genre genre; +} diff --git a/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Genre.java b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Genre.java new file mode 100644 index 000000000..bd1a8cb3c --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/test/java/com/introproventures/graphql/jpa/query/boot/test/starter/model/Genre.java @@ -0,0 +1,21 @@ +/* + * Copyright 2017 IntroPro Ventures Inc. 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 com.introproventures.graphql.jpa.query.boot.test.starter.model; + +public enum Genre { + NOVEL, PLAY +} diff --git a/graphql-jpa-query-boot-starter-graphql/src/test/resources/application.yml b/graphql-jpa-query-boot-starter-graphql/src/test/resources/application.yml new file mode 100644 index 000000000..3e1459b85 --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/test/resources/application.yml @@ -0,0 +1,14 @@ +spring: + jpa: + hibernate.ddl-auto: create-drop + show-sql: true + defer-datasource-initialization: true + h2: + console.enabled: true + + graphql: + jpa: + query: + name: GraphQLBooks + description: GraphQL Books Schema Description + enabled: true diff --git a/graphql-jpa-query-boot-starter-graphql/src/test/resources/data.sql b/graphql-jpa-query-boot-starter-graphql/src/test/resources/data.sql new file mode 100644 index 000000000..8e24ee48a --- /dev/null +++ b/graphql-jpa-query-boot-starter-graphql/src/test/resources/data.sql @@ -0,0 +1,8 @@ +-- Books +insert into author (id, name) values (1, 'Leo Tolstoy'); +insert into book (id, title, author_id, genre) values (2, 'War and Peace', 1, 'NOVEL'); +insert into book (id, title, author_id, genre) values (3, 'Anna Karenina', 1, 'NOVEL'); +insert into author (id, name) values (4, 'Anton Chekhov'); +insert into book (id, title, author_id, genre) values (5, 'The Cherry Orchard', 4, 'PLAY'); +insert into book (id, title, author_id, genre) values (6, 'The Seagull', 4, 'PLAY'); +insert into book (id, title, author_id, genre) values (7, 'Three Sisters', 4, 'PLAY'); diff --git a/graphql-jpa-query-boot-starter/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryAutoConfiguration.java b/graphql-jpa-query-boot-starter/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryAutoConfiguration.java index 6ae1334e3..9eae9ee0c 100644 --- a/graphql-jpa-query-boot-starter/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryAutoConfiguration.java +++ b/graphql-jpa-query-boot-starter/src/main/java/com/introproventures/graphql/jpa/query/boot/autoconfigure/GraphQLJpaQueryAutoConfiguration.java @@ -15,36 +15,27 @@ */ package com.introproventures.graphql.jpa.query.boot.autoconfigure; -import java.util.function.Supplier; - -import javax.persistence.EntityManagerFactory; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.introproventures.graphql.jpa.query.autoconfigure.GraphQLSchemaConfigurer; import com.introproventures.graphql.jpa.query.schema.GraphQLExecutionInputFactory; import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; import com.introproventures.graphql.jpa.query.schema.GraphQLExecutorContextFactory; -import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder; -import com.introproventures.graphql.jpa.query.schema.RestrictedKeysProvider; import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutorContextFactory; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; - import graphql.GraphQL; import graphql.GraphQLContext; import graphql.execution.ExecutionStrategy; import graphql.execution.instrumentation.Instrumentation; import graphql.schema.GraphQLSchema; import graphql.schema.visibility.GraphqlFieldVisibility; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.function.Supplier; @Configuration @ConditionalOnClass(GraphQL.class) @@ -55,33 +46,12 @@ public class GraphQLJpaQueryAutoConfiguration { @Configuration public static class DefaultGraphQLJpaQueryConfiguration { - @Bean - @ConditionalOnMissingBean - @ConditionalOnSingleCandidate(EntityManagerFactory.class) - public GraphQLSchemaBuilder graphQLJpaSchemaBuilder(final EntityManagerFactory entityManagerFactory, - ObjectProvider restrictedKeysProvider) { - GraphQLJpaSchemaBuilder bean = new GraphQLJpaSchemaBuilder(entityManagerFactory.createEntityManager()); - - restrictedKeysProvider.ifAvailable(bean::restrictedKeysProvider); - - return bean; - } - - @Bean - @ConditionalOnMissingBean - public GraphQLSchemaConfigurer graphQLJpaQuerySchemaConfigurer(GraphQLSchemaBuilder graphQLSchemaBuilder) { - - return (registry) -> { - registry.register(graphQLSchemaBuilder.build()); - }; - } - @Bean @ConditionalOnMissingBean public GraphQLExecutorContextFactory graphQLJpaExecutorContextFactory(ObjectProvider graphQLExecutionInputFactory, - ObjectProvider> graphqlFieldVisibility, - ObjectProvider> instrumentation, - ObjectProvider> graphqlContext, + ObjectProvider> graphqlFieldVisibility, + ObjectProvider> instrumentation, + ObjectProvider> graphqlContext, ObjectProvider> queryExecutionStrategy, ObjectProvider> mutationExecutionStrategy, ObjectProvider> subscriptionExecutionStrategy) { diff --git a/graphql-jpa-query-boot-starter/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties b/graphql-jpa-query-boot-starter/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties index 5465f5831..b2f212704 100644 --- a/graphql-jpa-query-boot-starter/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties +++ b/graphql-jpa-query-boot-starter/src/main/resources/com/introproventures/graphql/jpa/query/boot/autoconfigure/default.properties @@ -3,4 +3,3 @@ spring.graphql.jpa.query.description=GraphQL Jpa Query Schema Specification spring.graphql.jpa.query.useDistinctParameter=false spring.graphql.jpa.query.distinctFetcher=false spring.graphql.jpa.query.enabled=true -spring.graphql.jpa.query.path=/graphql \ No newline at end of file diff --git a/graphql-jpa-query-boot-starter/src/test/resources/application.yml b/graphql-jpa-query-boot-starter/src/test/resources/application.yml index 5db14f24d..72cb9082b 100644 --- a/graphql-jpa-query-boot-starter/src/test/resources/application.yml +++ b/graphql-jpa-query-boot-starter/src/test/resources/application.yml @@ -12,4 +12,5 @@ spring: name: GraphQLBooks description: GraphQL Books Schema Description enabled: true - path: /graphql \ No newline at end of file + web: + path: /graphql \ No newline at end of file diff --git a/graphql-jpa-query-dependencies/LICENSE b/graphql-jpa-query-dependencies/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/graphql-jpa-query-dependencies/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/graphql-jpa-query-dependencies/pom.xml b/graphql-jpa-query-dependencies/pom.xml index abdae4550..f3761f908 100644 --- a/graphql-jpa-query-dependencies/pom.xml +++ b/graphql-jpa-query-dependencies/pom.xml @@ -51,6 +51,11 @@ graphql-jpa-query-annotations ${project.version} + + com.introproventures + graphql-jpa-query-scalars + ${project.version} + com.introproventures graphql-jpa-query-schema @@ -61,6 +66,11 @@ graphql-jpa-query-boot-starter ${project.version} + + com.introproventures + graphql-jpa-query-boot-starter-graphql + ${project.version} + com.introproventures graphql-jpa-query-autoconfigure diff --git a/graphql-jpa-query-example-merge/pom.xml b/graphql-jpa-query-example-merge/pom.xml index b42777e0f..e9ef835c7 100644 --- a/graphql-jpa-query-example-merge/pom.xml +++ b/graphql-jpa-query-example-merge/pom.xml @@ -93,7 +93,7 @@ push - true + true diff --git a/graphql-jpa-query-example-merge/src/main/resources/application.yml b/graphql-jpa-query-example-merge/src/main/resources/application.yml index 0bcd7837e..86056e766 100644 --- a/graphql-jpa-query-example-merge/src/main/resources/application.yml +++ b/graphql-jpa-query-example-merge/src/main/resources/application.yml @@ -11,7 +11,8 @@ spring: description: Combined GraphQL Jpa Query for Starwars and Books Example use-distinct-parameter: true enabled: true - path: /graphql + web: + path: /graphql starwars: hikari: diff --git a/graphql-jpa-query-example-spring-graphql/pom.xml b/graphql-jpa-query-example-spring-graphql/pom.xml new file mode 100644 index 000000000..59ef09d1d --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/pom.xml @@ -0,0 +1,138 @@ + + 4.0.0 + + graphql-jpa-query-example-spring-graphql + graphql-jpa-query-example-spring-graphql + + + com.introproventures + graphql-jpa-query-build + 0.5.2-SNAPSHOT + ../graphql-jpa-query-build + + + + true + false + + + + + + com.introproventures + graphql-jpa-query-boot-starter-graphql + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-websocket + + + + org.springframework.boot + spring-boot-starter-graphql + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.hibernate.validator + hibernate-validator + + + + com.introproventures + graphql-jpa-query-example-model-starwars + + + + com.h2database + h2 + + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + 1.5.6.RELEASE + + + + repackage + + + + + + com.spotify + docker-maven-plugin + 0.4.14 + + + javax.activation + activation + 1.1.1 + + + + + build-image + package + + build + + + + push-image + deploy + + push + + + true + + + + + docker-hub + https://index.docker.io/v1/ + introproventures/${project.artifactId} + ${project.basedir}/src/main/docker + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + + ${skipDocker} + + + true + + ${project.version} + latest + + + + + + + + + \ No newline at end of file diff --git a/graphql-jpa-query-example-spring-graphql/src/main/docker/Dockerfile b/graphql-jpa-query-example-spring-graphql/src/main/docker/Dockerfile new file mode 100644 index 000000000..106359a5f --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/main/docker/Dockerfile @@ -0,0 +1,12 @@ +FROM openjdk:8-jdk-alpine + +VOLUME /var/log/ +VOLUME /tmp + +EXPOSE 8080 + +ADD graphql-jpa-query-example-spring-graphql.jar app.jar + +ENV JAVA_OPTS="" + +ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar diff --git a/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/Application.java b/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/Application.java new file mode 100644 index 000000000..a5fccff3a --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/Application.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017 IntroPro Ventures, Inc. 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 com.introproventures.graphql.jpa.query.example; + +import com.introproventures.graphql.jpa.query.schema.model.starwars.Character; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Configuration; + +/** + * GraphQL JPA Query Example with Spring Boot Autoconfiguration + * + * You can configure GraphQL JPA Query properties in application.yml + * + * @author Igor Dianov + * + */ +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Configuration + @EntityScan(basePackageClasses=Character.class) + static class StarwarsJpaModelConfiguration { + } +} diff --git a/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/controllers/MutationController.java b/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/controllers/MutationController.java new file mode 100644 index 000000000..c333024ed --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/controllers/MutationController.java @@ -0,0 +1,14 @@ +package com.introproventures.graphql.jpa.query.example.controllers; + +import org.springframework.graphql.data.method.annotation.Argument; +import org.springframework.graphql.data.method.annotation.MutationMapping; +import org.springframework.stereotype.Controller; + +@Controller +public class MutationController { + + @MutationMapping + String echo(@Argument String name) { + return name; + } +} diff --git a/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/controllers/QueryController.java b/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/controllers/QueryController.java new file mode 100644 index 000000000..79efae83d --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/controllers/QueryController.java @@ -0,0 +1,28 @@ +package com.introproventures.graphql.jpa.query.example.controllers; + +import org.springframework.graphql.data.method.annotation.Argument; +import org.springframework.graphql.data.method.annotation.QueryMapping; +import org.springframework.stereotype.Controller; + +import java.util.Date; +import java.util.Random; + +@Controller +public class QueryController { + + @QueryMapping + String hello(@Argument String name) { + return "Greetings, " + name + "!"; + } + + @QueryMapping + Long random() { + return new Random().nextLong(); + } + + @QueryMapping + Date now() { + return new Date(); + } + +} diff --git a/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/controllers/SubscriptionController.java b/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/controllers/SubscriptionController.java new file mode 100644 index 000000000..1033a0d1b --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/main/java/com/introproventures/graphql/jpa/query/example/controllers/SubscriptionController.java @@ -0,0 +1,21 @@ +package com.introproventures.graphql.jpa.query.example.controllers; + +import org.springframework.graphql.data.method.annotation.Argument; +import org.springframework.graphql.data.method.annotation.SubscriptionMapping; +import org.springframework.stereotype.Controller; +import reactor.core.publisher.Flux; + +import java.time.Duration; +import java.time.Instant; +import java.util.stream.Stream; + +@Controller +public class SubscriptionController { + + @SubscriptionMapping + Flux greetings(@Argument String name) { + return Flux.fromStream(Stream.generate(() -> "Hello, " + name + "@ " + Instant.now())) + .delayElements(Duration.ofSeconds(1)) + .take(10); + } +} diff --git a/graphql-jpa-query-example-spring-graphql/src/main/resources/application.yml b/graphql-jpa-query-example-spring-graphql/src/main/resources/application.yml new file mode 100644 index 000000000..66c2eece8 --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/main/resources/application.yml @@ -0,0 +1,23 @@ +spring: + jpa: + hibernate.ddl-auto: create-drop + show-sql: true + open-in-view: false + defer-datasource-initialization: true + h2: + console.enabled: true + datasource: + url: jdbc:h2:mem:starwars + graphql: + schema: + printer: + enabled: true + graphiql: + enabled: true + websocket: + path: /graphql/ws + jpa: + query: + name: Query + description: GraphQL Jpa Query Starwars Schema Example + enabled: true diff --git a/graphql-jpa-query-example-spring-graphql/src/main/resources/data.sql b/graphql-jpa-query-example-spring-graphql/src/main/resources/data.sql new file mode 100644 index 000000000..3d80c6295 --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/main/resources/data.sql @@ -0,0 +1,109 @@ +-- Insert Code Lists +insert into code_list (id, type, code, description, sequence, active, parent_id) values + (0, 'org.crygier.graphql.model.starwars.Gender', 'Male', 'Male', 1, true, null), + (1, 'org.crygier.graphql.model.starwars.Gender', 'Female', 'Female', 2, true, null); + +-- Insert Droid Functions +insert into droid_function(id, function) values +( '1000', 'Protocol'), +( '1001', 'Astromech'); + +-- Insert Droids +insert into character (id, name, primary_function, dtype) values + ('2000', 'C-3PO', '1000', 'Droid'), + ('2001', 'R2-D2', '1001', 'Droid'); + +-- Insert Humans +insert into character (id, name, home_planet, favorite_droid_id, dtype, gender_code_id) values + ('1000', 'Luke Skywalker', 'Tatooine', '2000', 'Human', 0), + ('1001', 'Darth Vader', 'Tatooine', '2001', 'Human', 0), + ('1002', 'Han Solo', NULL, NULL, 'Human', 0), + ('1003', 'Leia Organa', 'Alderaan', NULL, 'Human', 1), + ('1004', 'Wilhuff Tarkin', NULL, NULL, 'Human', 0); + +-- Luke's friends +insert into character_friends (source_id, friend_id) values + ('1000', '1002'), + ('1000', '1003'), + ('1000', '2000'), + ('1000', '2001'); + +-- Luke Appears in +insert into character_appears_in (character_id, appears_in) values + ('1000', 3), + ('1000', 4), + ('1000', 5), + ('1000', 6); + +-- Vader's friends +insert into character_friends (source_id, friend_id) values + ('1001', '1004'); + +-- Vader Appears in +insert into character_appears_in (character_id, appears_in) values + ('1001', 3), + ('1001', 4), + ('1001', 5); + +-- Solo's friends +insert into character_friends (source_id, friend_id) values + ('1002', '1000'), + ('1002', '1003'), + ('1002', '2001'); + +-- Solo Appears in +insert into character_appears_in (character_id, appears_in) values + ('1002', 3), + ('1002', 4), + ('1002', 5), + ('1002', 6); + +-- Leia's friends +insert into character_friends (source_id, friend_id) values + ('1003', '1000'), + ('1003', '1002'), + ('1003', '2000'), + ('1003', '2001'); + +-- Leia Appears in +insert into character_appears_in (character_id, appears_in) values + ('1003', 3), + ('1003', 4), + ('1003', 5), + ('1003', 6); + +-- Wilhuff's friends +insert into character_friends (source_id, friend_id) values + ('1004', '1001'); + +-- Wilhuff Appears in +insert into character_appears_in (character_id, appears_in) values + ('1004', 3); + +-- C3PO's friends +insert into character_friends (source_id, friend_id) values + ('2000', '1000'), + ('2000', '1002'), + ('2000', '1003'), + ('2000', '2001'); + +-- C3PO Appears in +insert into character_appears_in (character_id, appears_in) values + ('2000', 3), + ('2000', 4), + ('2000', 5), + ('2000', 6); + +-- R2's friends +insert into character_friends (source_id, friend_id) values + ('2001', '1000'), + ('2001', '1002'), + ('2001', '1003'); + +-- R2 Appears in +insert into character_appears_in (character_id, appears_in) values + ('2001', 3), + ('2001', 4), + ('2001', 5), + ('2001', 6); + diff --git a/graphql-jpa-query-example-spring-graphql/src/main/resources/graphql/schema.graphqls b/graphql-jpa-query-example-spring-graphql/src/main/resources/graphql/schema.graphqls new file mode 100644 index 000000000..874d2fafe --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/main/resources/graphql/schema.graphqls @@ -0,0 +1,17 @@ +scalar Long +scalar Date + +type Query { + hello(name: String!): String + random: Long + now: Date +} + +type Mutation { + echo(name: String): String +} + +type Subscription { + greetings(name: String): String +} + diff --git a/graphql-jpa-query-example-spring-graphql/src/main/resources/hibernate.properties b/graphql-jpa-query-example-spring-graphql/src/main/resources/hibernate.properties new file mode 100644 index 000000000..34f5b4c38 --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/main/resources/hibernate.properties @@ -0,0 +1,8 @@ +hibernate.generate_statistics=true +org.hibernate.stat=DEBUG +hibernate.show_sql=true +hibernate.format_sql=true + +logging.level.org.hibernate=debug +#logging.level.org.hibernate.type.descriptor.sql=trace +#logging.level.org.hibernate.SQL=debug diff --git a/graphql-jpa-query-example-spring-graphql/src/test/java/com/introproventures/graphql/jpa/query/example/ApplicationTest.java b/graphql-jpa-query-example-spring-graphql/src/test/java/com/introproventures/graphql/jpa/query/example/ApplicationTest.java new file mode 100644 index 000000000..5d7e4140c --- /dev/null +++ b/graphql-jpa-query-example-spring-graphql/src/test/java/com/introproventures/graphql/jpa/query/example/ApplicationTest.java @@ -0,0 +1,18 @@ +package com.introproventures.graphql.jpa.query.example; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT) +public class ApplicationTest { + + @Test + public void contextLoads() { + // success + } + +} diff --git a/graphql-jpa-query-scalars/LICENSE b/graphql-jpa-query-scalars/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/graphql-jpa-query-scalars/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/graphql-jpa-query-scalars/pom.xml b/graphql-jpa-query-scalars/pom.xml new file mode 100644 index 000000000..9bbe105bf --- /dev/null +++ b/graphql-jpa-query-scalars/pom.xml @@ -0,0 +1,34 @@ + + graphql-jpa-query-scalars + graphql-jpa-query-scalars + + + com.introproventures + graphql-jpa-query-build + 0.5.2-SNAPSHOT + ../graphql-jpa-query-build + + + 4.0.0 + + + + + com.graphql-java + graphql-java + + + + com.graphql-java + graphql-java-extended-scalars + + + + joda-time + joda-time + true + + + + + \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalars.java b/graphql-jpa-query-scalars/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalars.java similarity index 98% rename from graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalars.java rename to graphql-jpa-query-scalars/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalars.java index 3726e364f..e5dbbd950 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalars.java +++ b/graphql-jpa-query-scalars/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalars.java @@ -54,6 +54,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -61,6 +62,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; @@ -79,7 +81,7 @@ public class JavaScalars { static final Logger log = LoggerFactory.getLogger(JavaScalars.class); - private static HashMap, GraphQLScalarType> scalarsRegistry = new HashMap, GraphQLScalarType>(); + private static Map, GraphQLScalarType> scalarsRegistry = new HashMap, GraphQLScalarType>(); private static JavaScalars instance = new JavaScalars(); @@ -128,6 +130,17 @@ public class JavaScalars { scalarsRegistry.put(OffsetDateTime.class, newScalarType("OffsetDateTime", "OffsetDateTime type", new GraphQLOffsetDateTimeCoercing())); } + public static Optional of(String name) { + return scalarsRegistry.values() + .stream() + .filter(scalar -> name.equals(scalar.getName())) + .findFirst(); + } + + public static Collection scalars() { + return Collections.unmodifiableCollection(scalarsRegistry.values()); + } + public static GraphQLScalarType of(Class key) { return scalarsRegistry.computeIfAbsent(key, JavaScalars::computeGraphQLScalarType); } diff --git a/graphql-jpa-query-scalars/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalarsWiringPostProcessor.java b/graphql-jpa-query-scalars/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalarsWiringPostProcessor.java new file mode 100644 index 000000000..28b696fc5 --- /dev/null +++ b/graphql-jpa-query-scalars/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalarsWiringPostProcessor.java @@ -0,0 +1,73 @@ +package com.introproventures.graphql.jpa.query.schema; + +import graphql.language.NamedNode; +import graphql.schema.GraphQLNamedType; +import graphql.schema.GraphQLScalarType; +import graphql.schema.GraphQLSchema; +import graphql.schema.GraphQLSchemaElement; +import graphql.schema.GraphQLTypeVisitorStub; +import graphql.schema.SchemaTransformer; +import graphql.schema.idl.SchemaGeneratorPostProcessing; +import graphql.util.TraversalControl; +import graphql.util.TraverserContext; +import graphql.util.TreeTransformerUtil; + +import java.util.function.Function; + +import static graphql.util.TraversalControl.CONTINUE; + +public class JavaScalarsWiringPostProcessor implements SchemaGeneratorPostProcessing { + + public JavaScalarsWiringPostProcessor() { + } + + @Override + public GraphQLSchema process(GraphQLSchema originalSchema) { + Visitor visitor = new Visitor(); + + return SchemaTransformer.transformSchema(originalSchema, + visitor); + } + + class Visitor extends GraphQLTypeVisitorStub { + + private boolean schemaChanged = false; + + public boolean schemaChanged() { + return schemaChanged; + } + + private TraversalControl changOrContinue(GraphQLSchemaElement node, GraphQLSchemaElement newNode, TraverserContext context) { + if (node != newNode) { + TreeTransformerUtil.changeNode(context, newNode); + schemaChanged = true; + } + return CONTINUE; + } + + private boolean isIntrospectionType(GraphQLNamedType type) { + return type.getName().startsWith("__"); + } + + private boolean notSuitable(T node, Function> suitableFunc) { + if (isIntrospectionType(node)) { + return true; + } + NamedNode definition = suitableFunc.apply(node); + return definition == null; + } + + @Override + public TraversalControl visitGraphQLScalarType(GraphQLScalarType node, TraverserContext context) { + if (notSuitable(node, GraphQLScalarType::getDefinition)) { + return CONTINUE; + } + + GraphQLScalarType newNode = JavaScalars.of(node.getName()) + .orElse(node); + + return changOrContinue(node, newNode, context); + } + + } +} diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/JavaScalarsTest.java b/graphql-jpa-query-scalars/src/test/java/com/introproventures/graphql/jpa/query/schema/JavaScalarsTest.java similarity index 99% rename from graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/JavaScalarsTest.java rename to graphql-jpa-query-scalars/src/test/java/com/introproventures/graphql/jpa/query/schema/JavaScalarsTest.java index d6fdd29da..4183cbecb 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/JavaScalarsTest.java +++ b/graphql-jpa-query-scalars/src/test/java/com/introproventures/graphql/jpa/query/schema/JavaScalarsTest.java @@ -16,8 +16,19 @@ package com.introproventures.graphql.jpa.query.schema; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; +import com.introproventures.graphql.jpa.query.schema.JavaScalars.GraphQLObjectCoercing; +import com.introproventures.graphql.jpa.query.schema.fixtures.VariableValue; +import graphql.language.BooleanValue; +import graphql.language.IntValue; +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 org.assertj.core.api.Assertions; +import org.assertj.core.api.InstanceOfAssertFactory; +import org.junit.Test; import java.math.BigInteger; import java.sql.Timestamp; @@ -38,21 +49,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import org.assertj.core.api.Assertions; -import org.assertj.core.api.InstanceOfAssertFactory; -import org.junit.Test; - -import com.introproventures.graphql.jpa.query.converter.model.VariableValue; -import com.introproventures.graphql.jpa.query.schema.JavaScalars.GraphQLObjectCoercing; - -import graphql.language.BooleanValue; -import graphql.language.IntValue; -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 org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class JavaScalarsTest { @@ -504,5 +502,7 @@ public void dateCoercionThreadSafe() throws InterruptedException, ExecutionExcep //then assertThat(result.isCompletedExceptionally()).isFalse(); } + + } diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/ObjectCoercingTests.java b/graphql-jpa-query-scalars/src/test/java/com/introproventures/graphql/jpa/query/schema/ObjectCoercingTests.java similarity index 100% rename from graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/ObjectCoercingTests.java rename to graphql-jpa-query-scalars/src/test/java/com/introproventures/graphql/jpa/query/schema/ObjectCoercingTests.java diff --git a/graphql-jpa-query-scalars/src/test/java/com/introproventures/graphql/jpa/query/schema/fixtures/VariableValue.java b/graphql-jpa-query-scalars/src/test/java/com/introproventures/graphql/jpa/query/schema/fixtures/VariableValue.java new file mode 100644 index 000000000..9f9ed7a68 --- /dev/null +++ b/graphql-jpa-query-scalars/src/test/java/com/introproventures/graphql/jpa/query/schema/fixtures/VariableValue.java @@ -0,0 +1,58 @@ +package com.introproventures.graphql.jpa.query.schema.fixtures; + + +public class VariableValue { + + private T value; + + public VariableValue() { + } + + public VariableValue(T value) { + this.value = value; + } + + public T getValue() { + return value; + } + + + /** + * Encountered Java type [class org.activiti.cloud.services.query.model.VariableValue] for which we could not locate a JavaTypeDescriptor + * and which does not appear to implement equals and/or hashCode. This can lead to significant performance problems when performing + * equality/dirty checking involving this Java type. + * + * Consider registering a custom JavaTypeDescriptor or at least implementing equals/hashCode. + * + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VariableValue other = (VariableValue) obj; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + @Override + public String toString() { + return "VariableValue [value=" + value + "]"; + } + +} \ No newline at end of file diff --git a/graphql-jpa-query-scalars/src/test/resources/EntityWithEmbeddedIdTest.sql b/graphql-jpa-query-scalars/src/test/resources/EntityWithEmbeddedIdTest.sql new file mode 100644 index 000000000..555bab4f0 --- /dev/null +++ b/graphql-jpa-query-scalars/src/test/resources/EntityWithEmbeddedIdTest.sql @@ -0,0 +1,3 @@ +insert into Book(title, language, description) values +('War and Piece', 'Russian', 'War and Piece Novel'), +('Witch Of Water', 'English', 'Witch Of Water Fantasy'); \ No newline at end of file diff --git a/graphql-jpa-query-scalars/src/test/resources/EntityWithIdClassTest.sql b/graphql-jpa-query-scalars/src/test/resources/EntityWithIdClassTest.sql new file mode 100644 index 000000000..69a324e98 --- /dev/null +++ b/graphql-jpa-query-scalars/src/test/resources/EntityWithIdClassTest.sql @@ -0,0 +1,5 @@ +-- Json entity + +insert into ACCOUNT(account_number, account_type, description) values +('1', 'Savings', 'Saving account record'), +('2', 'Checking', 'Checking account record'); \ No newline at end of file diff --git a/graphql-jpa-query-scalars/src/test/resources/GraphQLJpaConverterTests.sql b/graphql-jpa-query-scalars/src/test/resources/GraphQLJpaConverterTests.sql new file mode 100644 index 000000000..2d4743661 --- /dev/null +++ b/graphql-jpa-query-scalars/src/test/resources/GraphQLJpaConverterTests.sql @@ -0,0 +1,24 @@ +-- Json entity +insert into json_entity (id, first_name, last_name, attributes) values + (1, 'john', 'doe', '{"attr":{"key":["1","2","3","4","5"]}}'), + (2, 'joe', 'smith', '{"attr":["1","2","3","4","5"]}'); + +insert into task (id, assignee, business_key, created_date, description, due_date, last_modified, last_modified_from, last_modified_to, name, priority, process_definition_id, process_instance_id, status, owner, claimed_date) values +('1', 'assignee', 'bk1', CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task1', 5, 'process_definition_id', 0, 'COMPLETED' , 'owner', null), +('2', 'assignee', null, CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task2', 10, 'process_definition_id', 0, 'CREATED' , 'owner', null), +('3', 'assignee', null, CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task3', 5, 'process_definition_id', 0, 'CREATED' , 'owner', null), +('4', 'assignee', null, CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task4', 10, 'process_definition_id', 1, 'CREATED' , 'owner', null), +('5', 'assignee', null, CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task5', 10, 'process_definition_id', 1, 'COMPLETED' , 'owner', null), +('6', 'assignee', 'bk6', CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task6', 10, 'process_definition_id', 0, 'ASSIGNED' , 'owner', null); + +insert into PROCESS_VARIABLE (create_time, execution_id, last_updated_time, name, process_instance_id, type, value) values + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'document', 1, 'json', '{"value":{"key":["1","2","3","4","5"]}}'); + +insert into TASK_VARIABLE (create_time, execution_id, last_updated_time, name, process_instance_id, task_id, type, value) values + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable1', 0, '1', 'string', '{"value":"data"}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable2', 0, '1', 'boolean', '{"value":true}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable3', 0, '2', 'null', '{"value":null}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable4', 0, '2', 'json', '{"value":{"key":"data"}}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable5', 1, '4', 'double', '{"value":1.2345}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable6', 1, '4', 'int', '{"value":12345}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable7', 1, '4', 'json', '{"value":[1,2,3,4,5]}'); \ No newline at end of file diff --git a/graphql-jpa-query-scalars/src/test/resources/LocalDatetTmeData.sql b/graphql-jpa-query-scalars/src/test/resources/LocalDatetTmeData.sql new file mode 100644 index 000000000..a33f3c1f9 --- /dev/null +++ b/graphql-jpa-query-scalars/src/test/resources/LocalDatetTmeData.sql @@ -0,0 +1,9 @@ +insert into LOCAL_DATE(id, LOCALDATETIME, OFFSETDATETIME, ZONEDDATETIME, INSTANT, LOCALDATE, TIMESTAMP, description) values +('1', '2019-08-01 10:58:08', '2019-08-01 10:58:07+07:00','2019-08-01 10:58:08+07:00[Asia/Bangkok]', '2019-08-01 03:58:08Z', '2019-08-01', '2019-08-01 03:58:08Z', 'Add test for LocalDate.'), +('2', '2019-08-02 10:58:08', '2019-08-02 10:58:07+07:00','2019-08-02 10:58:08+07:00[Asia/Bangkok]', '2019-08-02 03:58:08Z', '2019-08-02', '2019-08-02 03:58:08Z', 'Add test for LocalDate.'), +('3', '2019-08-03 10:58:08', '2019-08-03 10:58:07+07:00','2019-08-03 10:58:08+07:00[Asia/Bangkok]', '2019-08-03 03:58:08Z', '2019-08-03', '2019-08-03 03:58:08Z', 'Add test for LocalDate.'), +('4', '2019-08-04 10:58:08', '2019-08-04 10:58:07+07:00','2019-08-04 10:58:08+07:00[Asia/Bangkok]', '2019-08-04 03:58:08Z', '2019-08-04', '2019-08-04 03:58:08Z', 'Add test for LocalDate.'), +('5', '2019-08-05 10:58:08', '2019-08-05 10:58:07+07:00','2019-08-05 10:58:08+07:00[Asia/Bangkok]', '2019-08-05 03:58:08Z', '2019-08-05', '2019-08-05 03:58:08Z', 'Add test for LocalDate.'), +('6', '2019-08-06 10:58:08', '2019-08-06 10:58:07+07:00','2019-08-06 10:58:08+07:00[Asia/Bangkok]', '2019-08-06 03:58:08Z', '2019-08-06', '2019-08-06 03:58:08Z', 'Add test for LocalDate.'), +('7', '2019-08-07 10:58:08', '2019-08-07 10:58:07+07:00','2019-08-07 10:58:08+07:00[Asia/Bangkok]', '2019-08-07 03:58:08Z', '2019-08-07', '2019-08-07 03:58:08Z', 'Add test for LocalDate.'), +('8', '2019-08-08 10:58:08', '2019-08-08 10:58:07+07:00','2019-08-08 10:58:08+07:00[Asia/Bangkok]', '2019-08-08 03:58:08Z', '2019-08-08', '2019-08-08 03:58:08Z', 'Add test for LocalDate.'); \ No newline at end of file diff --git a/graphql-jpa-query-scalars/src/test/resources/RestrictedKeysProviderTests.sql b/graphql-jpa-query-scalars/src/test/resources/RestrictedKeysProviderTests.sql new file mode 100644 index 000000000..e6274a9b5 --- /dev/null +++ b/graphql-jpa-query-scalars/src/test/resources/RestrictedKeysProviderTests.sql @@ -0,0 +1,6 @@ +-- Things +insert into thing (id, type) values + ('2D1EBC5B7D2741979CF0E84451C5BBB1', 'Thing1'), + ('2D1EBC5B7D2741979CF0E84451C5BBC1', 'Thing2'), + ('2D1EBC5B7D2741979CF0E84451C5BBD1', 'Thing3'); + diff --git a/graphql-jpa-query-scalars/src/test/resources/application.properties b/graphql-jpa-query-scalars/src/test/resources/application.properties new file mode 100644 index 000000000..0efdb8928 --- /dev/null +++ b/graphql-jpa-query-scalars/src/test/resources/application.properties @@ -0,0 +1,4 @@ +logging.level.com.introproventures.graphql.jpa.query.schema=DEBUG + +# This modifies the default Spring Boot behavior and populates the data after the schema is generated by Hibernate +spring.jpa.defer-datasource-initialization=true \ No newline at end of file diff --git a/graphql-jpa-query-scalars/src/test/resources/data.sql b/graphql-jpa-query-scalars/src/test/resources/data.sql new file mode 100644 index 000000000..57e0426e1 --- /dev/null +++ b/graphql-jpa-query-scalars/src/test/resources/data.sql @@ -0,0 +1,166 @@ +-- Insert Code Lists +insert into code_list (id, type, code, description, sequence, active, parent_id) values + (0, 'org.crygier.graphql.model.starwars.Gender', 'Male', 'Male', 1, true, null), + (1, 'org.crygier.graphql.model.starwars.Gender', 'Female', 'Female', 2, true, null); + +-- Insert Droid Functions +insert into droid_function(id, function) values +( '1000', 'Protocol'), +( '1001', 'Astromech'); + +-- Insert Droids +insert into character (id, name, primary_function, dtype) values + ('2000', 'C-3PO', '1000', 'Droid'), + ('2001', 'R2-D2', '1001', 'Droid'); + +-- Insert Humans +insert into character (id, name, home_planet, favorite_droid_id, dtype, gender_code_id) values + ('1000', 'Luke Skywalker', 'Tatooine', '2000', 'Human', 0), + ('1001', 'Darth Vader', 'Tatooine', '2001', 'Human', 0), + ('1002', 'Han Solo', NULL, NULL, 'Human', 0), + ('1003', 'Leia Organa', 'Alderaan', NULL, 'Human', 1), + ('1004', 'Wilhuff Tarkin', NULL, NULL, 'Human', 0); + +-- Luke's friends +insert into character_friends (source_id, friend_id) values + ('1000', '1002'), + ('1000', '1003'), + ('1000', '2000'), + ('1000', '2001'); + +-- Luke Appears in +insert into character_appears_in (character_id, appears_in) values + ('1000', 3), + ('1000', 4), + ('1000', 5), + ('1000', 6); + +-- Vader's friends +insert into character_friends (source_id, friend_id) values + ('1001', '1004'); + +-- Vader Appears in +insert into character_appears_in (character_id, appears_in) values + ('1001', 3), + ('1001', 4), + ('1001', 5); + +-- Solo's friends +insert into character_friends (source_id, friend_id) values + ('1002', '1000'), + ('1002', '1003'), + ('1002', '2001'); + +-- Solo Appears in +insert into character_appears_in (character_id, appears_in) values + ('1002', 3), + ('1002', 4), + ('1002', 5), + ('1002', 6); + +-- Leia's friends +insert into character_friends (source_id, friend_id) values + ('1003', '1000'), + ('1003', '1002'), + ('1003', '2000'), + ('1003', '2001'); + +-- Leia Appears in +insert into character_appears_in (character_id, appears_in) values + ('1003', 3), + ('1003', 4), + ('1003', 5), + ('1003', 6); + +-- Wilhuff's friends +insert into character_friends (source_id, friend_id) values + ('1004', '1001'); + +-- Wilhuff Appears in +insert into character_appears_in (character_id, appears_in) values + ('1004', 3); + +-- C3PO's friends +insert into character_friends (source_id, friend_id) values + ('2000', '1000'), + ('2000', '1002'), + ('2000', '1003'), + ('2000', '2001'); + +-- C3PO Appears in +insert into character_appears_in (character_id, appears_in) values + ('2000', 3), + ('2000', 4), + ('2000', 5), + ('2000', 6); + +-- R2's friends +insert into character_friends (source_id, friend_id) values + ('2001', '1000'), + ('2001', '1002'), + ('2001', '1003'); + +-- R2 Appears in +insert into character_appears_in (character_id, appears_in) values + ('2001', 3), + ('2001', 4), + ('2001', 5), + ('2001', 6); + +-- Things +insert into thing (id, type) values + ('2D1EBC5B7D2741979CF0E84451C5BBB1', 'Thing1'); + +-- Books +insert into author (id, name, genre) values (1, 'Leo Tolstoy', 'NOVEL'); +insert into book (id, title, author_id, genre, publication_date, description) +values (2, 'War and Peace', 1, 'NOVEL', '1869-01-01', 'The novel chronicles the history of the French invasion of Russia and the impact of the Napoleonic era on Tsarist society through the stories of five Russian aristocratic families.'); +insert into book (id, title, author_id, genre, publication_date, description) +values (3, 'Anna Karenina', 1, 'NOVEL', '1877-04-01', 'A complex novel in eight parts, with more than a dozen major characters, it is spread over more than 800 pages (depending on the translation), typically contained in two volumes.'); +insert into author (id, name, genre) values (4, 'Anton Chekhov', 'PLAY'); +insert into book (id, title, author_id, genre, publication_date, description) +values (5, 'The Cherry Orchard', 4, 'PLAY', '1904-01-17', 'The play concerns an aristocratic Russian landowner who returns to her family estate (which includes a large and well-known cherry orchard) just before it is auctioned to pay the mortgage.'); +insert into book (id, title, author_id, genre, publication_date, description) +values (6, 'The Seagull', 4, 'PLAY', '1896-10-17', 'It dramatises the romantic and artistic conflicts between four characters'); +insert into book (id, title, author_id, genre, publication_date, description) +values (7, 'Three Sisters', 4, 'PLAY', '1900-01-01', 'The play is sometimes included on the short list of Chekhov''s outstanding plays, along with The Cherry Orchard, The Seagull and Uncle Vanya.[1]'); +insert into author (id, name, genre) values (8, 'Igor Dianov', 'JAVA'); + +insert into book_tags (book_id, tags) values (2, 'war'), (2, 'piece'); +insert into book_tags (book_id, tags) values (3, 'anna'), (3, 'karenina'); +insert into book_tags (book_id, tags) values (5, 'cherry'), (5, 'orchard'); +insert into book_tags (book_id, tags) values (6, 'seagull'); +insert into book_tags (book_id, tags) values (7, 'three'), (7, 'sisters'); + +insert into author_phone_numbers(phone_number, author_id) values + ('1-123-1234', 1), + ('1-123-5678', 1), + ('4-123-1234', 4), + ('4-123-5678', 4); + +insert into book_publishers(book_id, name, country) values + (3, 'Independent', 'UK'), (3, 'Amazon', 'US'), + (2, 'Willey', 'US'), (2, 'Simon', 'EU'); + +-- Car +insert into Car (id, brand, identification, hp) values + (1, 'Ford', 'xxxxx', 160), + (2, 'Cadillac', 'yyyyy', 250), + (3, 'Toyota', 'zzzzz', 180); + + +-- Boat +insert into Boat (id, country, identification) values + (1, 'EN', '12345'), + (2, 'EN', '23456'), + (1, 'FR', '34567'); + +-- Calculate entity +insert into calculated_entity (id, title, info) values + (1, 'title 1', 'inf 1'), + (2, 'title 2', 'inf 2'); + +-- FloatingThing +insert into floating_thing (id, float_value, double_value, big_decimal_value) values + (1, 4.55, 4.55, 4.55), + (2, -0.44, -0.44, -0.44) \ No newline at end of file diff --git a/graphql-jpa-query-scalars/src/test/resources/hibernate.properties b/graphql-jpa-query-scalars/src/test/resources/hibernate.properties new file mode 100644 index 000000000..31ad7379b --- /dev/null +++ b/graphql-jpa-query-scalars/src/test/resources/hibernate.properties @@ -0,0 +1,13 @@ +hibernate.generate_statistics=false +org.hibernate.stat=DEBUG + +spring.jpa.properties.hibernate.show_sql=false +spring.jpa.properties.hibernate.format_sql=true +# Fail fast to get instant feedback of any in-memory pagination queries +spring.jpa.properties.hibernate.query.fail_on_pagination_over_collection_fetch=true +# This should match TimeZone.setDefault(TimeZone.getTimeZone("UTC")); in JVM +spring.jpa.properties.hibernate.jdbc.time_zone=UTC + +logging.level.org.hibernate=info +#logging.level.org.hibernate.type.descriptor.sql=trace +#logging.level.org.hibernate.SQL=debug diff --git a/graphql-jpa-query-schema/pom.xml b/graphql-jpa-query-schema/pom.xml index eb1d4d191..5a02b3059 100644 --- a/graphql-jpa-query-schema/pom.xml +++ b/graphql-jpa-query-schema/pom.xml @@ -18,6 +18,11 @@ graphql-jpa-query-annotations + + com.introproventures + graphql-jpa-query-scalars + + com.introproventures graphql-jpa-query-introspection diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/BatchLoaderRegistry.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/BatchLoaderRegistry.java index 31cc31de2..23029f99c 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/BatchLoaderRegistry.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/BatchLoaderRegistry.java @@ -1,14 +1,15 @@ package com.introproventures.graphql.jpa.query.schema.impl; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - import org.dataloader.DataLoader; +import org.dataloader.DataLoaderFactory; import org.dataloader.DataLoaderOptions; import org.dataloader.DataLoaderRegistry; import org.dataloader.MappedBatchLoaderWithContext; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + public class BatchLoaderRegistry { private final static Map>> mappedToManyBatchLoaders = new LinkedHashMap<>(); private final static Map> mappedToOneBatchLoaders = new LinkedHashMap<>(); @@ -31,15 +32,17 @@ public static DataLoaderRegistry newDataLoaderRegistry(DataLoaderOptions dataLoa mappedToManyBatchLoaders.entrySet() .forEach(entry -> { - DataLoader> dataLoader = DataLoader.newMappedDataLoader(entry.getValue(), - dataLoaderOptions); + DataLoader> dataLoader = + DataLoaderFactory.newMappedDataLoader(entry.getValue(), + dataLoaderOptions); dataLoaderRegistry.register(entry.getKey(), dataLoader); }); mappedToOneBatchLoaders.entrySet() .forEach(entry -> { - DataLoader dataLoader = DataLoader.newMappedDataLoader(entry.getValue(), - dataLoaderOptions); + DataLoader dataLoader = + DataLoaderFactory.newMappedDataLoader(entry.getValue(), + dataLoaderOptions); dataLoaderRegistry.register(entry.getKey(), dataLoader); }); diff --git a/graphql-jpa-query-web/pom.xml b/graphql-jpa-query-web/pom.xml index 2e673ef0c..5e6ae271b 100644 --- a/graphql-jpa-query-web/pom.xml +++ b/graphql-jpa-query-web/pom.xml @@ -23,6 +23,11 @@ org.springframework.boot spring-boot-starter-validation + + org.springframework.boot + spring-boot-configuration-processor + true + org.springframework spring-tx diff --git a/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/GraphQLController.java b/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/GraphQLController.java index 56e9834e1..eab1d6016 100644 --- a/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/GraphQLController.java +++ b/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/GraphQLController.java @@ -15,17 +15,11 @@ */ package com.introproventures.graphql.jpa.query.web; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.UncheckedIOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; +import graphql.ExecutionResult; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -40,13 +34,14 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter.SseEventBuilder; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; - -import graphql.ExecutionResult; -import graphql.GraphQL; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UncheckedIOException; +import java.util.HashMap; +import java.util.Map; /** * Spring Boot GraphQL Query Rest Controller with HTTP mapping endpoints for GraphQLExecutor relay @@ -58,7 +53,7 @@ @Transactional public class GraphQLController { - private static final String PATH = "${spring.graphql.jpa.query.path:/graphql}"; + private static final String PATH = "${spring.graphql.jpa.query.web.path:/graphql}"; public static final String APPLICATION_GRAPHQL_VALUE = "application/graphql"; private final GraphQLExecutor graphQLExecutor; diff --git a/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/GraphQLControllerProperties.java b/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/GraphQLControllerProperties.java new file mode 100644 index 000000000..c4e33149d --- /dev/null +++ b/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/GraphQLControllerProperties.java @@ -0,0 +1,29 @@ +package com.introproventures.graphql.jpa.query.web; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import javax.validation.constraints.NotEmpty; + +@ConfigurationProperties(prefix = "spring.graphql.jpa.query.web") +public class GraphQLControllerProperties { + private boolean enabled; + + @NotEmpty + private String path; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } +} diff --git a/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/autoconfigure/GraphQLControllerAutoConfiguration.java b/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/autoconfigure/GraphQLControllerAutoConfiguration.java index 82ea2945d..4d4a5e9b6 100644 --- a/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/autoconfigure/GraphQLControllerAutoConfiguration.java +++ b/graphql-jpa-query-web/src/main/java/com/introproventures/graphql/jpa/query/web/autoconfigure/GraphQLControllerAutoConfiguration.java @@ -1,20 +1,22 @@ package com.introproventures.graphql.jpa.query.web.autoconfigure; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; +import com.introproventures.graphql.jpa.query.web.GraphQLController; +import com.introproventures.graphql.jpa.query.web.GraphQLControllerProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; -import com.introproventures.graphql.jpa.query.web.GraphQLController; - @Configuration @ConditionalOnWebApplication @ConditionalOnClass(GraphQLExecutor.class) @ConditionalOnProperty(prefix = "spring.graphql.jpa.query", name = {"enabled", "web.enabled"}, havingValue="true", matchIfMissing=true) +@EnableConfigurationProperties(GraphQLControllerProperties.class) public class GraphQLControllerAutoConfiguration { @Configuration diff --git a/graphql-jpa-query-web/src/test/java/com/introproventures/graphql/jpa/query/test/web/autoconfigure/GraphQLControllerAutoConfigurationPropertyDisabledTest.java b/graphql-jpa-query-web/src/test/java/com/introproventures/graphql/jpa/query/test/web/autoconfigure/GraphQLControllerAutoConfigurationPropertyDisabledTest.java index 6323b87e2..aafbc448d 100644 --- a/graphql-jpa-query-web/src/test/java/com/introproventures/graphql/jpa/query/test/web/autoconfigure/GraphQLControllerAutoConfigurationPropertyDisabledTest.java +++ b/graphql-jpa-query-web/src/test/java/com/introproventures/graphql/jpa/query/test/web/autoconfigure/GraphQLControllerAutoConfigurationPropertyDisabledTest.java @@ -1,7 +1,7 @@ package com.introproventures.graphql.jpa.query.test.web.autoconfigure; -import static org.assertj.core.api.Assertions.assertThat; - +import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; +import com.introproventures.graphql.jpa.query.web.GraphQLController; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -11,12 +11,11 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit4.SpringRunner; -import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; -import com.introproventures.graphql.jpa.query.web.GraphQLController; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT, - properties = "spring.graphql.jpa.query.enabled=false") + properties = "spring.graphql.jpa.query.web.enabled=false") public class GraphQLControllerAutoConfigurationPropertyDisabledTest { @MockBean diff --git a/pom.xml b/pom.xml index a645c0850..845cfc5d9 100644 --- a/pom.xml +++ b/pom.xml @@ -32,8 +32,10 @@ graphql-jpa-query-annotations + graphql-jpa-query-scalars graphql-jpa-query-schema graphql-jpa-query-boot-starter + graphql-jpa-query-boot-starter-graphql graphql-jpa-query-example-simple graphql-jpa-query-example-merge graphql-jpa-query-dependencies @@ -45,6 +47,7 @@ graphql-jpa-query-graphiql graphql-jpa-query-example-relay graphql-jpa-query-introspection + graphql-jpa-query-example-spring-graphql