From 9867d9cad8ee3cab42a36e90f336e9a176193b0f Mon Sep 17 00:00:00 2001 From: Bogdan Kobylynskyi <92bogdan@gmail.com> Date: Sun, 31 Jul 2022 16:21:07 -0400 Subject: [PATCH 1/2] Ability to supply resolverArgumentAnnotations and parametrizedResolverAnnotations #983 --- docs/codegen-options.md | 184 +++++++++--------- .../gradle/GraphQLCodegenGradleTask.java | 28 +++ .../graphql/codegen/GraphQLCodegenMojo.java | 18 ++ .../graphql/codegen/GraphQLCodegen.java | 10 + .../impl/FieldResolversGenerator.java | 5 +- .../codegen/mapper/AnnotationsMapper.java | 13 ++ .../model/GraphQLCodegenConfiguration.java | 24 +++ .../graphql/codegen/model/MappingConfig.java | 33 +++- .../codegen/model/MappingConfigConstants.java | 1 + .../graphql/codegen/model/MappingContext.java | 40 +++- .../model/definitions/ExtendedDocument.java | 18 ++ .../GraphQLCodegenAnnotationsTest.java | 43 +++- .../codegen/model/MappingConfigTest.java | 13 ++ ...ationResolver_ArgumentAnnotations.java.txt | 19 ++ ...r_ParametrizedResolverAnnotations.java.txt | 50 +++++ ...QueryResolver_ArgumentAnnotations.java.txt | 34 ++++ 16 files changed, 429 insertions(+), 104 deletions(-) create mode 100644 src/test/resources/expected-classes/annotation/CreateEventMutationResolver_ArgumentAnnotations.java.txt create mode 100644 src/test/resources/expected-classes/annotation/EventPropertyResolver_ParametrizedResolverAnnotations.java.txt create mode 100644 src/test/resources/expected-classes/annotation/QueryResolver_ArgumentAnnotations.java.txt diff --git a/docs/codegen-options.md b/docs/codegen-options.md index bc43da2a2..54abc7da8 100644 --- a/docs/codegen-options.md +++ b/docs/codegen-options.md @@ -1,67 +1,66 @@ # 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. | -| `addGeneratedAnnotation` | Boolean | True | Specifies whether generated classes should have `@Generated` annotation. | -| `generateJacksonTypeIdResolver` | Boolean | False | Specifies whether generated union interfaces should be annotated with a custom Jackson type id resolver generated in model package. | -| `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`. | -| `generateParameterizedFieldsR`
`esolvers` | 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. | -| `generateExtensionFieldsResol`
`vers` | 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`. | -| `useOptionalForNullableReturn`
`Types` | 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. | -| `generateApisWithThrowsExcept`
`ion` | Boolean | True | Specifies whether api interface methods should have `throws Exception` in signature. | -| `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. | -| `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| +| 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. | +| `addGeneratedAnnotation` | Boolean | True | Specifies whether generated classes should have `@Generated` annotation. | +| `generateJacksonTypeIdResolver` | Boolean | False | Specifies whether generated union interfaces should be annotated with a custom Jackson type id resolver generated in model package. | +| `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}}")` | +| `generateParameterizedFieldsR`
`esolvers` | 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. | +| `generateExtensionFieldsResol`
`vers` | 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`. | +| `useOptionalForNullableReturn`
`Types` | 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. | +| `generateApisWithThrowsExcept`
`ion` | Boolean | True | Specifies whether api interface methods should have `throws Exception` in signature. | +| `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. | +| `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 | ### Option `graphqlSchemas` @@ -69,12 +68,12 @@ See [DirectiveAnnotationsMapping](#option-directiveannotationsmapping)* | 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` @@ -83,32 +82,31 @@ to skip generation of separate interface class for each operation in favor of ha see *[ApiRootInterfaceStrategy](#option-apirootinterfacestrategy)* and *[ApiNamePrefixStrategy](#option-apinameprefixstrategy)*) -| Value | Description | -| --------------------------------------- | ----------- | -| `INTERFACE_PER_OPERATION` **(default)** | Generate separate interface classes for each GraphQL operation. | -| `DO_NOT_GENERATE` | Do not generate separate interfaces classes for GraphQL operation. | +| Value | Description | +|:---------------------------------------:|--------------------------------------------------------------------| +| `INTERFACE_PER_OPERATION` **(default)** | Generate separate interface classes for each GraphQL operation. | +| `DO_NOT_GENERATE` | Do not generate separate interfaces classes for GraphQL operation. | ### Option `ApiRootInterfaceStrategy` Defines how root interface (`QueryResolver` / `MutationResolver` / `SubscriptionResolver` will be generated (in addition to separate interfaces for each query/mutation/subscription) -| Value | Description | -| -------------------------------- | ----------- | -| `INTERFACE_PER_SCHEMA` | Generate multiple super-interfaces for each graphql file.
Takes into account `apiNamePrefixStrategy`.
E.g.: `OrderServiceQueryResolver.java`, `ProductServiceQueryResolver.java`, etc. | -| `SINGLE_INTERFACE` **( -default)** | Generate a single `QueryResolver.java`, `MutationResolver.java`, `SubscriptionResolver.java` for all graphql schema files. | -| `DO_NOT_GENERATE` | Do not generate super interface for GraphQL operations. | +| Value | Description | +|:--------------------------------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `INTERFACE_PER_SCHEMA` | Generate multiple super-interfaces for each graphql file.
Takes into account `apiNamePrefixStrategy`.
E.g.: `OrderServiceQueryResolver.java`, `ProductServiceQueryResolver.java`, etc. | +| `SINGLE_INTERFACE` **(default)** | Generate a single `QueryResolver.java`, `MutationResolver.java`, `SubscriptionResolver.java` for all graphql schema files. | +| `DO_NOT_GENERATE` | Do not generate super interface for GraphQL operations. | ### Option `ApiNamePrefixStrategy` Defines which prefix to use for API interfaces. -| Value | Description | -| ------------------------ | ----------- | -| `FILE_NAME_AS_PREFIX` | Will take GraphQL file name as a prefix for all generated API interfaces + value of `apiNamePrefix` config option.
E.g.:
* following schemas: *resources/schemas/order-service.graphql*, *resources/schemas/product-service.graphql*
* will result in: `OrderServiceQueryResolver.java`, `ProductServiceQueryResolver.java`, etc | +| Value | Description | +|:------------------------:|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `FILE_NAME_AS_PREFIX` | Will take GraphQL file name as a prefix for all generated API interfaces + value of `apiNamePrefix` config option.
E.g.:
* following schemas: *resources/schemas/order-service.graphql*, *resources/schemas/product-service.graphql*
* will result in: `OrderServiceQueryResolver.java`, `ProductServiceQueryResolver.java`, etc | | `FOLDER_NAME_AS_PREFIX` | Will take parent folder name as a prefix for all generated API interfaces + value of `apiNamePrefix` config option. E.g.:
* following schemas: *resources/order-service/schema1.graphql*, *resources/order-service/schema2.graphql*
* will result in: `OrderServiceQueryResolver.java`, `OrderServiceGetOrderByIdQueryResolver.java`, etc | -| `CONSTANT` **(default)** | Will take only the value of `apiNamePrefix` config option. | +| `CONSTANT` **(default)** | Will take only the value of `apiNamePrefix` config option. | resources/schemas/order-service.graphql*, * resources/schemas/product-service.graphql*
* will result in: `OrderServiceQueryResolver.java` @@ -128,12 +126,12 @@ to extend only interfaces generated by this plugin. **Note:** if you want to include a GraphQL type name into the interface name, then use `{{TYPE}}` placeholder. E.g.: `graphql.kickstart.tools.GraphQLResolver<{{TYPE}}>` -| Key inside `parentInterfaces` | Data Type | Default value | Description | -| ----------------------------- | --------- | ------------- | ----------- | -| `queryResolver` | String | Empty | Interface that will be added as "extend" to all generated api Query interfaces. | -| `mutationResolver` | String | Empty | Interface that will be added as "extend" to all generated api Mutation interfaces. | -| `subscriptionResolver` | String | Empty | Interface that will be added as "extend" to all generated api Subscription interfaces. | -| `resolver` | String | Empty | Interface that will be added as "extend" to all generated TypeResolver interfaces. | +| Key inside `parentInterfaces` | Data Type | Default value | Description | +|:-----------------------------:|:---------:|:-------------:|----------------------------------------------------------------------------------------| +| `queryResolver` | String | Empty | Interface that will be added as "extend" to all generated api Query interfaces. | +| `mutationResolver` | String | Empty | Interface that will be added as "extend" to all generated api Mutation interfaces. | +| `subscriptionResolver` | String | Empty | Interface that will be added as "extend" to all generated api Subscription interfaces. | +| `resolver` | String | Empty | Interface that will be added as "extend" to all generated TypeResolver interfaces. | ### Option `customTypesMapping` @@ -172,11 +170,11 @@ can also use one of the formatters for directive argument value: `{{val?toString Can be used to supply a custom configuration for Relay support. For reference see: https://www.graphql-java-kickstart.com/tools/relay/ -| Key inside `relayConfig` | Data Type | Default value | Description | -| ------------------------ | --------- | -------------------------- | ----------- | -| `directiveName` | String | `connection` | Directive name used for marking a field. | -| `directiveArgumentName` | String | `for` | Directive argument name that contains a GraphQL type name. | -| `connectionType` | String | `graphql.relay.Connection` | Generic Connection type. | +| Key inside `relayConfig` | Data Type | Default value | Description | +|:------------------------:|:---------:|:--------------------------:|------------------------------------------------------------| +| `directiveName` | String | `connection` | Directive name used for marking a field. | +| `directiveArgumentName` | String | `for` | Directive argument name that contains a GraphQL type name. | +| `connectionType` | String | `graphql.relay.Connection` | Generic Connection type. | For example, the following schema: 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 4b032aec0..c15ef29f1 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 @@ -87,6 +87,8 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode private Set fieldsWithResolvers = new HashSet<>(); private Set fieldsWithoutResolvers = new HashSet<>(); private Set typesAsInterfaces = new HashSet<>(); + private Set resolverArgumentAnnotations = new HashSet<>(); + private Set parametrizedResolverAnnotations = new HashSet<>(); private final RelayConfig relayConfig = new RelayConfig(); @@ -164,6 +166,10 @@ public void generate() throws Exception { fieldsWithoutResolvers != null ? fieldsWithoutResolvers : new HashSet<>()); mappingConfig.setTypesAsInterfaces( typesAsInterfaces != null ? typesAsInterfaces : new HashSet<>()); + mappingConfig.setResolverArgumentAnnotations( + resolverArgumentAnnotations != null ? resolverArgumentAnnotations : new HashSet<>()); + mappingConfig.setParametrizedResolverAnnotations( + parametrizedResolverAnnotations != null ? parametrizedResolverAnnotations : new HashSet<>()); mappingConfig.setRelayConfig(relayConfig); mappingConfig.setGenerateClient(generateClient); @@ -689,6 +695,28 @@ public void setTypesAsInterfaces(Set typesAsInterfaces) { this.typesAsInterfaces = typesAsInterfaces; } + @Input + @Optional + @Override + public Set getResolverArgumentAnnotations() { + return resolverArgumentAnnotations; + } + + public void setResolverArgumentAnnotations(Set resolverArgumentAnnotations) { + this.resolverArgumentAnnotations = resolverArgumentAnnotations; + } + + @Input + @Optional + @Override + public Set getParametrizedResolverAnnotations() { + return parametrizedResolverAnnotations; + } + + public void setParametrizedResolverAnnotations(Set parametrizedResolverAnnotations) { + this.parametrizedResolverAnnotations = parametrizedResolverAnnotations; + } + @Nested @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 e2982fc29..96cae7575 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 @@ -185,6 +185,12 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo @Parameter private String[] typesAsInterfaces; + @Parameter + private String[] resolverArgumentAnnotations; + + @Parameter + private String[] parametrizedResolverAnnotations; + @Parameter(defaultValue = MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH_STRING) private int responseProjectionMaxDepth; @@ -271,6 +277,8 @@ public void execute() throws MojoExecutionException { mappingConfig.setResponseProjectionMaxDepth(responseProjectionMaxDepth); mappingConfig.setUseObjectMapperForRequestSerialization(mapToHashSet(useObjectMapperForRequestSerialization)); mappingConfig.setTypesAsInterfaces(mapToHashSet(typesAsInterfaces)); + mappingConfig.setResolverArgumentAnnotations(mapToHashSet(resolverArgumentAnnotations)); + mappingConfig.setParametrizedResolverAnnotations(mapToHashSet(parametrizedResolverAnnotations)); mappingConfig.setResolverParentInterface(getResolverParentInterface()); mappingConfig.setQueryResolverParentInterface(getQueryResolverParentInterface()); @@ -590,6 +598,16 @@ public Set getTypesAsInterfaces() { return mapToHashSet(typesAsInterfaces); } + @Override + public Set getResolverArgumentAnnotations() { + return mapToHashSet(resolverArgumentAnnotations); + } + + @Override + public Set getParametrizedResolverAnnotations() { + return mapToHashSet(parametrizedResolverAnnotations); + } + @Override public String getQueryResolverParentInterface() { return parentInterfaces.getQueryResolver(); diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/GraphQLCodegen.java b/src/main/java/com/kobylynskyi/graphql/codegen/GraphQLCodegen.java index 638a1871d..2a59423e3 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/GraphQLCodegen.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/GraphQLCodegen.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static java.util.stream.Collectors.toList; @@ -96,6 +97,15 @@ private static void sanitizeValues(MappingConfig mappingConfig) { mappingConfig.setModelValidationAnnotation( Utils.replaceLeadingAtSign(mappingConfig.getModelValidationAnnotation())); + if (mappingConfig.getResolverArgumentAnnotations() != null) { + mappingConfig.setResolverArgumentAnnotations(mappingConfig.getResolverArgumentAnnotations().stream() + .map(Utils::replaceLeadingAtSign).collect(Collectors.toSet())); + } + if (mappingConfig.getParametrizedResolverAnnotations() != null) { + mappingConfig.setParametrizedResolverAnnotations(mappingConfig.getParametrizedResolverAnnotations().stream() + .map(Utils::replaceLeadingAtSign).collect(Collectors.toSet())); + } + Map> customAnnotationsMapping = mappingConfig.getCustomAnnotationsMapping(); if (customAnnotationsMapping != null) { for (Map.Entry> entry : customAnnotationsMapping.entrySet()) { diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/generators/impl/FieldResolversGenerator.java b/src/main/java/com/kobylynskyi/graphql/codegen/generators/impl/FieldResolversGenerator.java index e668ad7bf..e96eb8d9e 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/generators/impl/FieldResolversGenerator.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/generators/impl/FieldResolversGenerator.java @@ -17,6 +17,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import static java.util.stream.Collectors.toList; @@ -51,9 +52,9 @@ private List generate(List fieldDefinitions, if (!Boolean.TRUE.equals(mappingContext.getGenerateApis())) { return Collections.emptyList(); } + Set fieldNamesWithResolvers = mappingContext.getFieldNamesWithResolvers(); List fieldDefsWithResolvers = fieldDefinitions.stream() - .filter(fieldDef -> FieldDefinitionToParameterMapper.generateResolversForField( - mappingContext, fieldDef, parentDefinition)) + .filter(fieldDef -> fieldNamesWithResolvers.contains(parentDefinition.getName() + "." + fieldDef.getName())) .collect(toList()); List generatedFiles = new ArrayList<>(); diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/mapper/AnnotationsMapper.java b/src/main/java/com/kobylynskyi/graphql/codegen/mapper/AnnotationsMapper.java index 5a23d5ce6..ecc6a103e 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/mapper/AnnotationsMapper.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/mapper/AnnotationsMapper.java @@ -1,5 +1,6 @@ package com.kobylynskyi.graphql.codegen.mapper; +import com.kobylynskyi.graphql.codegen.model.MappingConfigConstants; import com.kobylynskyi.graphql.codegen.model.MappingContext; import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedDefinition; import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedFieldDefinition; @@ -120,6 +121,18 @@ public List getAnnotations(MappingContext mappingContext, String graphQL annotations.addAll(getAnnotationsForDirective(mappingContext, directiveAnnotations, directive)); } } + // 6. Add annotations for resolver arguments + if (!Utils.isEmpty(mappingContext.getResolverArgumentAnnotations()) + && mappingContext.getOperationsName().contains(parentTypeName)) { + annotations.addAll(mappingContext.getResolverArgumentAnnotations()); + } + // 7. Add annotations for parametrized resolvers + if (!Utils.isEmpty(mappingContext.getParametrizedResolverAnnotations()) + && mappingContext.getFieldNamesWithResolvers().contains(parentTypeName + "." + name)) { + for (String annotation : mappingContext.getParametrizedResolverAnnotations()) { + annotations.add(annotation.replace(MappingConfigConstants.TYPE_NAME_PLACEHOLDER, parentTypeName)); + } + } return annotations; } 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 efc0c4a5f..fd6d74cdf 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java @@ -432,6 +432,30 @@ public interface GraphQLCodegenConfiguration { */ Set getTypesAsInterfaces(); + /** + * Annotations that will be added to all resolver arguments. + * + *

E.g.: + *

    + *
  • {@code @org.springframework.graphql.data.method.annotation.Argument}
  • + *
+ * + * @return Set of annotations that every resolver argument will have. + */ + Set getResolverArgumentAnnotations(); + + /** + * Annotations that will be added to all parametrized resolver methods. + * + *

E.g.: + *

    + *
  • {@code @org.springframework.graphql.data.method.annotation.Argument}
  • + *
+ * + * @return Set of annotations that every parametrized resolver method will have. + */ + Set getParametrizedResolverAnnotations(); + /** * Generate code with lang * 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 128901e52..50397b9a1 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 java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -15,10 +16,6 @@ */ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable { - private Map customTypesMapping = new HashMap<>(); - private Map> customAnnotationsMapping = new HashMap<>(); - private Map> directiveAnnotationsMapping = new HashMap<>(); - // package name configs: private String packageName; private String apiPackageName; @@ -77,6 +74,14 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable useObjectMapperForRequestSerialization = new HashSet<>(); + // annotations: + private Map> customAnnotationsMapping = new HashMap<>(); + private Map> directiveAnnotationsMapping = new HashMap<>(); + private Set resolverArgumentAnnotations = new HashSet<>(); + private Set parametrizedResolverAnnotations = new HashSet<>(); + + private Map customTypesMapping = new HashMap<>(); + private Set typesAsInterfaces = new HashSet<>(); private boolean generateModelOpenClasses; @@ -179,6 +184,8 @@ public void combine(MappingConfig source) { customTypesMapping = combineMap(customTypesMapping, source.customTypesMapping); customAnnotationsMapping = combineMap(customAnnotationsMapping, source.customAnnotationsMapping); directiveAnnotationsMapping = combineMap(directiveAnnotationsMapping, source.directiveAnnotationsMapping); + resolverArgumentAnnotations = combineSet(resolverArgumentAnnotations, source.resolverArgumentAnnotations); + parametrizedResolverAnnotations = combineSet(parametrizedResolverAnnotations, source.parametrizedResolverAnnotations); generateAllMethodInProjection = getValueOrDefaultToThis(source, GraphQLCodegenConfiguration::getGenerateAllMethodInProjection); responseProjectionMaxDepth = getValueOrDefaultToThis(source, @@ -641,6 +648,24 @@ public void setUseObjectMapperForRequestSerialization(Set useObjectMappe this.useObjectMapperForRequestSerialization = useObjectMapperForRequestSerialization; } + @Override + public Set getResolverArgumentAnnotations() { + return resolverArgumentAnnotations; + } + + public void setResolverArgumentAnnotations(Set resolverArgumentAnnotations) { + this.resolverArgumentAnnotations = resolverArgumentAnnotations; + } + + @Override + public Set getParametrizedResolverAnnotations() { + return parametrizedResolverAnnotations; + } + + public void setParametrizedResolverAnnotations(Set parametrizedResolverAnnotations) { + this.parametrizedResolverAnnotations = parametrizedResolverAnnotations; + } + @Override public Set getTypesAsInterfaces() { return typesAsInterfaces; diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfigConstants.java b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfigConstants.java index df3499cde..8bd113789 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfigConstants.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfigConstants.java @@ -8,6 +8,7 @@ public class MappingConfigConstants { public static final String DEFAULT_VALIDATION_ANNOTATION = "javax.validation.constraints.NotNull"; public static final String PARENT_INTERFACE_TYPE_PLACEHOLDER = "{{TYPE}}"; + public static final String TYPE_NAME_PLACEHOLDER = "{{TYPE_NAME}}"; public static final boolean DEFAULT_GENERATE_APIS = true; public static final String DEFAULT_GENERATE_APIS_STRING = "true"; public static final boolean DEFAULT_BUILDER = true; 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 8b780d4d5..679b6fa89 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingContext.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingContext.java @@ -2,14 +2,17 @@ import com.kobylynskyi.graphql.codegen.mapper.DataModelMapper; import com.kobylynskyi.graphql.codegen.mapper.DataModelMapperFactory; +import com.kobylynskyi.graphql.codegen.mapper.FieldDefinitionToParameterMapper; import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedDefinition; import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedDocument; import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedEnumTypeDefinition; import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedFieldDefinition; import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedInterfaceTypeDefinition; +import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedObjectTypeDefinition; import java.io.File; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -26,11 +29,13 @@ public class MappingContext implements GraphQLCodegenConfiguration { private final ExtendedDocument document; private final Set typesUnionsInterfacesNames; private final Set interfacesName; + private final Set operationsName; private final Map> interfaceChildren; private final GeneratedInformation generatedInformation; private final DataModelMapperFactory dataModelMapperFactory; private Set enumImportItSelfInScala; private Map> parentInterfaceProperties; + private Set fieldNamesWithResolvers; private MappingContext(File outputDirectory, MappingConfig mappingConfig, @@ -44,6 +49,7 @@ private MappingContext(File outputDirectory, this.interfacesName = document.getInterfacesNames(); this.interfaceChildren = document.getInterfaceChildren(); this.generatedInformation = generatedInformation; + this.operationsName = document.getOperationsNames(); this.dataModelMapperFactory = dataModelMapperFactory; } @@ -63,7 +69,6 @@ public Boolean isInitializeNullableTypes() { } - @Override public Boolean isGenerateSealedInterfaces() { return config.isGenerateSealedInterfaces(); @@ -309,6 +314,16 @@ public Set getTypesAsInterfaces() { return config.getTypesAsInterfaces(); } + @Override + public Set getResolverArgumentAnnotations() { + return config.getResolverArgumentAnnotations(); + } + + @Override + public Set getParametrizedResolverAnnotations() { + return config.getParametrizedResolverAnnotations(); + } + @Override public Boolean isSupportUnknownFields() { return config.isSupportUnknownFields(); @@ -331,6 +346,10 @@ public Set getInterfacesName() { return interfacesName; } + public Set getOperationsName() { + return operationsName; + } + public Map> getInterfaceChildren() { return interfaceChildren; } @@ -381,6 +400,25 @@ public Map> getParentInterfaceProperties() { return parentInterfaceProperties; } + public Set getFieldNamesWithResolvers() { + if (fieldNamesWithResolvers == null) { + fieldNamesWithResolvers = new HashSet<>(); + for (ExtendedObjectTypeDefinition definition : document.getTypeDefinitions()) { + definition.getFieldDefinitions().stream() + .filter(fieldDef -> FieldDefinitionToParameterMapper.generateResolversForField( + this, fieldDef, definition)) + .forEach(fieldDef -> fieldNamesWithResolvers.add(definition.getName() + "." + fieldDef.getName())); + } + for (ExtendedInterfaceTypeDefinition definition : document.getInterfaceDefinitions()) { + definition.getFieldDefinitions().stream() + .filter(fieldDef -> FieldDefinitionToParameterMapper.generateResolversForField( + this, fieldDef, definition)) + .forEach(fieldDef -> fieldNamesWithResolvers.add(definition.getName() + "." + fieldDef.getName())); + } + } + return fieldNamesWithResolvers; + } + private String getModelClassNameWithPrefixAndSuffix(ExtendedEnumTypeDefinition extendedEnumTypeDefinition) { return DataModelMapper.getModelClassNameWithPrefixAndSuffix(this, extendedEnumTypeDefinition.getName()); } diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/model/definitions/ExtendedDocument.java b/src/main/java/com/kobylynskyi/graphql/codegen/model/definitions/ExtendedDocument.java index f8e089dca..a758e8090 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/model/definitions/ExtendedDocument.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/definitions/ExtendedDocument.java @@ -1,16 +1,26 @@ package com.kobylynskyi.graphql.codegen.model.definitions; +import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateFilesCreator; +import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType; +import com.kobylynskyi.graphql.codegen.mapper.FieldDefinitionToParameterMapper; +import graphql.language.FieldDefinition; import graphql.language.Type; import graphql.language.TypeName; +import java.io.File; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toList; + /** * GraphQL document that holds all extended definitions */ @@ -87,6 +97,14 @@ public Set getInterfacesNames() { .collect(Collectors.toSet()); } + public Set getOperationsNames() { + return operationDefinitions.stream() + .map(ExtendedObjectTypeDefinition::getFieldDefinitions) + .flatMap(Collection::stream) + .map(FieldDefinition::getName) + .collect(Collectors.toSet()); + } + public Collection getOperationDefinitions() { return operationDefinitions; } diff --git a/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenAnnotationsTest.java b/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenAnnotationsTest.java index bfd231fdf..baa5d0ab5 100644 --- a/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenAnnotationsTest.java +++ b/src/test/java/com/kobylynskyi/graphql/codegen/GraphQLCodegenAnnotationsTest.java @@ -17,6 +17,7 @@ import static com.kobylynskyi.graphql.codegen.TestUtils.assertFileContainsElements; import static com.kobylynskyi.graphql.codegen.TestUtils.assertSameTrimmedContent; import static com.kobylynskyi.graphql.codegen.TestUtils.getFileByName; +import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; @@ -202,6 +203,40 @@ void generate_CustomAnnotationMappings_Multiple() throws Exception { "public class AcceptTopicSuggestionPayload "); } + @Test + void generate_ResolverArgumentAnnotations() throws Exception { + mappingConfig.setGenerateDataFetchingEnvironmentArgumentInApis(true); + mappingConfig.setResolverArgumentAnnotations(singleton( + "@org.springframework.graphql.data.method.annotation.Argument")); + + new JavaGraphQLCodegen(singletonList("src/test/resources/schemas/test.graphqls"), + outputBuildDir, mappingConfig, TestUtils.getStaticGeneratedInfo()).generate(); + + File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles()); + assertSameTrimmedContent( + new File("src/test/resources/expected-classes/annotation/CreateEventMutationResolver_ArgumentAnnotations.java.txt"), + getFileByName(files, "CreateEventMutationResolver.java")); + assertSameTrimmedContent( + new File("src/test/resources/expected-classes/annotation/QueryResolver_ArgumentAnnotations.java.txt"), + getFileByName(files, "QueryResolver.java")); + } + + @Test + void generate_ParametrizedResolverAnnotations() throws Exception { + mappingConfig.setModelNameSuffix("TO"); + mappingConfig.setFieldsWithResolvers(singleton("@customResolver")); + mappingConfig.setParametrizedResolverAnnotations(singleton( + "@org.springframework.graphql.data.method.annotation.SchemaMapping(typeName=\"{{TYPE_NAME}}\")")); + + new JavaGraphQLCodegen(singletonList("src/test/resources/schemas/test.graphqls"), + outputBuildDir, mappingConfig, TestUtils.getStaticGeneratedInfo()).generate(); + + File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles()); + assertSameTrimmedContent( + new File("src/test/resources/expected-classes/annotation/EventPropertyResolver_ParametrizedResolverAnnotations.java.txt"), + getFileByName(files, "EventPropertyResolver.java")); + } + @Test void generate_CustomAnnotationMappings_RequestResponseClasses() throws Exception { Map> customAnnotationsMapping = new HashMap<>(); @@ -259,11 +294,11 @@ void generate_Directives() throws Exception { new File("src/test/resources/expected-classes/annotation/MutationResolver.java.txt"), getFileByName(files, "MutationResolver.java")); assertSameTrimmedContent( - new File("src/test/resources/expected-classes/annotation/EventProperty.java.txt"), - getFileByName(files, "EventProperty.java")); + new File("src/test/resources/expected-classes/annotation/EventProperty.java.txt"), + getFileByName(files, "EventProperty.java")); assertSameTrimmedContent( - new File("src/test/resources/expected-classes/annotation/User.java.txt"), - getFileByName(files, "User.java")); + new File("src/test/resources/expected-classes/annotation/User.java.txt"), + getFileByName(files, "User.java")); } @Test diff --git a/src/test/java/com/kobylynskyi/graphql/codegen/model/MappingConfigTest.java b/src/test/java/com/kobylynskyi/graphql/codegen/model/MappingConfigTest.java index 70695a1d7..a2cea64b4 100644 --- a/src/test/java/com/kobylynskyi/graphql/codegen/model/MappingConfigTest.java +++ b/src/test/java/com/kobylynskyi/graphql/codegen/model/MappingConfigTest.java @@ -62,6 +62,8 @@ private static MappingConfig buildMappingConfig() { config.setTypeResolverPrefix("11"); config.setTypeResolverSuffix("12"); config.setTypesAsInterfaces(new HashSet<>(singletonList("User"))); + config.setResolverArgumentAnnotations(new HashSet<>(singletonList("Ann1"))); + config.setParametrizedResolverAnnotations(new HashSet<>(singletonList("PAnn1"))); RelayConfig relayConfig = new RelayConfig(); relayConfig.setDirectiveArgumentName("key"); config.setRelayConfig(relayConfig); @@ -106,6 +108,8 @@ private static MappingConfig buildMappingConfig2() { config.setTypeResolverPrefix("1111"); config.setTypeResolverSuffix("1212"); config.setTypesAsInterfaces(new HashSet<>(singletonList("User2"))); + config.setResolverArgumentAnnotations(new HashSet<>(singletonList("Ann2"))); + config.setParametrizedResolverAnnotations(new HashSet<>(singletonList("PAnn2"))); RelayConfig relayConfig = new RelayConfig(); relayConfig.setDirectiveArgumentName("for"); config.setRelayConfig(relayConfig); @@ -163,6 +167,9 @@ private static void compareMappingConfigs(MappingConfig mappingConfig, MappingCo assertEquals(expectedMappingConfig.getUseOptionalForNullableReturnTypes(), mappingConfig.getUseOptionalForNullableReturnTypes()); assertEquals(expectedMappingConfig.getRelayConfig(), mappingConfig.getRelayConfig()); + assertEquals(expectedMappingConfig.getTypesAsInterfaces(), mappingConfig.getTypesAsInterfaces()); + assertEquals(expectedMappingConfig.getResolverArgumentAnnotations(), mappingConfig.getResolverArgumentAnnotations()); + assertEquals(expectedMappingConfig.getParametrizedResolverAnnotations(), mappingConfig.getParametrizedResolverAnnotations()); } @Test @@ -222,6 +229,8 @@ void combineDefaultWithCustom() { assertEquals("12", mappingConfig.getTypeResolverSuffix()); assertEquals("key", mappingConfig.getRelayConfig().getDirectiveArgumentName()); assertEquals(singleton("User"), mappingConfig.getTypesAsInterfaces()); + assertEquals(singleton("Ann1"), mappingConfig.getResolverArgumentAnnotations()); + assertEquals(singleton("PAnn1"), mappingConfig.getParametrizedResolverAnnotations()); } @Test @@ -266,6 +275,8 @@ void combineCustomWithDefault() { assertEquals("12", mappingConfig.getTypeResolverSuffix()); assertEquals("key", mappingConfig.getRelayConfig().getDirectiveArgumentName()); assertEquals(singleton("User"), mappingConfig.getTypesAsInterfaces()); + assertEquals(singleton("Ann1"), mappingConfig.getResolverArgumentAnnotations()); + assertEquals(singleton("PAnn1"), mappingConfig.getParametrizedResolverAnnotations()); } @Test @@ -315,6 +326,8 @@ void combineCustomWithCustom() { assertEquals("1212", mappingConfig.getTypeResolverSuffix()); assertEquals("for", mappingConfig.getRelayConfig().getDirectiveArgumentName()); assertEquals(new HashSet<>(Arrays.asList("User", "User2")), mappingConfig.getTypesAsInterfaces()); + assertEquals(new HashSet<>(Arrays.asList("Ann1", "Ann2")), mappingConfig.getResolverArgumentAnnotations()); + assertEquals(new HashSet<>(Arrays.asList("PAnn1", "PAnn2")), mappingConfig.getParametrizedResolverAnnotations()); } } diff --git a/src/test/resources/expected-classes/annotation/CreateEventMutationResolver_ArgumentAnnotations.java.txt b/src/test/resources/expected-classes/annotation/CreateEventMutationResolver_ArgumentAnnotations.java.txt new file mode 100644 index 000000000..f9e29a6ea --- /dev/null +++ b/src/test/resources/expected-classes/annotation/CreateEventMutationResolver_ArgumentAnnotations.java.txt @@ -0,0 +1,19 @@ +package com.kobylynskyi.graphql.test1; + + +/** + * Create a new event. + */ +@javax.annotation.Generated( + value = "com.kobylynskyi.graphql.codegen.GraphQLCodegen", + date = "2020-12-31T23:59:59-0500" +) +public interface CreateEventMutationResolver { + + /** + * Create a new event. + */ + @javax.validation.constraints.NotNull + Event createEvent(@javax.validation.constraints.NotNull @org.springframework.graphql.data.method.annotation.Argument String categoryId, @org.springframework.graphql.data.method.annotation.Argument String createdBy, graphql.schema.DataFetchingEnvironment env) throws Exception; + +} \ No newline at end of file diff --git a/src/test/resources/expected-classes/annotation/EventPropertyResolver_ParametrizedResolverAnnotations.java.txt b/src/test/resources/expected-classes/annotation/EventPropertyResolver_ParametrizedResolverAnnotations.java.txt new file mode 100644 index 000000000..d71e0c0df --- /dev/null +++ b/src/test/resources/expected-classes/annotation/EventPropertyResolver_ParametrizedResolverAnnotations.java.txt @@ -0,0 +1,50 @@ +package com.kobylynskyi.graphql.test1; + + +/** + * Resolver for EventProperty + */ +@javax.annotation.Generated( + value = "com.kobylynskyi.graphql.codegen.GraphQLCodegen", + date = "2020-12-31T23:59:59-0500" +) +public interface EventPropertyResolver { + + /** + * Float property + * with multiline comment + */ + @org.springframework.graphql.data.method.annotation.SchemaMapping(typeName="EventProperty") + Double floatVal(EventPropertyTO eventProperty) throws Exception; + + @org.springframework.graphql.data.method.annotation.SchemaMapping(typeName="EventProperty") + Boolean booleanVal(EventPropertyTO eventProperty) throws Exception; + + @org.springframework.graphql.data.method.annotation.SchemaMapping(typeName="EventProperty") + int intVal(EventPropertyTO eventProperty) throws Exception; + + /** + * primitive should not be generated + */ + @org.springframework.graphql.data.method.annotation.SchemaMapping(typeName="EventProperty") + java.util.List intVals(EventPropertyTO eventProperty) throws Exception; + + /** + * String comment + */ + @org.springframework.graphql.data.method.annotation.SchemaMapping(typeName="EventProperty") + String stringVal(EventPropertyTO eventProperty) throws Exception; + + /** + * Properties + */ + @org.springframework.graphql.data.method.annotation.SchemaMapping(typeName="EventProperty") + java.util.List child(EventPropertyTO eventProperty, Integer first, Integer last) throws Exception; + + /** + * Parent event of the property + */ + @org.springframework.graphql.data.method.annotation.SchemaMapping(typeName="EventProperty") + EventTO parent(EventPropertyTO eventProperty, EventStatusTO withStatus, String createdAfter) throws Exception; + +} \ No newline at end of file diff --git a/src/test/resources/expected-classes/annotation/QueryResolver_ArgumentAnnotations.java.txt b/src/test/resources/expected-classes/annotation/QueryResolver_ArgumentAnnotations.java.txt new file mode 100644 index 000000000..ed4597fe3 --- /dev/null +++ b/src/test/resources/expected-classes/annotation/QueryResolver_ArgumentAnnotations.java.txt @@ -0,0 +1,34 @@ +package com.kobylynskyi.graphql.test1; + + +@javax.annotation.Generated( + value = "com.kobylynskyi.graphql.codegen.GraphQLCodegen", + date = "2020-12-31T23:59:59-0500" +) +public interface QueryResolver { + + /** + * Version of the application. + */ + @javax.validation.constraints.NotNull + String version(graphql.schema.DataFetchingEnvironment env) throws Exception; + + /** + * List of events of a specified category. + */ + @javax.validation.constraints.NotNull + java.util.List eventsByCategoryAndStatus(@javax.validation.constraints.NotNull @org.springframework.graphql.data.method.annotation.Argument String categoryId, @org.springframework.graphql.data.method.annotation.Argument EventStatus status, graphql.schema.DataFetchingEnvironment env) throws Exception; + + /** + * Single event by ID. + */ + @javax.validation.constraints.NotNull + Event eventById(@javax.validation.constraints.NotNull @org.springframework.graphql.data.method.annotation.Argument String id, graphql.schema.DataFetchingEnvironment env) throws Exception; + + /** + * Events by IDs. + */ + @javax.validation.constraints.NotNull + java.util.List eventsByIds(@javax.validation.constraints.NotNull @org.springframework.graphql.data.method.annotation.Argument java.util.List ids, graphql.schema.DataFetchingEnvironment env) throws Exception; + +} \ No newline at end of file From 4edf8790d41e930c3a5bd5199e590113cf371bb1 Mon Sep 17 00:00:00 2001 From: Bogdan Kobylynskyi <92bogdan@gmail.com> Date: Sun, 31 Jul 2022 16:42:12 -0400 Subject: [PATCH 2/2] Fix imports and markdown table styling --- docs/codegen-options.md | 120 +++++++++--------- .../impl/FieldResolversGenerator.java | 1 - .../graphql/codegen/model/MappingConfig.java | 1 - .../model/definitions/ExtendedDocument.java | 9 -- 4 files changed, 60 insertions(+), 71 deletions(-) diff --git a/docs/codegen-options.md b/docs/codegen-options.md index 54abc7da8..ab795aaaa 100644 --- a/docs/codegen-options.md +++ b/docs/codegen-options.md @@ -1,66 +1,66 @@ # 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. | -| `addGeneratedAnnotation` | Boolean | True | Specifies whether generated classes should have `@Generated` annotation. | -| `generateJacksonTypeIdResolver` | Boolean | False | Specifies whether generated union interfaces should be annotated with a custom Jackson type id resolver generated in model package. | -| `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}}")` | -| `generateParameterizedFieldsR`
`esolvers` | 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. | -| `generateExtensionFieldsResol`
`vers` | 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`. | -| `useOptionalForNullableReturn`
`Types` | 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. | -| `generateApisWithThrowsExcept`
`ion` | Boolean | True | Specifies whether api interface methods should have `throws Exception` in signature. | -| `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. | -| `typesAsInterfaces` | Set(String) | Empty | Types that must generated as interfaces should be defined here in format: `TypeName` or `@directive`. E.g.: `User`, `@asInterface`. | +| 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. | +| `addGeneratedAnnotation` | Boolean | True | Specifies whether generated classes should have `@Generated` annotation. | +| `generateJacksonTypeIdResolver` | Boolean | False | Specifies whether generated union interfaces should be annotated with a custom Jackson type id resolver generated in model package. | +| `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}}")` | +| `generateParameterizedFieldsR`
`esolvers` | 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. | +| `generateExtensionFieldsResol`
`vers` | 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`. | +| `useOptionalForNullableReturn`
`Types` | 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. | +| `generateApisWithThrowsExcept`
`ion` | Boolean | True | Specifies whether api interface methods should have `throws Exception` in signature. | +| `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. | +| `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 | +| `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 | ### Option `graphqlSchemas` diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/generators/impl/FieldResolversGenerator.java b/src/main/java/com/kobylynskyi/graphql/codegen/generators/impl/FieldResolversGenerator.java index e96eb8d9e..583475be4 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/generators/impl/FieldResolversGenerator.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/generators/impl/FieldResolversGenerator.java @@ -4,7 +4,6 @@ import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateFilesCreator; import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType; import com.kobylynskyi.graphql.codegen.mapper.DataModelMapperFactory; -import com.kobylynskyi.graphql.codegen.mapper.FieldDefinitionToParameterMapper; import com.kobylynskyi.graphql.codegen.mapper.FieldDefinitionsToResolverDataModelMapper; import com.kobylynskyi.graphql.codegen.model.MappingContext; import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedDefinition; 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 50397b9a1..792e1c5c2 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java @@ -1,6 +1,5 @@ package com.kobylynskyi.graphql.codegen.model; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/model/definitions/ExtendedDocument.java b/src/main/java/com/kobylynskyi/graphql/codegen/model/definitions/ExtendedDocument.java index a758e8090..7a694c7dd 100644 --- a/src/main/java/com/kobylynskyi/graphql/codegen/model/definitions/ExtendedDocument.java +++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/definitions/ExtendedDocument.java @@ -1,26 +1,17 @@ package com.kobylynskyi.graphql.codegen.model.definitions; -import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateFilesCreator; -import com.kobylynskyi.graphql.codegen.generators.FreeMarkerTemplateType; -import com.kobylynskyi.graphql.codegen.mapper.FieldDefinitionToParameterMapper; import graphql.language.FieldDefinition; import graphql.language.Type; import graphql.language.TypeName; -import java.io.File; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import static java.util.stream.Collectors.toList; - /** * GraphQL document that holds all extended definitions */