diff --git a/docs/codegen-options.md b/docs/codegen-options.md index 2efd0942a..c64ad322c 100644 --- a/docs/codegen-options.md +++ b/docs/codegen-options.md @@ -1,89 +1,90 @@ # 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)* | -| `customTemplatesRoot` | File | Project's dir | Use to supply the path the to custom FreeMarker templates root directory. | -| `customTemplates` | Map(String,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`. | -| `fieldsToExcludeFromGeneration` | Set(String) | Empty | Fields to exclude from generation should be defined here in format: `TypeName.fieldName`. | -| `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. | -| `useWrapperForNullableInputTypes` | Boolean | False | Specifies whether nullable parameters on input types should be wrapped into [`ArgumentValue<>`](https://docs.spring.io/spring-graphql/docs/current/api/org/springframework/graphql/data/ArgumentValue.html). This requires org.springframework.graphql.data version 1.1.0+. Lists will not be wrapped. | -| `generateApisWithThrowsException` | Boolean | True | Specifies whether api interface methods should have `throws Exception` in signature. | -| `generateApisWithSuspendFunctions` | Boolean | False | Specifies whether api interface methods should have `suspend` modifier in signature. Only supported in Kotlin. | -| `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 | -| `skipSchemaSizeLimit` | Boolean | True | When set to true, the GraphQL schema will be processed with token, character, line and rule depth limits. Set to false to process the schema regardless of its size. | - +| 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)* | +| `customTemplatesRoot` | File | Project's dir | Use to supply the path the to custom FreeMarker templates root directory. | +| `customTemplates` | Map(String,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`. | +| `fieldsToExcludeFromGeneration` | Set(String) | Empty | Fields to exclude from generation should be defined here in format: `TypeName.fieldName`. | +| `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. | +| `useWrapperForNullableInputTypes` | Boolean | False | Specifies whether nullable parameters on input types should be wrapped into [`ArgumentValue<>`](https://docs.spring.io/spring-graphql/docs/current/api/org/springframework/graphql/data/ArgumentValue.html). This requires org.springframework.graphql.data version 1.1.0+. Lists will not be wrapped. | +| `generateApisWithThrowsException` | Boolean | True | Specifies whether api interface methods should have `throws Exception` in signature. | +| `generateApisWithSuspendFunctions` | Boolean | False | Specifies whether api interface methods should have `suspend` modifier in signature. Only supported in Kotlin. | +| `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`. | +| `fieldsWithDataFetcherResult` | Set(String) | Empty | Types that must have DataFetchResult should be defined here in format: `TypeName`, or `TypeName.fieldName` or `@directive`. E.g.: `Item`, `Item.items` or `@dataFetcherResult`. | +| `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 | +| `skipSchemaSizeLimit` | Boolean | True | When set to true, the GraphQL schema will be processed with token, character, line and rule depth limits. Set to false to process the schema regardless of its size. +| ### Option `graphqlSchemas` When exact paths to GraphQL schemas are too cumbersome to provide in the `graphqlSchemaPaths`, use the `graphqlSchemas` block. The parameters inside that block are the following: -| Key inside `graphqlSchemas` | Data Type | Default value | Description | -|:---------------------------:|:------------:|:------------------:|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `rootDir` | String | Main resources dir | The root directory from which to start searching for schema files. | -| `recursive` | Boolean | `true` | Whether to recursively look into sub directories. | -| `includePattern` | String | `.*\.graphqls?` | A Java regex that file names must match to be included. It should be a regex as defined by the [Pattern](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) JDK class. It will be used to match only the file name without path. | -| `excludedFiles` | Set | (empty set) | A set of files to exclude, even if they match the include pattern. These paths should be either absolute or relative to the provided `rootDir`. | +| Key inside `graphqlSchemas` | Data Type | Default value | Description | +|:---------------------------:|:-----------:|:------------------:|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `rootDir` | String | Main resources dir | The root directory from which to start searching for schema files. | +| `recursive` | Boolean | `true` | Whether to recursively look into sub directories. | +| `includePattern` | String | `.*\.graphqls?` | A Java regex that file names must match to be included. It should be a regex as defined by the [Pattern](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) JDK class. It will be used to match only the file name without path. | +| `excludedFiles` | Set | (empty set) | A set of files to exclude, even if they match the include pattern. These paths should be either absolute or relative to the provided `rootDir`. | ### Option `ApiInterfaceStrategy` @@ -204,6 +205,7 @@ Provide a path to external file via property `configurationFiles` Sample content of the file: JSON: + ```json { "generateApis": true, @@ -215,6 +217,7 @@ JSON: ``` [HOCON](https://en.wikipedia.org/wiki/HOCON): + ``` generateClient=true generateApis=true 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 250b80423..3850a68d6 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 @@ -103,6 +103,7 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode private Set typesAsInterfaces = new HashSet<>(); private Set resolverArgumentAnnotations = new HashSet<>(); private Set parametrizedResolverAnnotations = new HashSet<>(); + private Set fieldsWithDataFetcherResult = new HashSet<>(); private final RelayConfig relayConfig = new RelayConfig(); @@ -193,6 +194,8 @@ public void generate() throws Exception { fieldsWithoutResolvers != null ? fieldsWithoutResolvers : new HashSet<>()); mappingConfig.setFieldsToExcludeFromGeneration( fieldsToExcludeFromGeneration != null ? fieldsToExcludeFromGeneration : new HashSet<>()); + mappingConfig.setFieldsWithDataFetcherResult( + fieldsWithDataFetcherResult != null ? fieldsWithDataFetcherResult : new HashSet<>()); mappingConfig.setTypesAsInterfaces( typesAsInterfaces != null ? typesAsInterfaces : new HashSet<>()); mappingConfig.setResolverArgumentAnnotations( @@ -815,6 +818,17 @@ public void setFieldsToExcludeFromGeneration(Set fieldsToExcludeFromGene this.fieldsToExcludeFromGeneration = fieldsToExcludeFromGeneration; } + @Input + @Optional + @Override + public Set getFieldsWithDataFetcherResult() { + return fieldsWithDataFetcherResult; + } + + public void setFieldsWithDataFetcherResult(Set fieldsWithDataFetcherResult) { + this.fieldsWithDataFetcherResult = fieldsWithDataFetcherResult; + } + @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 b1fb3e045..721c048fa 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 @@ -186,6 +186,9 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo @Parameter private String[] fieldsToExcludeFromGeneration; + @Parameter + private String[] fieldsWithDataFetcherResult; + @Parameter private RelayConfig relayConfig = new RelayConfig(); @@ -305,6 +308,7 @@ public void execute() throws MojoExecutionException { mappingConfig.setFieldsWithResolvers(mapToHashSet(fieldsWithResolvers)); mappingConfig.setFieldsWithoutResolvers(mapToHashSet(fieldsWithoutResolvers)); mappingConfig.setFieldsToExcludeFromGeneration(mapToHashSet(fieldsToExcludeFromGeneration)); + mappingConfig.setFieldsWithDataFetcherResult(mapToHashSet(fieldsWithDataFetcherResult)); mappingConfig.setRelayConfig(relayConfig); mappingConfig.setGenerateClient(generateClient); @@ -627,6 +631,11 @@ public Set getFieldsToExcludeFromGeneration() { return mapToHashSet(fieldsToExcludeFromGeneration); } + @Override + public Set getFieldsWithDataFetcherResult() { + return mapToHashSet(fieldsWithDataFetcherResult); + } + @Override public Boolean getGenerateAllMethodInProjection() { return generateAllMethodInProjection; diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/java/JavaGraphQLTypeMapper.java b/src/main/java/com/kobylynskyi/graphql/codegen/java/JavaGraphQLTypeMapper.java index 224ce6ac4..3e967f38d 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/java/JavaGraphQLTypeMapper.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/java/JavaGraphQLTypeMapper.java @@ -7,14 +7,17 @@ import com.kobylynskyi.graphql.codegen.model.NamedDefinition; import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperation; import com.kobylynskyi.graphql.codegen.utils.Utils; +import graphql.language.Directive; import graphql.language.InputValueDefinition; import graphql.language.NullValue; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import static java.util.Arrays.asList; @@ -108,7 +111,8 @@ public boolean isPrimitive(String possiblyPrimitiveType) { @Override public NamedDefinition getLanguageType(MappingContext mappingContext, String graphQLType, String name, - String parentTypeName, boolean mandatory, boolean collection) { + String parentTypeName, boolean mandatory, boolean collection, + List directives) { Map customTypesMapping = mappingContext.getCustomTypesMapping(); Set serializeFieldsUsingObjectMapper = mappingContext.getUseObjectMapperForRequestSerialization(); String langTypeName; @@ -122,6 +126,9 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, String gra } else { langTypeName = DataModelMapper.getModelClassNameWithPrefixAndSuffix(mappingContext, graphQLType); } + + langTypeName = wrapWithDataFetcherResultIfRequired(mappingContext, directives, langTypeName, name); + if (serializeFieldsUsingObjectMapper.contains(graphQLType) || (name != null && parentTypeName != null && serializeFieldsUsingObjectMapper.contains(parentTypeName + "." + name))) { @@ -164,4 +171,26 @@ public String wrapApiDefaultValueIfRequired(MappingContext mappingContext, Named } } -} \ No newline at end of file + private String wrapWithDataFetcherResultIfRequired(MappingContext mappingContext, + List directives, + String langTypeName, + String name) { + Set fieldsWithDataFetcherResult = mappingContext.getFieldsWithDataFetcherResult(); + Set directivesNames = directives.stream() + .map(directive -> "@" + directive.getName()) + .collect(Collectors.toSet()); + + // Create the representation of 'name' + String nameRepresentation = langTypeName + "." + name; + + boolean shouldWrap = directivesNames.stream().anyMatch(fieldsWithDataFetcherResult::contains) + || fieldsWithDataFetcherResult.contains(langTypeName) + || fieldsWithDataFetcherResult.contains(nameRepresentation); + + if (shouldWrap) { + langTypeName = "graphql.execution.DataFetcherResult<" + langTypeName + ">"; + } + + return langTypeName; + } +} diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/mapper/FieldDefinitionToParameterMapper.java b/src/main/java/com/kobylynskyi/graphql/codegen/mapper/FieldDefinitionToParameterMapper.java index 14d83d5f6..9165b08f9 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/mapper/FieldDefinitionToParameterMapper.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/mapper/FieldDefinitionToParameterMapper.java @@ -146,7 +146,9 @@ private ParameterDefinition mapField(MappingContext mappingContext, ExtendedFieldDefinition fieldDef, ExtendedDefinition parentDefinition) { NamedDefinition namedDefinition = graphQLTypeMapper - .getLanguageType(mappingContext, fieldDef.getType(), fieldDef.getName(), parentDefinition.getName()); + .getLanguageType(mappingContext, fieldDef.getType(), fieldDef.getName(), parentDefinition.getName(), + fieldDef.getDirectives() + ); ParameterDefinition parameter = new ParameterDefinition(); parameter.setName(dataModelMapper.capitalizeIfRestricted(mappingContext, fieldDef.getName())); diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/mapper/GraphQLTypeMapper.java b/src/main/java/com/kobylynskyi/graphql/codegen/mapper/GraphQLTypeMapper.java index d5d8318e4..a14942fb2 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/mapper/GraphQLTypeMapper.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/mapper/GraphQLTypeMapper.java @@ -217,6 +217,23 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, Type gr return getLanguageType(mappingContext, graphqlType, name, parentTypeName, false, false); } + /** + * Convert GraphQL type to a corresponding language-specific type (java/scala/kotlin/etc) + * + * @param mappingContext Global mapping context + * @param graphqlType GraphQL type + * @param name GraphQL type name + * @param parentTypeName Name of the parent type + * @param directives GraphQL field directives + * @return Corresponding language-specific type (java/scala/kotlin/etc) + */ + public NamedDefinition getLanguageType(MappingContext mappingContext, Type graphqlType, String name, + String parentTypeName, List directives) { + return getLanguageType(mappingContext, graphqlType, name, parentTypeName, false, false, + directives + ); + } + /** * Convert GraphQL type to a corresponding language-specific type (java/scala/kotlin/etc) * @@ -231,12 +248,32 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, Type gr public NamedDefinition getLanguageType(MappingContext mappingContext, Type graphqlType, String name, String parentTypeName, boolean mandatory, boolean collection) { + return getLanguageType(mappingContext, graphqlType, name, parentTypeName, mandatory, collection, + Collections.emptyList() + ); + } + + /** + * Convert GraphQL type to a corresponding language-specific type (java/scala/kotlin/etc) + * + * @param mappingContext Global mapping context + * @param graphqlType GraphQL type + * @param name GraphQL type name + * @param parentTypeName Name of the parent type + * @param mandatory GraphQL type is non-null + * @param collection GraphQL type is collection + * @param directives GraphQL field directives + * @return Corresponding language-specific type (java/scala/kotlin/etc) + */ + public NamedDefinition getLanguageType(MappingContext mappingContext, Type graphqlType, + String name, String parentTypeName, + boolean mandatory, boolean collection, List directives) { if (graphqlType instanceof TypeName) { return getLanguageType(mappingContext, ((TypeName) graphqlType).getName(), name, parentTypeName, mandatory, - collection); + collection, directives); } else if (graphqlType instanceof ListType) { NamedDefinition mappedCollectionType = getLanguageType(mappingContext, ((ListType) graphqlType).getType(), - name, parentTypeName, false, true); + name, parentTypeName, false, true, directives); if (mappedCollectionType.isInterfaceOrUnion() && isInterfaceOrUnion(mappingContext, parentTypeName)) { mappedCollectionType.setJavaName( @@ -247,8 +284,8 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, Type gr } return mappedCollectionType; } else if (graphqlType instanceof NonNullType) { - return getLanguageType(mappingContext, ((NonNullType) graphqlType).getType(), name, parentTypeName, true, - collection); + return getLanguageType(mappingContext, ((NonNullType) graphqlType).getType(), name, parentTypeName, + true, collection, directives); } throw new IllegalArgumentException("Unknown type: " + graphqlType); } @@ -262,10 +299,12 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, Type gr * @param parentTypeName Name of the parent type * @param mandatory GraphQL type is non-null * @param collection GraphQL type is collection + * @param directives GraphQL field directives * @return Corresponding language-specific type (java/scala/kotlin/etc) */ public NamedDefinition getLanguageType(MappingContext mappingContext, String graphQLType, String name, - String parentTypeName, boolean mandatory, boolean collection) { + String parentTypeName, boolean mandatory, boolean collection, + List directives) { Map customTypesMapping = mappingContext.getCustomTypesMapping(); Set serializeFieldsUsingObjectMapper = mappingContext.getUseObjectMapperForRequestSerialization(); String langTypeName; @@ -329,5 +368,4 @@ protected boolean isInterfaceOrUnion(MappingContext mappingContext, String graph return mappingContext.getInterfacesName().contains(graphQLType) || mappingContext.getUnionsNames().contains(graphQLType); } - } 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 e6f0709d2..8510667d0 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java @@ -349,6 +349,21 @@ public interface GraphQLCodegenConfiguration { */ Set getFieldsToExcludeFromGeneration(); + /** + * Fields that require DataFetcherResult. + * + *

