diff --git a/.gitignore b/.gitignore
index 377246b2f..963d0847c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,10 @@ build
modules.xml
.idea/misc.xml
*.ipr
-
+bin/
+.classpath
+.project
+.settings/
### Maven ###
target/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 57e627d2a..929f2c3e8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -36,8 +36,7 @@ Please follow the steps below in order to make the changes:
./gradlew -p plugins/gradle/graphql-java-codegen-gradle-plugin clean build
# Build Maven plugin
- cd plugins/maven/graphql-java-codegen-maven-plugin
- mvn clean verify
+ mvn clean verify -f plugins/maven/graphql-java-codegen-maven-plugin/pom.xml
```
9. Make changes to the plugin code
@@ -48,8 +47,7 @@ Please follow the steps below in order to make the changes:
./gradlew -p plugins/gradle/graphql-java-codegen-gradle-plugin clean build publishToMavenLocal
# Install Maven plugin
- cd plugins/maven/graphql-java-codegen-maven-plugin
- mvn clean install
+ mvn clean install -f plugins/maven/graphql-java-codegen-maven-plugin/pom.xml
```
11. Make sure that `example` projects are compiling and running.
diff --git a/docs/codegen-options.md b/docs/codegen-options.md
index 0ea1dbe10..6801803a8 100644
--- a/docs/codegen-options.md
+++ b/docs/codegen-options.md
@@ -1,70 +1,71 @@
# Codegen Options
-| Option | Data Type | Default value | Description |
-|:-----------------------------------------------------:|:---------------------------------------------------------------------:|:--------------------------------------------------------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `graphqlSchemaPaths` | List(String) | (falls back to `graphqlSchemas`) | GraphQL schema locations. You can supply multiple paths to GraphQL schemas. To include many schemas from a folder hierarchy, use the `graphqlSchemas` block instead. |
-| `graphqlSchemas` | *See
[graphqlSchemas](#option-graphqlschemas)* | All
`.graphqls`/`.graphql`
files in
resources | Block to define the input GraphQL schemas, when exact paths are too cumbersome. See table below for a list of options. *See [graphqlSchemas](#option-graphqlschemas)* |
-| `graphqlQueryIntrospectionResu`
`ltPath` | String | None | Path to GraphQL Introspection Query result in json format (with root object `__schema` or `data.__schema`). Sample: [sample-introspection-query-result.json](../src/test/resources/introspection-result/sample-introspection-query-result.json) |
-| `outputDir` | String | None | The output target directory into which code will be generated. |
-| `configurationFiles` | List(String) | Empty | Paths to the files with mapping configurations. Supported formats. JSON, HOCON. Order of specified configuration files matters, so the default configuration should be placed at the end. |
-| `packageName` | String | Empty | Java package for generated classes. |
-| `apiPackageName` | String | Empty | Java package for generated api classes (Query, Mutation, Subscription). |
-| `modelPackageName` | String | Empty | Java package for generated model classes (type, input, interface, enum, union). |
-| `generateBuilder` | Boolean | True | Specifies whether generated model classes should have builder. |
-| `generateApis` | Boolean | True | Specifies whether api classes should be generated as well as model classes. |
-| `generateDataFetchingEnvironme`
`ntArgumentInApis` | Boolean | False | If true, then `graphql.schema.DataFetchingEnvironment env` will be added as a last argument to all methods of root type resolvers and field resolvers. |
-| `generateEqualsAndHashCode` | Boolean | False | Specifies whether generated model classes should have equals and hashCode methods defined. |
-| `generateParameterizedFieldsResolvers` | Boolean | True | Specifies whether separate `Resolver` interface for parametrized fields should be generated. If `false`, then add parametrized field to the type definition and ignore field parameters. If `true` then separate `Resolver` interface for parametrized fields will be generated. |
-| `generateImmutableModels` | Boolean | False | Specifies whether generated model classes should be immutable. |
-| `generateToString` | Boolean | False | Specifies whether generated model classes should have toString method defined. |
-| `generateJacksonTypeIdResolver` | Boolean | False | Specifies whether generated union interfaces should be annotated with a custom Jackson type id resolver generated in model package. |
-| `addGeneratedAnnotation` | Boolean | True | Specifies whether generated classes should have `@Generated` annotation. |
-| `generatedAnnotation` | String | `jakarta.annotation.Generated`
`javax.annotation.Generated` | Qualified class name (with package) of the `@Generated` annotation that will be added on top of every generated class (if `addGeneratedAnnotation` is **true**) |
-| `apiNamePrefix` | String | Empty | Sets the prefix for GraphQL api classes (query, mutation, subscription). |
-| `apiNameSuffix` | String | `Resolver` | Sets the suffix for GraphQL api classes (query, mutation, subscription). |
-| `apiInterfaceStrategy` | *See
[ApiInterfaceStrategy](#option-apiinterfacestrategy)* | `INTERFACE_PER_OPERATION` | *See [ApiInterfaceStrategy](#option-apiinterfacestrategy)* |
-| `apiRootInterfaceStrategy` | *See
[ApiRootInterfaceStrategy](#option-apirootinterfacestrategy)* | `SINGLE_INTERFACE` | *See [ApiRootInterfaceStrategy](#option-apirootinterfacestrategy)* |
-| `apiNamePrefixStrategy` | *See
[ApiNamePrefixStrategy](#option-apinameprefixstrategy)* | `CONSTANT` | *See [ApiNamePrefixStrategy](#option-apinameprefixstrategy)* |
-| `modelNamePrefix` | String | Empty | Sets the prefix for GraphQL model classes (type, input, interface, enum, union). |
-| `modelNameSuffix` | String | Empty | Sets the suffix for GraphQL model classes (type, input, interface, enum, union). |
-| `modelValidationAnnotation` | String | `@javax.validation.`
`constraints.NotNull` | Annotation for mandatory (NonNull) fields. Can be null/empty. |
-| `typeResolverPrefix` | String | Empty | Sets the prefix for GraphQL type resolver classes. |
-| `typeResolverSuffix` | String | `Resolver` | Sets the suffix for GraphQL type resolver classes. |
-| `customTypesMapping` | Map(String,String) | Empty | *See [CustomTypesMapping](#option-customtypesmapping)* |
-| `customAnnotationsMapping` | Map(String,String[]) | Empty | *See [CustomAnnotationsMapping](#option-customannotationsmapping)* |
-| `directiveAnnotationsMapping` | Map(String,String[]) | Empty | *See [DirectiveAnnotationsMapping](#option-directiveannotationsmapping)* |
-| `fieldsWithResolvers` | Set(String) | Empty | Fields that require Resolvers should be defined here in format: `TypeName.fieldName` or `TypeName` or `@directive`. E.g.: `Person`, `Person.friends`, `@customResolver`. |
-| `fieldsWithoutResolvers` | Set(String) | Empty | Fields that DO NOT require Resolvers should be defined here in format: `TypeName.fieldName` or `TypeName` or `@directive`. Can be used in conjunction with `generateExtensionFieldsResolvers` option. E.g.: `Person`, `Person.friends`, `@noResolver`. |
-| `resolverArgumentAnnotations` | Set(String) | Empty | Annotations that will be added to all resolver arguments. Can be used for [spring-graphql](https://github.com/spring-projects/spring-graphql) inegration by supplying: `org.springframework.graphql.data.method.annotation.Argument` |
-| `parametrizedResolverAnnotations` | Set(String) | Empty | Annotations that will be added to all parametrized resolver methods. Can be used for [spring-graphql](https://github.com/spring-projects/spring-graphql) inegration by supplying: `org.springframework.graphql.data.method.annotation.SchemaMapping(typeName="{{TYPE_NAME}}")` |
-| `generateParameterizedFieldsResolvers` | Boolean | True | If true, then generate separate `Resolver` interface for parametrized fields. If false, then add field to the type definition and ignore field parameters. |
-| `generateExtensionFieldsResolvers` | Boolean | False | Specifies whether all fields in extensions (`extend type` and `extend interface`) should be present in Resolver interface instead of the type class itself. |
-| `generateModelsForRootTypes` | Boolean | False | Specifies whether model classes should be generated for `type Query`, `type Subscription`, `type Mutation`. |
-| `useOptionalForNullableReturnTypes` | Boolean | False | Specifies whether nullable return types of api methods should be wrapped into [`java.util.Optional<>`](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Optional.html). Lists will not be wrapped. |
-| `generateApisWithThrowsException` | Boolean | True | Specifies whether api interface methods should have `throws Exception` in signature. |
-| `generateNoArgsConstructorOnly` | Boolean | False | Specifies whether model classes should only have a no-args constructor. All-args constructor will not be generated in case value is true |
-| `generateModelsWithPublicFields` | Boolean | False | Specifies whether model classes should have public fields and NO getters/setters. By default, fields are private and there are getters/setters for each field. |
-| `apiReturnType` | String | Empty | Return type for api methods (query/mutation). For example: `reactor.core.publisher.Mono`, etc. |
-| `apiReturnListType` | String | Empty | Return type for api methods (query/mutation) having list type. For example: `reactor.core.publisher.Flux`, etc. By default is empty, so `apiReturnType` will be used. |
-| `subscriptionReturnType` | String | Empty | Return type for subscription methods. For example: `org.reactivestreams.Publisher`, `io.reactivex.Observable`, etc. |
-| `relayConfig` | *See
[RelayConfig](#option-relayconfig)* | `@connection(for: ...)` | *See [RelayConfig](#option-relayconfig)* |
-| `generateClient` | Boolean | False | Specifies whether client-side classes should be generated for each query, mutation and subscription. This includes: `Request` classes (contain input data), `ResponseProjection` classes for each type (contain response fields) and `Response` classes (contain response data). |
-| `requestSuffix` | String | Request | Sets the suffix for `Request` classes. |
-| `responseSuffix` | String | Response | Sets the suffix for `Response` classes. |
-| `responseProjectionSuffix` | String | ResponseProjection | Sets the suffix for `ResponseProjection` classes. |
-| `parametrizedInputSuffix` | String | ParametrizedInput | Sets the suffix for `ParametrizedInput` classes. |
-| `parentInterfaces` | *See
[parentInterfaces](#option-parentinterfaces)* | Empty | Block to define parent interfaces for generated interfaces (query / mutation / subscription / type resolver). *See [parentInterfaces](#option-parentinterfaces)* |
-| `generateAllMethodInProjection` | Boolean | True | Enables whether the `all$()` method should be generated in the projection classes. Disabling enforces the client to select the fields manually. |
-| `responseProjectionMaxDepth` | Integer | 3 | Sets max depth when use `all$()` which for facilitating the construction of projection automatically, the fields on all projections are provided when it be invoked. This is a global configuration, of course, you can use `all$(max)` to set for each method. For self recursive types, too big depth may result in a large number of returned data! |
-| `generatedLanguage` | Enum | GeneratedLanguage.JAVA | Choose which language you want to generate, Java,Scala,Kotlin were supported. Note that due to language features, there are slight differences in default values between languages. |
-| `generateModelOpenClasses` | Boolean | False | The class type of the generated model. If true, generate normal classes, else generate data classes. It only support in kotlin(```data class```) and scala(```case class```). Maybe we will consider to support Java ```record``` in the future. |
-| `initializeNullableTypes` | Boolean | False | Adds a default null value to nullable arguments. Only supported in Kotlin. |
-| `generateSealedInterfaces` | Boolean | False | This applies to generated interfaces on unions and interfaces. If true, generate sealed interfaces, else generate normal ones. It is only supported in Kotlin and Scala. |
-| `typesAsInterfaces` | Set(String) | Empty | Types that must generated as interfaces should be defined here in format: `TypeName` or `@directive`. E.g.: `User`, `@asInterface`. |
-| `useObjectMapperForRequestSerialization` | Set(String) | Empty | Fields that require serialization using `com.fasterxml.jackson.databind.ObjectMapper#writeValueAsString(Object)`. Values should be defined here in the following format: `GraphqlObjectName.fieldName` or `GraphqlTypeName`. If just type is specified, then all fields of this type will be serialized using ObjectMapper. E.g.: `["Person.createdDateTime", ZonedDateTime"]` |
-| `supportUnknownFields` | Boolean | False | Specifies whether api classes should support unknown fields during serialization or deserialization. If `true`, classes will include a property of type [`java.util.Map`](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Map.html) that will store unknown fields. |
-| `unknownFieldsPropertyName` | String | userDefinedFields | Specifies the name of the property to be included in api classes to support unknown fields during serialization or deserialization |
-| `skip` | Boolean | False | If true, then code generation will not happen |
+| Option | Data Type | Default value | Description |
+|:-----------------------------------------------------:|:----------------------------------------------------------------------:|:--------------------------------------------------------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `graphqlSchemaPaths` | List(String) | (falls back to `graphqlSchemas`) | GraphQL schema locations. You can supply multiple paths to GraphQL schemas. To include many schemas from a folder hierarchy, use the `graphqlSchemas` block instead. |
+| `graphqlSchemas` | *See
[graphqlSchemas](#option-graphqlschemas)* | All
`.graphqls`/`.graphql`
files in
resources | Block to define the input GraphQL schemas, when exact paths are too cumbersome. See table below for a list of options. *See [graphqlSchemas](#option-graphqlschemas)* |
+| `graphqlQueryIntrospectionResu`
`ltPath` | String | None | Path to GraphQL Introspection Query result in json format (with root object `__schema` or `data.__schema`). Sample: [sample-introspection-query-result.json](../src/test/resources/introspection-result/sample-introspection-query-result.json) |
+| `outputDir` | String | None | The output target directory into which code will be generated. |
+| `configurationFiles` | List(String) | Empty | Paths to the files with mapping configurations. Supported formats. JSON, HOCON. Order of specified configuration files matters, so the default configuration should be placed at the end. |
+| `packageName` | String | Empty | Java package for generated classes. |
+| `apiPackageName` | String | Empty | Java package for generated api classes (Query, Mutation, Subscription). |
+| `modelPackageName` | String | Empty | Java package for generated model classes (type, input, interface, enum, union). |
+| `generateBuilder` | Boolean | True | Specifies whether generated model classes should have builder. |
+| `generateApis` | Boolean | True | Specifies whether api classes should be generated as well as model classes. |
+| `generateDataFetchingEnvironme`
`ntArgumentInApis` | Boolean | False | If true, then `graphql.schema.DataFetchingEnvironment env` will be added as a last argument to all methods of root type resolvers and field resolvers. |
+| `generateEqualsAndHashCode` | Boolean | False | Specifies whether generated model classes should have equals and hashCode methods defined. |
+| `generateParameterizedFieldsResolvers` | Boolean | True | Specifies whether separate `Resolver` interface for parametrized fields should be generated. If `false`, then add parametrized field to the type definition and ignore field parameters. If `true` then separate `Resolver` interface for parametrized fields will be generated. |
+| `generateImmutableModels` | Boolean | False | Specifies whether generated model classes should be immutable. |
+| `generateToString` | Boolean | False | Specifies whether generated model classes should have toString method defined. |
+| `generateJacksonTypeIdResolver` | Boolean | False | Specifies whether generated union interfaces should be annotated with a custom Jackson type id resolver generated in model package. |
+| `addGeneratedAnnotation` | Boolean | True | Specifies whether generated classes should have `@Generated` annotation. |
+| `generatedAnnotation` | String | `jakarta.annotation.Generated`
`javax.annotation.Generated` | Qualified class name (with package) of the `@Generated` annotation that will be added on top of every generated class (if `addGeneratedAnnotation` is **true**) |
+| `apiNamePrefix` | String | Empty | Sets the prefix for GraphQL api classes (query, mutation, subscription). |
+| `apiNameSuffix` | String | `Resolver` | Sets the suffix for GraphQL api classes (query, mutation, subscription). |
+| `apiInterfaceStrategy` | *See
[ApiInterfaceStrategy](#option-apiinterfacestrategy)* | `INTERFACE_PER_OPERATION` | *See [ApiInterfaceStrategy](#option-apiinterfacestrategy)* |
+| `apiRootInterfaceStrategy` | *See
[ApiRootInterfaceStrategy](#option-apirootinterfacestrategy)* | `SINGLE_INTERFACE` | *See [ApiRootInterfaceStrategy](#option-apirootinterfacestrategy)* |
+| `apiNamePrefixStrategy` | *See
[ApiNamePrefixStrategy](#option-apinameprefixstrategy)* | `CONSTANT` | *See [ApiNamePrefixStrategy](#option-apinameprefixstrategy)* |
+| `modelNamePrefix` | String | Empty | Sets the prefix for GraphQL model classes (type, input, interface, enum, union). |
+| `modelNameSuffix` | String | Empty | Sets the suffix for GraphQL model classes (type, input, interface, enum, union). |
+| `modelValidationAnnotation` | String | `@javax.validation.`
`constraints.NotNull` | Annotation for mandatory (NonNull) fields. Can be null/empty. |
+| `typeResolverPrefix` | String | Empty | Sets the prefix for GraphQL type resolver classes. |
+| `typeResolverSuffix` | String | `Resolver` | Sets the suffix for GraphQL type resolver classes. |
+| `customTypesMapping` | Map(String,String) | Empty | *See [CustomTypesMapping](#option-customtypesmapping)* |
+| `customTemplates` | Map(FreeMarkerTemplateType,String) | Empty | Use to supply paths to custom FreeMarker templates for code generation. |
+| `customAnnotationsMapping` | Map(String,String[]) | Empty | *See [CustomAnnotationsMapping](#option-customannotationsmapping)* |
+| `directiveAnnotationsMapping` | Map(String,String[]) | Empty | *See [DirectiveAnnotationsMapping](#option-directiveannotationsmapping)* |
+| `fieldsWithResolvers` | Set(String) | Empty | Fields that require Resolvers should be defined here in format: `TypeName.fieldName` or `TypeName` or `@directive`. E.g.: `Person`, `Person.friends`, `@customResolver`. |
+| `fieldsWithoutResolvers` | Set(String) | Empty | Fields that DO NOT require Resolvers should be defined here in format: `TypeName.fieldName` or `TypeName` or `@directive`. Can be used in conjunction with `generateExtensionFieldsResolvers` option. E.g.: `Person`, `Person.friends`, `@noResolver`. |
+| `resolverArgumentAnnotations` | Set(String) | Empty | Annotations that will be added to all resolver arguments. Can be used for [spring-graphql](https://github.com/spring-projects/spring-graphql) inegration by supplying: `org.springframework.graphql.data.method.annotation.Argument` |
+| `parametrizedResolverAnnotations` | Set(String) | Empty | Annotations that will be added to all parametrized resolver methods. Can be used for [spring-graphql](https://github.com/spring-projects/spring-graphql) inegration by supplying: `org.springframework.graphql.data.method.annotation.SchemaMapping(typeName="{{TYPE_NAME}}")` |
+| `generateParameterizedFieldsResolvers` | Boolean | True | If true, then generate separate `Resolver` interface for parametrized fields. If false, then add field to the type definition and ignore field parameters. |
+| `generateExtensionFieldsResolvers` | Boolean | False | Specifies whether all fields in extensions (`extend type` and `extend interface`) should be present in Resolver interface instead of the type class itself. |
+| `generateModelsForRootTypes` | Boolean | False | Specifies whether model classes should be generated for `type Query`, `type Subscription`, `type Mutation`. |
+| `useOptionalForNullableReturnTypes` | Boolean | False | Specifies whether nullable return types of api methods should be wrapped into [`java.util.Optional<>`](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Optional.html). Lists will not be wrapped. |
+| `generateApisWithThrowsException` | Boolean | True | Specifies whether api interface methods should have `throws Exception` in signature. |
+| `generateNoArgsConstructorOnly` | Boolean | False | Specifies whether model classes should only have a no-args constructor. All-args constructor will not be generated in case value is true |
+| `generateModelsWithPublicFields` | Boolean | False | Specifies whether model classes should have public fields and NO getters/setters. By default, fields are private and there are getters/setters for each field. |
+| `apiReturnType` | String | Empty | Return type for api methods (query/mutation). For example: `reactor.core.publisher.Mono`, etc. |
+| `apiReturnListType` | String | Empty | Return type for api methods (query/mutation) having list type. For example: `reactor.core.publisher.Flux`, etc. By default is empty, so `apiReturnType` will be used. |
+| `subscriptionReturnType` | String | Empty | Return type for subscription methods. For example: `org.reactivestreams.Publisher`, `io.reactivex.Observable`, etc. |
+| `relayConfig` | *See
[RelayConfig](#option-relayconfig)* | `@connection(for: ...)` | *See [RelayConfig](#option-relayconfig)* |
+| `generateClient` | Boolean | False | Specifies whether client-side classes should be generated for each query, mutation and subscription. This includes: `Request` classes (contain input data), `ResponseProjection` classes for each type (contain response fields) and `Response` classes (contain response data). |
+| `requestSuffix` | String | Request | Sets the suffix for `Request` classes. |
+| `responseSuffix` | String | Response | Sets the suffix for `Response` classes. |
+| `responseProjectionSuffix` | String | ResponseProjection | Sets the suffix for `ResponseProjection` classes. |
+| `parametrizedInputSuffix` | String | ParametrizedInput | Sets the suffix for `ParametrizedInput` classes. |
+| `parentInterfaces` | *See
[parentInterfaces](#option-parentinterfaces)* | Empty | Block to define parent interfaces for generated interfaces (query / mutation / subscription / type resolver). *See [parentInterfaces](#option-parentinterfaces)* |
+| `generateAllMethodInProjection` | Boolean | True | Enables whether the `all$()` method should be generated in the projection classes. Disabling enforces the client to select the fields manually. |
+| `responseProjectionMaxDepth` | Integer | 3 | Sets max depth when use `all$()` which for facilitating the construction of projection automatically, the fields on all projections are provided when it be invoked. This is a global configuration, of course, you can use `all$(max)` to set for each method. For self recursive types, too big depth may result in a large number of returned data! |
+| `generatedLanguage` | Enum | GeneratedLanguage.JAVA | Choose which language you want to generate, Java,Scala,Kotlin were supported. Note that due to language features, there are slight differences in default values between languages. |
+| `generateModelOpenClasses` | Boolean | False | The class type of the generated model. If true, generate normal classes, else generate data classes. It only support in kotlin(```data class```) and scala(```case class```). Maybe we will consider to support Java ```record``` in the future. |
+| `initializeNullableTypes` | Boolean | False | Adds a default null value to nullable arguments. Only supported in Kotlin. |
+| `generateSealedInterfaces` | Boolean | False | This applies to generated interfaces on unions and interfaces. If true, generate sealed interfaces, else generate normal ones. It is only supported in Kotlin and Scala. |
+| `typesAsInterfaces` | Set(String) | Empty | Types that must generated as interfaces should be defined here in format: `TypeName` or `@directive`. E.g.: `User`, `@asInterface`. |
+| `useObjectMapperForRequestSerialization` | Set(String) | Empty | Fields that require serialization using `com.fasterxml.jackson.databind.ObjectMapper#writeValueAsString(Object)`. Values should be defined here in the following format: `GraphqlObjectName.fieldName` or `GraphqlTypeName`. If just type is specified, then all fields of this type will be serialized using ObjectMapper. E.g.: `["Person.createdDateTime", ZonedDateTime"]` |
+| `supportUnknownFields` | Boolean | False | Specifies whether api classes should support unknown fields during serialization or deserialization. If `true`, classes will include a property of type [`java.util.Map`](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Map.html) that will store unknown fields. |
+| `unknownFieldsPropertyName` | String | userDefinedFields | Specifies the name of the property to be included in api classes to support unknown fields during serialization or deserialization |
+| `skip` | Boolean | False | If true, then code generation will not happen |
### Option `graphqlSchemas`
diff --git a/plugins/gradle/graphql-java-codegen-gradle-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/gradle/GraphQLCodegenGradleTask.java b/plugins/gradle/graphql-java-codegen-gradle-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/gradle/GraphQLCodegenGradleTask.java
index 1dbff1ca6..244454bfc 100644
--- a/plugins/gradle/graphql-java-codegen-gradle-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/gradle/GraphQLCodegenGradleTask.java
+++ b/plugins/gradle/graphql-java-codegen-gradle-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/gradle/GraphQLCodegenGradleTask.java
@@ -1,6 +1,7 @@
package io.github.kobylynskyi.graphql.codegen.gradle;
import com.kobylynskyi.graphql.codegen.GraphQLCodegen;
+import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.kotlin.KotlinGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.model.ApiInterfaceStrategy;
@@ -54,6 +55,7 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
private Map customTypesMapping = new HashMap<>();
private Map> customAnnotationsMapping = new HashMap<>();
+ private Map customTemplates = new HashMap<>();
private Map> directiveAnnotationsMapping = new HashMap<>();
private String packageName;
private String apiPackageName;
@@ -133,8 +135,8 @@ public void generate() throws Exception {
mappingConfig.setPackageName(packageName);
mappingConfig.setCustomTypesMapping(
customTypesMapping != null ? customTypesMapping : new HashMap<>());
- mappingConfig.setCustomAnnotationsMapping(
- customAnnotationsMapping != null ? customAnnotationsMapping : new HashMap<>());
+ mappingConfig.setCustomTemplates(
+ customTemplates != null ? customTemplates : new HashMap<>());
mappingConfig.setDirectiveAnnotationsMapping(
directiveAnnotationsMapping != null ? directiveAnnotationsMapping : new HashMap<>());
mappingConfig.setApiNameSuffix(apiNameSuffix);
@@ -333,6 +335,17 @@ public void setCustomTypesMapping(Map customTypesMapping) {
this.customTypesMapping = customTypesMapping;
}
+ @Input
+ @Optional
+ @Override
+ public Map getCustomTemplates() {
+ return customTemplates;
+ }
+
+ public void setCustomTemplates(Map customTemplates) {
+ this.customTemplates = customTemplates;
+ }
+
@Input
@Optional
@Override
diff --git a/plugins/maven/graphql-java-codegen-maven-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/GraphQLCodegenMojo.java b/plugins/maven/graphql-java-codegen-maven-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/GraphQLCodegenMojo.java
index 18af6a2a1..d47f9dc9b 100644
--- a/plugins/maven/graphql-java-codegen-maven-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/GraphQLCodegenMojo.java
+++ b/plugins/maven/graphql-java-codegen-maven-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/GraphQLCodegenMojo.java
@@ -1,6 +1,7 @@
package io.github.kobylynskyi.graphql.codegen;
import com.kobylynskyi.graphql.codegen.GraphQLCodegen;
+import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.kotlin.KotlinGraphQLCodegen;
import com.kobylynskyi.graphql.codegen.model.ApiInterfaceStrategy;
@@ -65,6 +66,9 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
@Parameter
private Map customAnnotationsMapping;
+ @Parameter
+ private Map customTemplates;
+
@Parameter
private Map directiveAnnotationsMapping;
@@ -246,6 +250,7 @@ public void execute() throws MojoExecutionException {
MappingConfig mappingConfig = new MappingConfig();
mappingConfig.setPackageName(packageName);
mappingConfig.setCustomTypesMapping(convertToMap(customTypesMapping));
+ mappingConfig.setCustomTemplates(customTemplates);
mappingConfig.setCustomAnnotationsMapping(convertToListsMap(customAnnotationsMapping));
mappingConfig.setDirectiveAnnotationsMapping(convertToListsMap(directiveAnnotationsMapping));
mappingConfig.setApiNameSuffix(apiNameSuffix);
@@ -737,4 +742,12 @@ private static Map convertToMap(Properties properties) {
return result;
}
+ @Override
+ public Map getCustomTemplates() {
+ if (customTemplates == null) {
+ return new HashMap<>();
+ }
+ return customTemplates;
+ }
+
}
diff --git a/plugins/sbt/graphql-java-codegen-sbt-plugin/src/main/scala/io/github/dreamylost/graphql/codegen/GraphQLCodegenKeys.scala b/plugins/sbt/graphql-java-codegen-sbt-plugin/src/main/scala/io/github/dreamylost/graphql/codegen/GraphQLCodegenKeys.scala
index 3c63e379c..acc45095b 100644
--- a/plugins/sbt/graphql-java-codegen-sbt-plugin/src/main/scala/io/github/dreamylost/graphql/codegen/GraphQLCodegenKeys.scala
+++ b/plugins/sbt/graphql-java-codegen-sbt-plugin/src/main/scala/io/github/dreamylost/graphql/codegen/GraphQLCodegenKeys.scala
@@ -3,6 +3,7 @@ package io.github.dreamylost.graphql.codegen
import java.util
import com.kobylynskyi.graphql.codegen.model._
+import com.kobylynskyi.graphql.codegen.generators._
import sbt._
/** @author
@@ -43,6 +44,8 @@ trait GraphQLCodegenKeys {
val customAnnotationsMapping = settingKey[util.Map[String, util.List[String]]]("customAnnotationsMapping")
+ val customTemplates = settingKey[util.Map[FreeMarkerTemplateType, String]]("customTemplates")
+
val generateEqualsAndHashCode =
settingKey[Boolean]("Specifies whether generated model classes should have equals and hashCode methods defined.")
diff --git a/plugins/sbt/graphql-java-codegen-sbt-plugin/src/main/scala/io/github/dreamylost/graphql/codegen/GraphQLCodegenPlugin.scala b/plugins/sbt/graphql-java-codegen-sbt-plugin/src/main/scala/io/github/dreamylost/graphql/codegen/GraphQLCodegenPlugin.scala
index 0fe649e68..72a0c0474 100644
--- a/plugins/sbt/graphql-java-codegen-sbt-plugin/src/main/scala/io/github/dreamylost/graphql/codegen/GraphQLCodegenPlugin.scala
+++ b/plugins/sbt/graphql-java-codegen-sbt-plugin/src/main/scala/io/github/dreamylost/graphql/codegen/GraphQLCodegenPlugin.scala
@@ -7,6 +7,7 @@ import com.kobylynskyi.graphql.codegen.model.exception.LanguageNotSupportedExcep
import com.kobylynskyi.graphql.codegen.model.GeneratedLanguage._
import com.kobylynskyi.graphql.codegen.scala.ScalaGraphQLCodegen
import com.kobylynskyi.graphql.codegen.supplier._
+import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType
import sbt.{ AutoPlugin, PluginTrigger, _ }
import sbt.Keys.{ sLog, sourceManaged, _ }
import sbt.internal.util.complete.DefaultParsers.spaceDelimited
@@ -67,6 +68,7 @@ class GraphQLCodegenPlugin(configuration: Configuration, private[codegen] val co
generateJacksonTypeIdResolver := MappingConfigConstants.DEFAULT_GENERATE_JACKSON_TYPE_ID_RESOLVER,
customTypesMapping := new JHashMap[String, String](), // TODO use scala Map, convert to java Map
customAnnotationsMapping := new JHashMap[String, JList[String]](),
+ customTemplates := new JHashMap[FreeMarkerTemplateType, String](),
directiveAnnotationsMapping := new JHashMap[String, JList[String]](),
javaxValidationApiVersion := None,
graphqlJavaCodegenVersion := None,
@@ -118,17 +120,17 @@ class GraphQLCodegenPlugin(configuration: Configuration, private[codegen] val co
generateBuilder := MappingConfigConstants.DEFAULT_BUILDER,
generateApis := MappingConfigConstants.DEFAULT_GENERATE_APIS,
generateEqualsAndHashCode := MappingConfigConstants.DEFAULT_EQUALS_AND_HASHCODE,
- generateImmutableModels := MappingConfigConstants.DEFAULT_GENERATE_IMMUTABLE_MODELS, // TODO change default value
- generateToString := MappingConfigConstants.DEFAULT_TO_STRING,
+ generateImmutableModels := MappingConfigConstants.DEFAULT_GENERATE_IMMUTABLE_MODELS, // TODO change default value
+ generateToString := MappingConfigConstants.DEFAULT_TO_STRING,
// parent interfaces configs:
- parentInterfaces := parentInterfacesConfig,
- generateAllMethodInProjection := MappingConfigConstants.DEFAULT_GENERATE_ALL_METHOD,
- responseProjectionMaxDepth := MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH,
- supportUnknownFields := MappingConfigConstants.DEFAULT_SUPPORT_UNKNOWN_FIELDS,
- unknownFieldsPropertyName := MappingConfigConstants.DEFAULT_UNKNOWN_FIELDS_PROPERTY_NAME,
- generateNoArgsConstructorOnly := MappingConfigConstants.DEFAULT_GENERATE_NOARGS_CONSTRUCTOR_ONLY,
- generateModelsWithPublicFields := MappingConfigConstants.DEFAULT_GENERATE_MODELS_WITH_PUBLIC_FIELDS,
- skip := false
+ parentInterfaces := parentInterfacesConfig,
+ generateAllMethodInProjection := MappingConfigConstants.DEFAULT_GENERATE_ALL_METHOD,
+ responseProjectionMaxDepth := MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH,
+ supportUnknownFields := MappingConfigConstants.DEFAULT_SUPPORT_UNKNOWN_FIELDS,
+ unknownFieldsPropertyName := MappingConfigConstants.DEFAULT_UNKNOWN_FIELDS_PROPERTY_NAME,
+ generateNoArgsConstructorOnly := MappingConfigConstants.DEFAULT_GENERATE_NOARGS_CONSTRUCTOR_ONLY,
+ generateModelsWithPublicFields := MappingConfigConstants.DEFAULT_GENERATE_MODELS_WITH_PUBLIC_FIELDS,
+ skip := false
)
private def getMappingConfig(): Def.Initialize[MappingConfig] = Def.setting {
@@ -149,6 +151,7 @@ class GraphQLCodegenPlugin(configuration: Configuration, private[codegen] val co
mappingConfig.setTypeResolverPrefix((GraphQLCodegenConfig / typeResolverPrefix).value.orNull)
mappingConfig.setModelValidationAnnotation((GraphQLCodegenConfig / modelValidationAnnotation).value)
mappingConfig.setCustomAnnotationsMapping((GraphQLCodegenConfig / customAnnotationsMapping).value)
+ mappingConfig.setCustomTemplates((GraphQLCodegenConfig / customTemplates).value)
mappingConfig.setGenerateEqualsAndHashCode((GraphQLCodegenConfig / generateEqualsAndHashCode).value)
mappingConfig.setGenerateImmutableModels((GraphQLCodegenConfig / generateImmutableModels).value)
mappingConfig.setGenerateToString((GraphQLCodegenConfig / generateToString).value)
diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/generators/FreeMarkerTemplateFilesCreator.java b/src/main/java/com/kobylynskyi/graphql/codegen/generators/FreeMarkerTemplateFilesCreator.java
index 96049c639..837295e0a 100644
--- a/src/main/java/com/kobylynskyi/graphql/codegen/generators/FreeMarkerTemplateFilesCreator.java
+++ b/src/main/java/com/kobylynskyi/graphql/codegen/generators/FreeMarkerTemplateFilesCreator.java
@@ -48,7 +48,7 @@ public static File create(MappingContext mappingContext,
}
try (FileWriter fileWriter = new FileWriter(javaSourceFile)) {
- Template template = FreeMarkerTemplatesRegistry.getTemplateWithLang(language, templateType);
+ Template template = getTemplateForTypeAndLanguage(mappingContext, templateType, language);
template.process(dataModel, fileWriter);
} catch (Exception e) {
throw new UnableToCreateFileException(e);
@@ -56,6 +56,20 @@ public static File create(MappingContext mappingContext,
return javaSourceFile;
}
+ private static Template getTemplateForTypeAndLanguage(MappingContext mappingContext,
+ FreeMarkerTemplateType templateType,
+ GeneratedLanguage language) {
+ String templatePath = null;
+ if (mappingContext.getCustomTemplates() != null) {
+ templatePath = mappingContext.getCustomTemplates().get(templateType);
+ }
+ if (templatePath != null) {
+ return FreeMarkerTemplatesRegistry.getCustomTemplates(templatePath);
+ } else {
+ return FreeMarkerTemplatesRegistry.getTemplateWithLang(language, templateType);
+ }
+ }
+
private static File getFileTargetDirectory(Map dataModel, File outputDir) {
File targetDir;
Object packageName = dataModel.get(DataModelFields.PACKAGE);
diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/generators/FreeMarkerTemplatesRegistry.java b/src/main/java/com/kobylynskyi/graphql/codegen/generators/FreeMarkerTemplatesRegistry.java
index 9d467641e..3c3da45d7 100644
--- a/src/main/java/com/kobylynskyi/graphql/codegen/generators/FreeMarkerTemplatesRegistry.java
+++ b/src/main/java/com/kobylynskyi/graphql/codegen/generators/FreeMarkerTemplatesRegistry.java
@@ -22,9 +22,9 @@ class FreeMarkerTemplatesRegistry {
private static final EnumMap> templateMap =
new EnumMap<>(GeneratedLanguage.class);
+ private static final Configuration configuration = buildFreeMarkerTemplateConfiguration();
+
static {
- Configuration configuration = buildFreeMarkerTemplateConfiguration();
-
try {
templateMap.put(GeneratedLanguage.JAVA, getTemplates(configuration, GeneratedLanguage.JAVA));
templateMap.put(GeneratedLanguage.SCALA, getTemplates(configuration, GeneratedLanguage.SCALA));
@@ -70,4 +70,12 @@ private static Configuration buildFreeMarkerTemplateConfiguration() {
return configuration;
}
+ public static Template getCustomTemplates(String templatePath) {
+ try {
+ return configuration.getTemplate(templatePath);
+ } catch (IOException e) {
+ throw new UnableToLoadFreeMarkerTemplateException(e);
+ }
+ }
+
}
diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java b/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java
index 7972ca044..c6755b953 100644
--- a/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java
+++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java
@@ -1,5 +1,6 @@
package com.kobylynskyi.graphql.codegen.model;
+import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -27,6 +28,13 @@ public interface GraphQLCodegenConfiguration {
* @return mappings from GraphqlType to JavaType
*/
Map getCustomTypesMapping();
+
+ /**
+ * Can be used to supply paths to custom FreeMarker templates for code generation.
+ *
+ * @return a map, where key is a tempalte type and a value is path to a FreeMarker template
+ */
+ Map getCustomTemplates();
/**
* Can be used to supply custom annotations (serializers) for scalars.
diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java
index 72de287c0..46cc31f85 100644
--- a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java
+++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java
@@ -1,5 +1,6 @@
package com.kobylynskyi.graphql.codegen.model;
+import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -85,6 +86,7 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable parametrizedResolverAnnotations = new HashSet<>();
private Map customTypesMapping = new HashMap<>();
+ private Map customTemplates = new HashMap<>();
private Set typesAsInterfaces = new HashSet<>();
@@ -94,9 +96,9 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable Map combineMap(Map thisMap, Map otherMap) {
+ private static Map combineMap(Map thisMap, Map otherMap) {
if (thisMap != null && otherMap != null) {
- Map resultMap = new HashMap<>();
+ Map resultMap = new HashMap<>();
resultMap.putAll(thisMap);
resultMap.putAll(otherMap);
return resultMap;
@@ -186,6 +188,7 @@ public void combine(MappingConfig source) {
fieldsWithResolvers = combineSet(fieldsWithResolvers, source.fieldsWithResolvers);
fieldsWithoutResolvers = combineSet(fieldsWithoutResolvers, source.fieldsWithoutResolvers);
customTypesMapping = combineMap(customTypesMapping, source.customTypesMapping);
+ customTemplates = combineMap(customTemplates, source.customTemplates);
customAnnotationsMapping = combineMap(customAnnotationsMapping, source.customAnnotationsMapping);
directiveAnnotationsMapping = combineMap(directiveAnnotationsMapping, source.directiveAnnotationsMapping);
resolverArgumentAnnotations = combineSet(resolverArgumentAnnotations, source.resolverArgumentAnnotations);
@@ -244,6 +247,28 @@ public void setCustomTypesMapping(Map customTypesMapping) {
this.customTypesMapping = customTypesMapping;
}
+ /**
+ * Provide a path to a custom template for the specific FreeMarker template type (if absent).
+ *
+ * @param from the from
+ * @param to the to
+ */
+ public void putCustomTemplatesIfAbsent(FreeMarkerTemplateType from, String to) {
+ if (customTemplates == null) {
+ customTemplates = new HashMap<>();
+ }
+ customTemplates.computeIfAbsent(from, k -> to);
+ }
+
+ @Override
+ public Map getCustomTemplates() {
+ return customTemplates;
+ }
+
+ public void setCustomTemplates(Map customTemplates) {
+ this.customTemplates = customTemplates;
+ }
+
@Override
public Map> getCustomAnnotationsMapping() {
return customAnnotationsMapping;
diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingContext.java b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingContext.java
index 6420a8158..503f7d961 100644
--- a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingContext.java
+++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingContext.java
@@ -1,5 +1,6 @@
package com.kobylynskyi.graphql.codegen.model;
+import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
import com.kobylynskyi.graphql.codegen.mapper.DataModelMapper;
import com.kobylynskyi.graphql.codegen.mapper.DataModelMapperFactory;
import com.kobylynskyi.graphql.codegen.mapper.FieldDefinitionToParameterMapper;
@@ -83,6 +84,11 @@ public Boolean isGenerateSealedInterfaces() {
public Map getCustomTypesMapping() {
return config.getCustomTypesMapping();
}
+
+ @Override
+ public Map getCustomTemplates() {
+ return config.getCustomTemplates();
+ }
@Override
public Map> getCustomAnnotationsMapping() {
diff --git a/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenCustomTemplatesTest.java b/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenCustomTemplatesTest.java
new file mode 100644
index 000000000..e965414fd
--- /dev/null
+++ b/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenCustomTemplatesTest.java
@@ -0,0 +1,54 @@
+package com.kobylynskyi.graphql.codegen;
+
+import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType;
+import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
+import com.kobylynskyi.graphql.codegen.model.MappingConfig;
+import com.kobylynskyi.graphql.codegen.utils.Utils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Objects;
+
+import static com.kobylynskyi.graphql.codegen.TestUtils.assertFileContainsElements;
+import static java.util.Collections.singletonList;
+
+class GraphQLCodegenCustomTemplatesTest {
+
+ private final File outputBuildDir = new File("build/generated");
+ private final File outputJavaClassesDir = new File("build/generated/com/kobylynskyi/graphql/test1");
+
+ private MappingConfig mappingConfig;
+
+ @BeforeEach
+ void init() {
+ mappingConfig = new MappingConfig();
+ mappingConfig.setPackageName("com.kobylynskyi.graphql.test1");
+ mappingConfig.setGenerateClient(true);
+ }
+
+ @AfterEach
+ void cleanup() {
+ Utils.deleteDir(outputBuildDir);
+ }
+
+ @Test
+ void generate_CustomTemplates_Type() throws Exception {
+ mappingConfig.putCustomTemplatesIfAbsent(FreeMarkerTemplateType.TYPE, "/template/record_type.ftl");
+
+ generate("src/test/resources/schemas/test.graphqls");
+
+ File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
+ assertFileContainsElements(files, "Event.java",
+ "public record Event (");
+ }
+
+ private void generate(String path) throws IOException {
+ new JavaGraphQLCodegen(singletonList(path),
+ outputBuildDir, mappingConfig, TestUtils.getStaticGeneratedInfo(mappingConfig))
+ .generate();
+ }
+
+}
diff --git a/src/test/resources/template/record_type.ftl b/src/test/resources/template/record_type.ftl
new file mode 100644
index 000000000..3ed480643
--- /dev/null
+++ b/src/test/resources/template/record_type.ftl
@@ -0,0 +1,19 @@
+<#assign MapperUtil=statics["com.kobylynskyi.graphql.codegen.java.JavaGraphQLTypeMapper"]>
+<#if package?has_content>
+package ${package};
+#if>
+
+public record ${className} (
+<#if fields?has_content>
+ <#list fields as field>
+ ${field.type} ${field.name}<#if field.defaultValue?has_content> = ${field.defaultValue}#if><#if field?has_next>,#if>
+ #list>
+#if>
+)
+{
+
+ public ${className}() {
+ <#list fields as field><#if field.defaultValue?has_content> ${field.name} = ${field.defaultValue};#if>#list>
+ }
+
+}