Values should be defined here in format: TypeName, TypeName.fieldName, @directive + * + * + *

E.g.: + *

    + *
  • {@code @dataFetcherResult}
  • + *
+ * + * @return Set of types and fields that should have DataFetcherResult. + */ + Set getFieldsWithDataFetcherResult(); + /** * Specifies whether return types of generated API interface should be wrapped into java.util.Optional * 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 f172b5c80..b67ca9b55 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java @@ -65,6 +65,7 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable fieldsWithResolvers = new HashSet<>(); private Set fieldsWithoutResolvers = new HashSet<>(); private Set fieldsToExcludeFromGeneration = new HashSet<>(); + private Set fieldsWithDataFetcherResult = new HashSet<>(); // parent interfaces configs: private String queryResolverParentInterface; @@ -195,6 +196,7 @@ public void combine(MappingConfig source) { parametrizedInputSuffix = getValueOrDefaultToThis(source, GraphQLCodegenConfiguration::getParametrizedInputSuffix); fieldsWithResolvers = combineSet(fieldsWithResolvers, source.fieldsWithResolvers); + fieldsWithDataFetcherResult = combineSet(fieldsWithDataFetcherResult, source.fieldsWithDataFetcherResult); fieldsWithoutResolvers = combineSet(fieldsWithoutResolvers, source.fieldsWithoutResolvers); fieldsToExcludeFromGeneration = combineSet(fieldsToExcludeFromGeneration, source.fieldsToExcludeFromGeneration); customTypesMapping = combineMap(customTypesMapping, source.customTypesMapping); @@ -622,6 +624,15 @@ public void setFieldsToExcludeFromGeneration(Set fieldsToExcludeFromGene this.fieldsToExcludeFromGeneration = fieldsToExcludeFromGeneration; } + @Override + public Set getFieldsWithDataFetcherResult() { + return fieldsWithDataFetcherResult; + } + + public void setFieldsWithDataFetcherResult(Set fieldsWithDataFetcherResult) { + this.fieldsWithDataFetcherResult = fieldsWithDataFetcherResult; + } + @Override public String getQueryResolverParentInterface() { return queryResolverParentInterface; 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 937d07cb6..544b5f812 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingContext.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingContext.java @@ -279,6 +279,11 @@ public Set getFieldsToExcludeFromGeneration() { return config.getFieldsToExcludeFromGeneration(); } + @Override + public Set getFieldsWithDataFetcherResult() { + return config.getFieldsWithDataFetcherResult(); + } + @Override public Boolean getGenerateClient() { return config.getGenerateClient(); diff --git a/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenFieldsWithDataFetcherTest.java b/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenFieldsWithDataFetcherTest.java new file mode 100644 index 000000000..9998173f8 --- /dev/null +++ b/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenFieldsWithDataFetcherTest.java @@ -0,0 +1,113 @@ +package com.kobylynskyi.graphql.codegen; + +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.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; + +import static com.kobylynskyi.graphql.codegen.TestUtils.getFileByName; +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class GraphQLCodegenFieldsWithDataFetcherTest { + + private final File outputBuildDir = new File("build/generated"); + private final File outputJavaClassesDir = new File("build/generated/com/github/graphql"); + private final MappingConfig mappingConfig = new MappingConfig(); + + @BeforeEach + void init() { + mappingConfig.setPackageName("com.github.graphql"); + mappingConfig.setFieldsWithDataFetcherResult(new HashSet<>(singleton("@dataFetcherResult"))); + } + + @AfterEach + void cleanup() { + Utils.deleteDir(outputBuildDir); + } + + @Test + void generate_fieldsWithDataFetcherResult_with_directives() throws Exception { + generate("src/test/resources/schemas/fields-with-data-fetcher-result.graphqls"); + + File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles()); + Assertions.assertNotNull(files); + + List generatedFileNames = Arrays.stream(files).map(File::getName).sorted().collect(toList()); + assertEquals( + asList("Cart.java", "Item.java", "Order.java", "QueryResolver.java", "User.java", + "UserCurrentQueryResolver.java"), + generatedFileNames + ); + + File user = getFileByName(files, "User.java"); + String userContext = Utils.getFileContent(user.getPath()).trim(); + + assertTrue(userContext.contains("java.util.List>")); + assertTrue(userContext.contains("graphql.execution.DataFetcherResult")); + } + + @Test + void generate_fieldsWithDataFetcherResult_with_typename() throws Exception { + mappingConfig.getFieldsWithDataFetcherResult().add("Item"); + + generate("src/test/resources/schemas/fields-with-data-fetcher-result.graphqls"); + + File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles()); + Assertions.assertNotNull(files); + + List generatedFileNames = Arrays.stream(files).map(File::getName).sorted().collect(toList()); + assertEquals( + asList("Cart.java", "Item.java", "Order.java", "QueryResolver.java", "User.java", + "UserCurrentQueryResolver.java"), + generatedFileNames + ); + + File cart = getFileByName(files, "Cart.java"); + String userContext = Utils.getFileContent(cart.getPath()).trim(); + + assertTrue(userContext.contains("java.util.List>")); + } + + @Test + void generate_fieldsWithDataFetcherResult_with_name() throws Exception { + mappingConfig.getFieldsWithDataFetcherResult().add("Item.items"); + + generate("src/test/resources/schemas/fields-with-data-fetcher-result.graphqls"); + + File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles()); + Assertions.assertNotNull(files); + + List generatedFileNames = Arrays.stream(files).map(File::getName).sorted().collect(toList()); + assertEquals( + asList("Cart.java", "Item.java", "Order.java", "QueryResolver.java", "User.java", + "UserCurrentQueryResolver.java"), + generatedFileNames + ); + + File cart = getFileByName(files, "Cart.java"); + String userContext = Utils.getFileContent(cart.getPath()).trim(); + + assertTrue(userContext.contains("java.util.List>")); + } + + private void generate(String o) throws IOException { + new JavaGraphQLCodegen(singletonList(o), outputBuildDir, mappingConfig, + TestUtils.getStaticGeneratedInfo(mappingConfig)) + .generate(); + } +} diff --git a/src/test/resources/schemas/fields-with-data-fetcher-result.graphqls b/src/test/resources/schemas/fields-with-data-fetcher-result.graphqls new file mode 100644 index 000000000..56b3a2c18 --- /dev/null +++ b/src/test/resources/schemas/fields-with-data-fetcher-result.graphqls @@ -0,0 +1,32 @@ +# A GraphQL schema provides a root type for each kind of operation. +schema { + # The query root. + query: Query +} + +type Query { + userCurrent: User +} + +type User { + username: String! + email: String! + orders: [Order!]! @dataFetcherResult + cart: Cart! @dataFetcherResult +} + +type Order { + number: String! + price: String! +} + +type Cart { + id: Long! + items: [Item!]! +} + +type Item { + id: String +} + +directive @dataFetcherResult on FIELD_DEFINITION